void RegisterQAppAssociation::registerAllAssociation()
{
    if (isVistaOrNewer() && !registerAppCapabilities()) {
        return;
    }

    QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin();
    while (i != _fileAssocHash.constEnd()) {
        registerAssociation(i.key(), FileAssociation);
        ++i;
    }

    i = _urlAssocHash.constBegin();
    while (i != _urlAssocHash.constEnd()) {
        registerAssociation(i.key(), UrlAssociation);
        ++i;
    }

    if (!isVistaOrNewer()) {
#ifndef __MINGW32__
        // On Windows Vista or newer for updating icons 'pAAR->SetAppAsDefault()'
        // calls 'SHChangeNotify()'. Thus, we just need care about older Windows.
        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0 , 0);
#endif
    }
}
static void getNonClientMetrics(NONCLIENTMETRICS* metrics)
{
    static UINT size = isVistaOrNewer() ?
        sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
    metrics->cbSize = size;
    bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0);
    ASSERT(success);
}
bool RegisterQAppAssociation::showNativeDefaultAppSettingsUi()
{
    if (!isVistaOrNewer()) {
        return false;
    }

#ifdef _WIN32_WINNT_WIN8
    IApplicationActivationManager* pActivator;
    HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
                                  nullptr,
                                  CLSCTX_INPROC,
                                  IID_IApplicationActivationManager,
                                  (void**)&pActivator);

    if (!SUCCEEDED(hr)) {
        return false;
    }

    DWORD pid;
    hr = pActivator->ActivateApplication(
        L"windows.immersivecontrolpanel_cw5n1h2txyewy" // appUserModelId of "Settings"
        L"!microsoft.windows.immersivecontrolpanel",   //  in Windows Store
        L"page=SettingsPageAppsDefaults", AO_NONE, &pid);

    if (!SUCCEEDED(hr)) {
        return false;
    }

    // Do not check error because we could at least open
    // the "Default apps" setting.
    pActivator->ActivateApplication(
        L"windows.immersivecontrolpanel_cw5n1h2txyewy"
        L"!microsoft.windows.immersivecontrolpanel",
        L"page=SettingsPageAppsDefaults"
        L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid);

    pActivator->Release();
#else // Vista or Win7
    IApplicationAssociationRegistrationUI* pAARUI = NULL;

    HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
                                  NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI),
                                  reinterpret_cast< void** > (&pAARUI));

    if (!SUCCEEDED(hr)) {
        return false;
    }

    hr = pAARUI->LaunchAdvancedAssociationUI(reinterpret_cast<LPCWSTR>(_appRegisteredName.utf16()));
    pAARUI->Release();
