Пример #1
0
void CreateToc(WindowInfo *win)
{
    win->hwndTocBox = CreateWindow(WC_STATIC, L"", WS_CHILD|WS_CLIPCHILDREN,
                                   0, 0, gGlobalPrefs->sidebarDx, 0,
                                   win->hwndFrame, (HMENU)0, GetModuleHandle(nullptr), nullptr);

    LabelWithCloseWnd *l = CreateLabelWithCloseWnd(win->hwndTocBox, IDC_TOC_LABEL_WITH_CLOSE);
    win->tocLabelWithClose = l;
    SetPaddingXY(l, 2, 2);
    SetFont(l, GetDefaultGuiFont());
    // label is set in UpdateToolbarSidebarText()

    win->hwndTocTree = CreateWindowEx(WS_EX_STATICEDGE, WC_TREEVIEW, L"TOC",
                                      TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT|TVS_SHOWSELALWAYS|
                                      TVS_TRACKSELECT|TVS_DISABLEDRAGDROP|TVS_NOHSCROLL|TVS_INFOTIP|
                                      WS_TABSTOP|WS_VISIBLE|WS_CHILD,
                                      0, 0, 0, 0, win->hwndTocBox, (HMENU)IDC_TOC_TREE, GetModuleHandle(nullptr), nullptr);

    TreeView_SetUnicodeFormat(win->hwndTocTree, true);

    if (nullptr == DefWndProcTocTree)
        DefWndProcTocTree = (WNDPROC)GetWindowLongPtr(win->hwndTocTree, GWLP_WNDPROC);
    SetWindowLongPtr(win->hwndTocTree, GWLP_WNDPROC, (LONG_PTR)WndProcTocTree);

    if (nullptr == DefWndProcTocBox)
        DefWndProcTocBox = (WNDPROC)GetWindowLongPtr(win->hwndTocBox, GWLP_WNDPROC);
    SetWindowLongPtr(win->hwndTocBox, GWLP_WNDPROC, (LONG_PTR)WndProcTocBox);
}
Пример #2
0
void CreateFavorites(WindowInfo* win) {
    HMODULE h = GetModuleHandleW(nullptr);
    int dx = gGlobalPrefs->sidebarDx;
    DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN;
    win->hwndFavBox = CreateWindowW(WC_STATIC, L"", dwStyle, 0, 0, dx, 0, win->hwndFrame, (HMENU)0, h, nullptr);

    LabelWithCloseWnd* l = CreateLabelWithCloseWnd(win->hwndFavBox, IDC_FAV_LABEL_WITH_CLOSE);
    win->favLabelWithClose = l;
    SetPaddingXY(l, 2, 2);
    SetFont(l, GetDefaultGuiFont());
    // label is set in UpdateToolbarSidebarText()

    dwStyle = TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | TVS_TRACKSELECT |
              TVS_DISABLEDRAGDROP | TVS_NOHSCROLL | TVS_INFOTIP | WS_TABSTOP | WS_VISIBLE | WS_CHILD;
    win->hwndFavTree = CreateWindowExW(WS_EX_STATICEDGE, WC_TREEVIEW, L"Fav", dwStyle, 0, 0, 0, 0, win->hwndFavBox,
                                       (HMENU)IDC_FAV_TREE, h, nullptr);

    TreeView_SetUnicodeFormat(win->hwndFavTree, true);

    if (nullptr == DefWndProcFavTree) {
        DefWndProcFavTree = (WNDPROC)GetWindowLongPtr(win->hwndFavTree, GWLP_WNDPROC);
    }
    SetWindowLongPtr(win->hwndFavTree, GWLP_WNDPROC, (LONG_PTR)WndProcFavTree);

    if (nullptr == DefWndProcFavBox) {
        DefWndProcFavBox = (WNDPROC)GetWindowLongPtr(win->hwndFavBox, GWLP_WNDPROC);
    }
    SetWindowLongPtr(win->hwndFavBox, GWLP_WNDPROC, (LONG_PTR)WndProcFavBox);
}
Пример #3
0
bool CreateEditCtrl(EditCtrl *w) {
    // Note: has to remember this here because when I GetWindowStyle() later on,
    // WS_BORDER is not set, which is a mystery, because it is being drawn.
    // also, WS_BORDER seems to be painted in client areay
    w->hasBorder = bit::IsMaskSet<DWORD>(w->dwStyle, WS_BORDER);

    RECT rc = w->initialPos;
    w->hwnd = CreateWindowExW(w->dwExStyle, WC_EDIT, L"", w->dwStyle, rc.left, rc.top, RectDx(rc),
                              RectDy(rc), w->parent, nullptr, GetModuleHandleW(nullptr), nullptr);

    if (!w->hwnd) {
        return false;
    }
    SetFont(w, GetDefaultGuiFont());
    SetWindowSubclass(w->hwnd, EditProc, 0, (DWORD_PTR)w);
    SetWindowSubclass(GetParent(w->hwnd), EditParentProc, 0, (DWORD_PTR)w);
    return true;
}
Пример #4
0
bool TreeCtrl::Create(const WCHAR* title) {
    if (!title) {
        title = L"";
    }

    RECT rc = this->initialPos;
    HMODULE hmod = GetModuleHandleW(nullptr);
    this->hwnd = CreateWindowExW(this->dwExStyle, WC_TREEVIEWW, title, this->dwStyle, rc.left, rc.top, RectDx(rc),
                                 RectDy(rc), this->parent, this->menu, hmod, nullptr);
    if (!this->hwnd) {
        return false;
    }
    TreeView_SetUnicodeFormat(this->hwnd, true);
    this->SetFont(GetDefaultGuiFont());
    Subclass(this);

    return true;
}
Пример #5
0
void CreateTabbar(WindowInfo* win) {
    DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS /*| WS_VISIBLE*/ | TCS_FOCUSNEVER | TCS_FIXEDWIDTH | TCS_FORCELABELLEFT;
    DWORD dwStyleEx = 0;
    auto h = GetModuleHandleW(nullptr);
    HWND hwndTabBar = CreateWindowExW(dwStyleEx, WC_TABCONTROL, L"", dwStyle, 0, 0, 0, 0, win->hwndFrame,
                                      (HMENU)IDC_TABBAR, h, nullptr);

    SetWindowSubclass(hwndTabBar, TabBarProc, 0, (DWORD_PTR)win);
    SetWindowSubclass(GetParent(hwndTabBar), TabBarParentProc, 0, (DWORD_PTR)win);

    SizeI tabSize = GetTabSize(win->hwndFrame);
    TabPainter* tp = new TabPainter(hwndTabBar, tabSize);
    SetWindowLongPtr(hwndTabBar, GWLP_USERDATA, (LONG_PTR)tp);

    SetWindowFont(hwndTabBar, GetDefaultGuiFont(), FALSE);
    TabCtrl_SetItemSize(hwndTabBar, tabSize.dx, tabSize.dy);

    win->hwndTabBar = hwndTabBar;

    win->tabSelectionHistory = new Vec<TabInfo*>();
}
Пример #6
0
bool CreateFrameRateWnd(FrameRateWnd *w)
{
    // WS_POPUP removes all decorations
    DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_DISABLED;
    RECT r = GetClientRect(w->hwndAssociatedWith);
    // since this is WS_POPUP window, providing w->hwndAssocatedWith doesn't establish
    // parent-child relationship but ownership relationship (as long as hwndAssociatedWith
    // is WS_OVERLAPEPED or WS_POPUP). Owned window always shows up on top of owner in z-order
    // http://msdn.microsoft.com/en-us/library/ms632599%28v=VS.85%29.aspx#owned_windows
    HWND hwnd = CreateWindowEx(WS_EX_LAYERED, FRAME_RATE_CLASS_NAME, NULL, dwStyle,
             0, 0, 0, 0, w->hwndAssociatedWith, NULL, GetModuleHandle(NULL), w);
    CrashIf(hwnd != w->hwnd);
    if (!hwnd) {
        return false;
    }
    w->font = GetDefaultGuiFont();
    SetWindowSubclass(w->hwndAssociatedWith, WndProcFrameRateAssociated, 0, (DWORD_PTR) w);

    SetLayeredWindowAttributes(hwnd, 0, 0x7f, LWA_ALPHA);
    ShowFrameRate(w, 0);
    return true;
}
Пример #7
0
void CreateTabbar(WindowInfo *win)
{
    HWND hwndTabBar = CreateWindow(WC_TABCONTROL, L"",
        WS_CHILD | WS_CLIPSIBLINGS /*| WS_VISIBLE*/ |
        TCS_FOCUSNEVER | TCS_FIXEDWIDTH | TCS_FORCELABELLEFT,
        0, 0, 0, 0,
        win->hwndFrame, (HMENU)IDC_TABBAR, GetModuleHandle(nullptr), nullptr);

    if (!DefWndProcTabBar)
        DefWndProcTabBar = (WNDPROC)GetWindowLongPtr(hwndTabBar, GWLP_WNDPROC);
    SetWindowLongPtr(hwndTabBar, GWLP_WNDPROC, (LONG_PTR)WndProcTabBar);

    SizeI tabSize = GetTabSize(win->hwndFrame);
    TabPainter *tp = new TabPainter(hwndTabBar, tabSize);
    SetWindowLongPtr(hwndTabBar, GWLP_USERDATA, (LONG_PTR)tp);

    SetWindowFont(hwndTabBar, GetDefaultGuiFont(), FALSE);
    TabCtrl_SetItemSize(hwndTabBar, tabSize.dx, tabSize.dy);

    win->hwndTabBar = hwndTabBar;

    win->tabSelectionHistory = new Vec<TabInfo *>();
}
Пример #8
0
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    int retCode = 1;    // by default it's error

#ifdef DEBUG
    // Memory leak detection (only enable _CRTDBG_LEAK_CHECK_DF for
    // regular termination so that leaks aren't checked on exceptions,
    // aborts, etc. where some clean-up might not take place)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF);
    //_CrtSetBreakAlloc(421);
    TryLoadMemTrace();
#endif

    DisableDataExecution();
    // ensure that C functions behave consistently under all OS locales
    // (use Win32 functions where localized input or output is desired)
    setlocale(LC_ALL, "C");
    // don't show system-provided dialog boxes when accessing files on drives
    // that are not mounted (e.g. a: drive without floppy or cd rom drive
    // without a cd).
    SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);

#if defined(DEBUG) || defined(SVN_PRE_RELEASE_VER)
    if (str::StartsWith(lpCmdLine, "/tester")) {
        extern int TesterMain(); // in Tester.cpp
        return TesterMain();
    }

    if (str::StartsWith(lpCmdLine, "/regress")) {
        extern int RegressMain(); // in Regress.cpp
        return RegressMain();
    }
#endif
#ifdef SUPPORTS_AUTO_UPDATE
    if (str::StartsWith(lpCmdLine, "-autoupdate")) {
        bool quit = AutoUpdateMain();
        if (quit)
            return 0;
    }
#endif

    srand((unsigned int)time(NULL));

    // load uiautomationcore.dll before installing crash handler (i.e. initializing
    // dbghelp.dll), so that we get function names/offsets in GetCallstack()
    uia::Initialize();