#endif // _WIN32_WINNT_WIN8

    return true;
}
bool RegisterQAppAssociation::registerAppCapabilities()
{
    if (!isVistaOrNewer()) {
        return true;
    }
    // Vista and newer
    QSettings regLocalMachine("HKEY_LOCAL_MACHINE", QSettings::NativeFormat);
    QString capabilitiesKey = regLocalMachine.value("Software/RegisteredApplications/" + _appRegisteredName).toString();

    if (capabilitiesKey.isEmpty()) {
        regLocalMachine.setValue("Software/RegisteredApplications/" + _appRegisteredName,
                                 QString("Software\\" + _appRegisteredName + "\\Capabilities"));
        capabilitiesKey = regLocalMachine.value("Software/RegisteredApplications/" + _appRegisteredName).toString();

        if (capabilitiesKey.isEmpty()) {
            QMessageBox::warning(0, tr("Warning!"),
                                 tr("There are some problems. Please, reinstall QupZilla.\n"
                                    "Maybe relaunch with administrator right do a magic for you! ;)"));
            return false;
        }
    }

    capabilitiesKey.replace("\\", "/");

    QHash<QString, QPair<QString, QString> >::const_iterator it = _assocDescHash.constBegin();
    while (it != _assocDescHash.constEnd()) {
        createProgId(it.key());
        ++it;
    }

    regLocalMachine.setValue(capabilitiesKey + "/ApplicationDescription", _appDesc);
    regLocalMachine.setValue(capabilitiesKey + "/ApplicationIcon", _appIcon);
    regLocalMachine.setValue(capabilitiesKey + "/ApplicationName", _appRegisteredName);

    QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin();
    while (i != _fileAssocHash.constEnd()) {
        regLocalMachine.setValue(capabilitiesKey + "/FileAssociations/" + i.key(), i.value());
        ++i;
    }

    i = _urlAssocHash.constBegin();
    while (i != _urlAssocHash.constEnd()) {
        regLocalMachine.setValue(capabilitiesKey + "/URLAssociations/" + i.key(), i.value());
        ++i;
    }
    regLocalMachine.setValue(capabilitiesKey + "/Startmenu/StartMenuInternet", _appPath);

    return true;
}
bool RegisterQAppAssociation::isDefaultApp(const QString &assocName, AssociationType type)
{
    if (isVistaOrNewer()) {
        QSettings regCurrentUserRoot("HKEY_CURRENT_USER", QSettings::NativeFormat);
        switch (type) {
        case FileAssociation: {
            regCurrentUserRoot.beginGroup("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts");
            if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
                return (_fileAssocHash.value(assocName)
                        == regCurrentUserRoot.value(assocName + "/UserChoice/Progid"));
            }
            else {
                regCurrentUserRoot.endGroup();
                return false;
            }
            break;
        }
        case UrlAssociation: {
            regCurrentUserRoot.beginGroup("Software/Microsoft/Windows/Shell/Associations/UrlAssociations");
            if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
                return (_urlAssocHash.value(assocName)
                        == regCurrentUserRoot.value(assocName + "/UserChoice/Progid"));
            }
            else {
                regCurrentUserRoot.endGroup();
                return false;
            }
        }
        break;

        default:
            break;
        }
    }
    else {
        QSettings regClassesRoot("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
        {
            if (!regClassesRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
                return false;
            }
        }
        switch (type) {
        case FileAssociation: {
            return (_fileAssocHash.value(assocName)
                    == regClassesRoot.value(assocName + "/Default"));
        }
        break;
        case UrlAssociation: {
            QString currentDefault = regClassesRoot.value(assocName + "/shell/open/command/Default").toString();
            currentDefault.remove("\"");
            currentDefault.remove("%1");
            currentDefault = currentDefault.trimmed();
            return (_appPath == currentDefault);
        }
        break;

        default:
            break;
        }
    }

    return false;
}
void RegisterQAppAssociation::registerAssociation(const QString &assocName, AssociationType type)
{
    if (isVistaOrNewer()) { // Vista and newer
        IApplicationAssociationRegistration* pAAR;

        HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
                                      NULL,
                                      CLSCTX_INPROC,
                                      __uuidof(IApplicationAssociationRegistration),
                                      (void**)&pAAR);
        if (SUCCEEDED(hr)) {
            switch (type) {
            case FileAssociation:
                hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(),
                                           assocName.toStdWString().c_str(),
                                           AT_FILEEXTENSION);
                break;
            case UrlAssociation: {
                QSettings regCurrentUserRoot("HKEY_CURRENT_USER", QSettings::NativeFormat);
                QString currentUrlDefault =
                    regCurrentUserRoot.value("Software/Microsoft/Windows/Shell/Associations/UrlAssociations/"
                                             + assocName + "/UserChoice/Progid").toString();
                hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(),
                                           assocName.toStdWString().c_str(),
                                           AT_URLPROTOCOL);
                if (SUCCEEDED(hr)
                        && !currentUrlDefault.isEmpty()
                        && currentUrlDefault != _urlAssocHash.value(assocName)) {
                    regCurrentUserRoot.setValue("Software/Classes"
                                                + assocName
                                                + "/shell/open/command/backup_progid", currentUrlDefault);
                }
            }
            break;

            default:
                break;
            }

            pAAR->Release();
        }
    }
    else { // Older than Vista
        QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat);
        regUserRoot.beginGroup("Software/Classes");
        QSettings regClassesRoot("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
        switch (type) {
        case FileAssociation: {
            QString progId = _fileAssocHash.value(assocName);
            createProgId(progId);
            QString currentDefault = regClassesRoot.value(assocName + "/Default").toString();
            if (!currentDefault.isEmpty()
                    && currentDefault != progId
                    && regUserRoot.value(assocName + "/backup_val").toString() != progId) {
                regUserRoot.setValue(assocName + "/backup_val", currentDefault);
            }
            regUserRoot.setValue(assocName + "/.", progId);
        }
        break;
        case UrlAssociation: {
            QString progId = _urlAssocHash.value(assocName);
            createProgId(progId);
            QString currentDefault = regClassesRoot.value(assocName + "/shell/open/command/Default").toString();
            QString command = "\"" + _appPath + "\" \"%1\"";
            if (!currentDefault.isEmpty()
                    && currentDefault != command
                    && regUserRoot.value(assocName + "/shell/open/command/backup_val").toString() != command) {
                regUserRoot.setValue(assocName + "/shell/open/command/backup_val", currentDefault);
            }

            regUserRoot.setValue(assocName + "/shell/open/command/.", command);
            regUserRoot.setValue(assocName + "/URL Protocol", "");
            break;
        }
        default:
            break;
        }
        regUserRoot.endGroup();
    }
}
// Fills |length| glyphs starting at |offset| in a |page| in the Basic 
// Multilingual Plane (<= U+FFFF). The input buffer size should be the
// same as |length|. We can use the standard Windows GDI functions here. 
// Returns true if any glyphs were found.
static bool fillBMPGlyphs(unsigned offset,
                          unsigned length,
                          UChar* buffer,
                          GlyphPage* page,
                          const SimpleFontData* fontData,
                          bool recurse)
{
    HDC dc = GetDC((HWND)0);
    HGDIOBJ oldFont = SelectObject(dc, fontData->platformData().hfont());

    TEXTMETRIC tm = {0};
    if (!GetTextMetrics(dc, &tm)) {
        SelectObject(dc, oldFont);
        ReleaseDC(0, dc);

        if (recurse) {
            if (PlatformBridge::ensureFontLoaded(fontData->platformData().hfont()))
                return fillBMPGlyphs(offset, length, buffer, page, fontData, false);

            fillEmptyGlyphs(page);
            return false;
        } else {
            // FIXME: Handle gracefully the error if this call also fails.
            // See http://crbug.com/6401
            LOG_ERROR("Unable to get the text metrics after second attempt");
            fillEmptyGlyphs(page);
            return false;
        }
    }

    // FIXME: GetGlyphIndices() sets each item of localGlyphBuffer[]
    // with the one of the values listed below.
    //  * With the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x1F (TrueType Collection?) or
    //                            0xFFFF (OpenType?).
    //  * Without the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x80.
    //      (Windows automatically assigns the glyph for a box character to
    //      prevent ExtTextOut() from returning errors.)
    // To avoid from hurting the rendering performance, this code just
    // tells WebKit whether or not the all glyph indices for the given
    // characters are 0x80 (i.e. a possibly-invalid glyph) and let it
    // use alternative fonts for the characters.
    // Although this may cause a problem, it seems to work fine as far as I
    // have tested. (Obviously, I need more tests.)
    WORD localGlyphBuffer[GlyphPage::size];

    // FIXME: I find some Chinese characters can not be correctly displayed
    // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS,
    // because the corresponding glyph index is set as 0x20 when current font
    // does not have glyphs available for the character. According a blog post
    // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx
    // I think we should switch to the way about calling GetGlyphIndices with
    // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the
    // description of MSDN.
    // Also according to Jungshik and Hironori's suggestion and modification
    // we treat turetype and raster Font as different way when windows version
    // is less than Vista.
    GetGlyphIndices(dc, buffer, length, localGlyphBuffer, GGI_MARK_NONEXISTING_GLYPHS);

    // Copy the output to the GlyphPage
    bool haveGlyphs = false;
    int invalidGlyph = 0xFFFF;
    const DWORD cffTableTag = 0x20464643; // 4-byte identifier for OpenType CFF table ('CFF ').
    if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE) && (GetFontData(dc, cffTableTag, 0, 0, 0) == GDI_ERROR))
        invalidGlyph = 0x1F;

    Glyph spaceGlyph = 0;  // Glyph for a space. Lazily filled.

    for (unsigned i = 0; i < length; i++) {
        UChar c = buffer[i];
        Glyph glyph = localGlyphBuffer[i];
        const SimpleFontData* glyphFontData = fontData;
        // When this character should be a space, we ignore whatever the font
        // says and use a space. Otherwise, if fonts don't map one of these
        // space or zero width glyphs, we will get a box.
        if (Font::treatAsSpace(c)) {
            // Hard code the glyph indices for characters that should be
            // treated like spaces.
            glyph = initSpaceGlyph(dc, &spaceGlyph);
        } else if (glyph == invalidGlyph) {
            // WebKit expects both the glyph index and FontData
            // pointer to be 0 if the glyph is not present
            glyph = 0;
            glyphFontData = 0;
        } else
            haveGlyphs = true;
        page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData);
    }

    SelectObject(dc, oldFont);
    ReleaseDC(0, dc);
    return haveGlyphs;
}