#ifdef DEBUG
    dbghelp::RememberCallstackLogs();
#endif

    SetupCrashHandler();

    ScopedOle ole;
    InitAllCommonControls();
    ScopedGdiPlus gdiPlus(true);
    mui::Initialize();
    uitask::Initialize();

    prefs::Load();

    CommandLineInfo i(GetCommandLine());

    SetCurrentLang(i.lang ? i.lang : gGlobalPrefs->uiLanguage);

    // This allows ad-hoc comparison of gdi, gdi+ and gdi+ quick when used
    // in layout
#if 0
    RedirectIOToConsole();
    BenchEbookLayout(L"C:\\kjk\\downloads\\pg12.mobi");
    system("pause");
    goto Exit;
#endif

    if (i.showConsole) {
        RedirectIOToConsole();
        RedirectDllIOToConsole();
    }
    if (i.makeDefault)
        AssociateExeWithPdfExtension();
    if (i.pathsToBenchmark.Count() > 0) {
        BenchFileOrDir(i.pathsToBenchmark);
        if (i.showConsole)
            system("pause");
    }
    if (i.exitImmediately)
        goto Exit;
    gCrashOnOpen = i.crashOnOpen;

    gPolicyRestrictions = GetPolicies(i.restrictedUse);
    GetFixedPageUiColors(gRenderCache.textColor, gRenderCache.backgroundColor);
    DebugGdiPlusDevice(gUseGdiRenderer);

    if (!RegisterWinClass())
        goto Exit;

    CrashIf(hInstance != GetModuleHandle(NULL));
    if (!InstanceInit(nCmdShow))
        goto Exit;

    if (i.hwndPluginParent) {
        if (!SetupPluginMode(i))
            goto Exit;
    }

    if (i.printerName) {
        // note: this prints all PDF files. Another option would be to
        // print only the first one
        for (size_t n = 0; n < i.fileNames.Count(); n++) {
            bool ok = PrintFile(i.fileNames.At(n), i.printerName, !i.silent, i.printSettings);
            if (!ok)
                retCode++;
        }
        --retCode; // was 1 if no print failures, turn 1 into 0
        goto Exit;
    }

    bool showStartPage = i.fileNames.Count() == 0 && gGlobalPrefs->rememberOpenedFiles && gGlobalPrefs->showStartPage;
    if (showStartPage) {
        // make the shell prepare the image list, so that it's ready when the first window's loaded
        SHFILEINFO sfi;
        SHGetFileInfo(L".pdf", 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES);
    }

    if (gGlobalPrefs->reopenOnce) {
        WStrVec moreFileNames;
        ParseCmdLine(gGlobalPrefs->reopenOnce, moreFileNames);
        moreFileNames.Reverse();
        for (WCHAR **fileName = moreFileNames.IterStart(); fileName; fileName = moreFileNames.IterNext()) {
            i.fileNames.Append(*fileName);
        }
        moreFileNames.RemoveAt(0, moreFileNames.Count());
        str::ReplacePtr(&gGlobalPrefs->reopenOnce, NULL);
    }

    HANDLE hMutex = NULL;
    HWND hPrevWnd = NULL;
    if (i.printDialog || i.stressTestPath || gPluginMode) {
        // TODO: pass print request through to previous instance?
    }
    else if (i.reuseDdeInstance) {
        hPrevWnd = FindWindow(FRAME_CLASS_NAME, NULL);
    }
    else if (gGlobalPrefs->reuseInstance || gGlobalPrefs->useTabs) {
        hPrevWnd = FindPrevInstWindow(&hMutex);
    }
    if (hPrevWnd) {
        for (size_t n = 0; n < i.fileNames.Count(); n++) {
            OpenUsingDde(hPrevWnd, i.fileNames.At(n), i, 0 == n);
        }
        goto Exit;
    }

    WindowInfo *win = NULL;
    for (size_t n = 0; n < i.fileNames.Count(); n++) {
        win = LoadOnStartup(i.fileNames.At(n), i, !win);
        if (!win) {
            retCode++;
            continue;
        }
        if (i.printDialog)
            OnMenuPrint(win, i.exitWhenDone);
    }
    if (i.fileNames.Count() > 0 && !win) {
        // failed to create any window, even though there
        // were files to load (or show a failure message for)
        goto Exit;
    }
    if (i.printDialog && i.exitWhenDone)
        goto Exit;

    if (!win) {
        win = CreateAndShowWindowInfo();
        if (!win)
            goto Exit;
    }

    UpdateUITextForLanguage(); // needed for RTL languages
    if (win->IsAboutWindow()) {
        // TODO: shouldn't CreateAndShowWindowInfo take care of this?
        UpdateToolbarAndScrollbarState(*win);
    }

    // Make sure that we're still registered as default,
    // if the user has explicitly told us to be
    if (gGlobalPrefs->associatedExtensions)
        RegisterForPdfExtentions(win->hwndFrame);

    if (i.stressTestPath) {
        // don't save file history and preference changes
        gPolicyRestrictions = (gPolicyRestrictions | Perm_RestrictedUse) & ~Perm_SavePreferences;
        RebuildMenuBarForWindow(win);
        StartStressTest(&i, win, &gRenderCache);
    }

    if (gGlobalPrefs->checkForUpdates)
        UpdateCheckAsync(win, true);

    // only hide newly missing files when showing the start page on startup
    if (showStartPage && gFileHistory.Get(0)) {
        gFileExistenceChecker = new FileExistenceChecker();
        gFileExistenceChecker->Start();
    }
    // call this once it's clear whether Perm_SavePreferences has been granted
    prefs::RegisterForFileChanges();

    retCode = RunMessageLoop();

    SafeCloseHandle(&hMutex);
    CleanUpThumbnailCache(gFileHistory);

Exit:
    prefs::UnregisterForFileChanges();

    while (gWindows.Count() > 0) {
        DeleteWindowInfo(gWindows.At(0));
    }

#ifndef DEBUG

    // leave all the remaining clean-up to the OS
    // (as recommended for a quick exit)
    ExitProcess(retCode);

#else

    DeleteObject(GetDefaultGuiFont());
    DeleteBitmap(gBitmapReloadingCue);
    DeleteSplitterBrush();

    // wait for FileExistenceChecker to terminate
    // (which should be necessary only very rarely)
    while (gFileExistenceChecker) {
        Sleep(10);
        uitask::DrainQueue();
    }

    mui::Destroy();
    uitask::Destroy();
    trans::Destroy();

    SaveCallstackLogs();
    dbghelp::FreeCallstackLogs();

    // must be after uitask::Destroy() because we might have queued prefs::Reload()
    // which crashes if gGlobalPrefs is freed
    gFileHistory.UpdateStatesSource(NULL);
    DeleteGlobalPrefs(gGlobalPrefs);

    // it's still possible to crash after this (destructors of static classes,
    // atexit() code etc.) point, but it's very unlikely
    UninstallCrashHandler();

    // output leaks after all destructors of static objects have run
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    return retCode;
#endif
}
Пример #9
0
    // Paints the tabs that intersect the window's update rectangle.
    void Paint(HDC hdc, RECT &rc) {
        IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);

        // paint the background
        bool isTranslucentMode = inTitlebar && dwm::IsCompositionEnabled();
        if (isTranslucentMode)
            PaintParentBackground(hwnd, hdc);
        else {
            HBRUSH brush = CreateSolidBrush(color.bar);
            FillRect(hdc, &rc, brush);
            DeleteObject(brush);
        }

        // TODO: GDI+ doesn't seem to cope well with SetWorldTransform
        XFORM ctm = { 1.0, 0, 0, 1.0, 0, 0 };
        SetWorldTransform(hdc, &ctm);

        Graphics graphics(hdc);
        graphics.SetCompositingMode(CompositingModeSourceCopy);
        graphics.SetCompositingQuality(CompositingQualityHighQuality);
        graphics.SetSmoothingMode(SmoothingModeHighQuality);
        graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
        graphics.SetPageUnit(UnitPixel);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);

        SolidBrush br(Color(0, 0, 0));
        Pen pen(&br, 2.0f);

        Font f(hdc, GetDefaultGuiFont());
        // TODO: adjust these constant values for DPI?
        RectF layout((REAL)DpiScaleX(hwnd,3), 1.0f, REAL(width - DpiScaleX(hwnd,20)), (REAL)height);
        StringFormat sf(StringFormat::GenericDefault());
        sf.SetFormatFlags(StringFormatFlagsNoWrap);
        sf.SetLineAlignment(StringAlignmentCenter);
        sf.SetTrimming(StringTrimmingEllipsisCharacter);

        REAL yPosTab = inTitlebar ? 0.0f : REAL(ClientRect(hwnd).dy - height - 1);
        for (int i = 0; i < Count(); i++) {
            graphics.ResetTransform();
            graphics.TranslateTransform(1.f + (REAL)(width + 1) * i - (REAL)rc.left, yPosTab - (REAL)rc.top);

            if (!graphics.IsVisible(0, 0, width + 1, height + 1))
                continue;

            // in firefox style we only paint current and highlighed tabs
            // all other tabs only show
            bool onlyText = g_FirefoxStyle && !((current == i) || (highlighted == i));
            if (onlyText) {
#if 0
                // we need to first paint the background with the same color as caption,
                // otherwise the text looks funny (because is transparent?)
                // TODO: what is the damn bg color of caption? bar is too light, outline is too dark
                Color bgColTmp;
                bgColTmp.SetFromCOLORREF(color.bar);
                {
                    SolidBrush bgBr(bgColTmp);
                    graphics.FillRectangle(&bgBr, layout);
                }
                bgColTmp.SetFromCOLORREF(color.outline);
                {
                    SolidBrush bgBr(bgColTmp);
                    graphics.FillRectangle(&bgBr, layout);
                }
#endif
                // TODO: this is a hack. If I use no background and cleartype, the
                // text looks funny (is bold).
                // CompositingModeSourceCopy doesn't work with clear type
                // another option is to draw background before drawing text, but
                // I can't figure out what is the actual color of caption
                graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
                graphics.SetCompositingMode(CompositingModeSourceCopy);
                //graphics.SetCompositingMode(CompositingModeSourceOver);
                br.SetColor(ToColor(color.text));
                graphics.DrawString(text.At(i), -1, &f, layout, &sf, &br);
                graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
                continue;
            }

            COLORREF bgCol = color.background;;
            if (current == i) {
                bgCol = color.current;
            } else if (highlighted == i) {
                bgCol = color.highlight;
            }

            // ensure contrast between text and background color
            // TODO: adjust threshold (and try adjusting both current/background tabs)
            COLORREF textCol = color.text;
            float bgLight = GetLightness(bgCol), textLight = GetLightness(textCol);
            if (textLight < bgLight ? bgLight < 0x70 : bgLight > 0x90)
                textCol = textLight ? AdjustLightness(textCol, 255.0f / textLight - 1.0f) : RGB(255, 255, 255);
            if (fabs(textLight - bgLight) < 0x40)
                textCol = bgLight < 0x80 ? RGB(255, 255, 255) : RGB(0, 0, 0);

            // paint tab's body
            graphics.SetCompositingMode(CompositingModeSourceCopy);
            iterator.NextMarker(&shape);
            br.SetColor(ToColor(bgCol));
            graphics.FillPath(&br, &shape);

            // draw tab's text
            graphics.SetCompositingMode(CompositingModeSourceOver);
            br.SetColor(ToColor(textCol));
            graphics.DrawString(text.At(i), -1, &f, layout, &sf, &br);

            // paint "x"'s circle
            iterator.NextMarker(&shape);
            if (xClicked == i || xHighlighted == i) {
                br.SetColor(ToColor(i == xClicked ? color.x_click : color.x_highlight));
                graphics.FillPath(&br, &shape);
            }

            // paint "x"
            iterator.NextMarker(&shape);
            if (xClicked == i || xHighlighted == i)
                pen.SetColor(ToColor(color.x_line));
            else
                pen.SetColor(ToColor(color.outline));
            graphics.DrawPath(&pen, &shape);
            iterator.Rewind();
        }
    }
Пример #10
0
    // Paints the tabs that intersect the window's update rectangle.
    void Paint(HDC hdc, RECT& rc) {
        IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);

// paint the background
#if 0
        bool isTranslucentMode = inTitlebar && dwm::IsCompositionEnabled();
        if (isTranslucentMode) {
            PaintParentBackground(hwnd, hdc);
        } else {
            // note: not sure what color should be used here and painting
            // background works fine
            /*HBRUSH brush = CreateSolidBrush(colors.bar);
            FillRect(hdc, &rc, brush);
            DeleteObject(brush);*/
        }
#else
        PaintParentBackground(hwnd, hdc);
#endif
        // TODO: GDI+ doesn't seem to cope well with SetWorldTransform
        XFORM ctm = {1.0, 0, 0, 1.0, 0, 0};
        SetWorldTransform(hdc, &ctm);

        Graphics gfx(hdc);
        gfx.SetCompositingMode(CompositingModeSourceCopy);
        gfx.SetCompositingQuality(CompositingQualityHighQuality);
        gfx.SetSmoothingMode(SmoothingModeHighQuality);
        gfx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
        gfx.SetPageUnit(UnitPixel);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);

        SolidBrush br(Color(0, 0, 0));
        Pen pen(&br, 2.0f);

        Font f(hdc, GetDefaultGuiFont());
        // TODO: adjust these constant values for DPI?
        RectF layout((REAL)DpiScaleX(hwnd, 3), 1.0f, REAL(width - DpiScaleX(hwnd, 20)), (REAL)height);
        StringFormat sf(StringFormat::GenericDefault());
        sf.SetFormatFlags(StringFormatFlagsNoWrap);
        sf.SetLineAlignment(StringAlignmentCenter);
        sf.SetTrimming(StringTrimmingEllipsisCharacter);

        REAL yPosTab = inTitlebar ? 0.0f : REAL(ClientRect(hwnd).dy - height - 1);
        for (int i = 0; i < Count(); i++) {
            gfx.ResetTransform();
            gfx.TranslateTransform(1.f + (REAL)(width + 1) * i - (REAL)rc.left, yPosTab - (REAL)rc.top);

            if (!gfx.IsVisible(0, 0, width + 1, height + 1))
                continue;

            // Get the correct colors based on the state and the current theme
            COLORREF bgCol = GetAppColor(AppColor::TabBackgroundBg);
            COLORREF textCol = GetAppColor(AppColor::TabBackgroundText);
            COLORREF xColor = GetAppColor(AppColor::TabBackgroundCloseX);
            COLORREF circleColor = GetAppColor(AppColor::TabBackgroundCloseCircle);

            if (selectedTabIdx == i) {
                bgCol = GetAppColor(AppColor::TabSelectedBg);
                textCol = GetAppColor(AppColor::TabSelectedText);
                xColor = GetAppColor(AppColor::TabSelectedCloseX);
                circleColor = GetAppColor(AppColor::TabSelectedCloseCircle);
            } else if (highlighted == i) {
                bgCol = GetAppColor(AppColor::TabHighlightedBg);
                textCol = GetAppColor(AppColor::TabHighlightedText);
                xColor = GetAppColor(AppColor::TabHighlightedCloseX);
                circleColor = GetAppColor(AppColor::TabHighlightedCloseCircle);
            }
            if (xHighlighted == i) {
                xColor = GetAppColor(AppColor::TabHoveredCloseX);
                circleColor = GetAppColor(AppColor::TabHoveredCloseCircle);
            }
            if (xClicked == i) {
                xColor = GetAppColor(AppColor::TabClickedCloseX);
                circleColor = GetAppColor(AppColor::TabClickedCloseCircle);
            }

            // paint tab's body
            gfx.SetCompositingMode(CompositingModeSourceCopy);
            iterator.NextMarker(&shape);
            br.SetColor(ToColor(bgCol));
            Point points[4];
            shape.GetPathPoints(points, 4);
            Rect body(points[0].X, points[0].Y, points[2].X - points[0].X, points[2].Y - points[0].Y);
            body.Inflate(0, 0);
            gfx.SetClip(body);
            body.Inflate(5, 5);
            gfx.FillRectangle(&br, body);
            gfx.ResetClip();

            // draw tab's text
            gfx.SetCompositingMode(CompositingModeSourceOver);
            br.SetColor(ToColor(textCol));
            gfx.DrawString(text.at(i), -1, &f, layout, &sf, &br);

            // paint "x"'s circle
            iterator.NextMarker(&shape);
            bool closeCircleEnabled = true;
            if ((xClicked == i || xHighlighted == i) && closeCircleEnabled) {
                br.SetColor(ToColor(circleColor));
                gfx.FillPath(&br, &shape);
            }

            // paint "x"
            iterator.NextMarker(&shape);
            pen.SetColor(ToColor(xColor));
            gfx.DrawPath(&pen, &shape);
            iterator.Rewind();
        }
    }