/*
 ============================================================================
 xoblite -> an alternative shell based on Blackbox for Windows
 Copyright © 2002-2005 Karl-Henrik Henriksson [qwilk]
 Copyright © 2001-2004 The Blackbox for Windows Development Team
 http://xoblite.net/ - #bb4win on irc.freenode.net
 ============================================================================

  Blackbox for Windows is free software, released under the
  GNU General Public License (GPL version 2 or later), with an extension
  that allows linking of proprietary modules under a controlled interface.
  What this means is that plugins etc. are allowed to be released
  under any license the author wishes. Please note, however, that the
  original Blackbox gradient math code used in Blackbox for Windows
  is available under the BSD license.

  http://www.fsf.org/licenses/gpl.html
  http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
  http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  For additional license information, please read the included license.html

 ============================================================================

  Note1: The saturation/hue code section in SystembarWndProc was inspired by
  the equivalent section in LiteStep's systray2 module (systray.cpp),
  which is copyright (C) 1997-2002 The LiteStep Development Team.

  Note2: Parts of the TrayManager icon handling code was derived from the
  geOShell "TrayService" code, which is (C) 1999-2001 Geoffrey J. Elliott
  and the geOShell Development Team.

 ============================================================================
*/

#include "Systembar.h" 

const char szSystembarName[] = "BBSystembar"; // Window class etc.

extern BImage *pBImage;
extern Desk *pDesk;
extern Systembar *pSystembar;
extern TrayManager *pTrayManager;
extern Settings *pSettings;
extern MenuMaker *pMenuMaker;
extern Workspaces *pWorkspaces;
extern Toolbar *pToolbar;
extern Slit *pSlit;

//===========================================================================

Systembar::Systembar(HINSTANCE hInstance, bool DisableTray)
{
        // At startup, xoblite checks if the -nosystray switch was used or if it
        // is running on top of Explorer (which unfortunately also locks on to a
        // stand alone systray atm), and if so disables the xoblite internal systray
        if (DisableTray) SysTrayDisabled = true;
        else SysTrayDisabled = false;

        hSystembarWnd = NULL;
        hToolTips = NULL;
        hBlackboxWnd = GetBBWnd();
        hSystembarInstance = hInstance;

        cachedBackground = CreateCompatibleDC(NULL);
        cachedBackgroundExists = false;

        cachedTaskButtonActive = CreateCompatibleDC(NULL);
        cachedTaskButtonInactive = CreateCompatibleDC(NULL);
        cachedTaskButtonsExist = false;

        DockedToSlit = false;
        SystembarPosInProgress = false;

        //====================

        // Get size and position for our window...
        GetSettings();

        // Register our window class...
        WNDCLASS wc;
        ZeroMemory(&wc,sizeof(wc));
        wc.lpfnWndProc = SystembarWndProc;                                              // our window procedure
        wc.hInstance = hSystembarInstance;
        wc.lpszClassName = szSystembarName;                                             // our window class name

        if (!RegisterClass(&wc))
        {
                MessageBox(0, "Error registering window class", szSystembarName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("Systembar: Error registering window class", NULL);
                return;
        }

        hSystembarWnd = CreateWindowEx(
                WS_EX_TOOLWINDOW,                                                                       // exstyles
                szSystembarName,                                                                        // our window class name
                NULL,                                                                                           // use description for a window title
                WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                SystembarX, SystembarY,                                                         // position
                SystembarWidth, SystembarHeight,                                        // width & height of window
                NULL,                                                                                           // parent window
                NULL,                                                                                           // no menu
                hSystembarInstance,                                                                     // hInstance of DLL
                NULL);

        if (!hSystembarWnd)
        {
                UnregisterClass(szSystembarName,hSystembarInstance); // unregister window class
                MessageBox(0, "Error creating window", szSystembarName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("Systembar: Error creating window", NULL);
                return;
        }

        //====================

        int msgs[] = { BB_RECONFIGURE, BB_TOGGLESYSTEMBAR, BB_ADDTASK, BB_REMOVETASK, BB_REDRAW, BB_ACTIVETASK, BB_ACTIVATESHELLWINDOW, 0 };
        SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hSystembarWnd, (LPARAM)msgs);

        // Make the systembar window sticky...
        MakeSticky(hSystembarWnd);
        // Set window to accept doubleclicks...
        SetClassLongPtr(hSystembarWnd, GCL_STYLE, CS_DBLCLKS | GetClassLongPtr(hSystembarWnd, GCL_STYLE));
        // Set window to accept drag'n'drop...
        DragAcceptFiles(hSystembarWnd, true);

        // Dock to slit depending on placement setting...
        if (!stricmp(pSettings->systembarPlacement, "DockedToSlit"))
        {
                ShowWindow(hSystembarWnd, SW_SHOWNOACTIVATE);
                SendMessage(pSlit->hSlitWnd, SLIT_ADD, NULL, (LPARAM)hSystembarWnd);
                DockedToSlit = true;
        }
        else
        {
                // If the systembar is docked to the toolbar it inherits the
                // toolbar alpha transparency value and AlwaysOnTop setting...
                if (!stricmp(pSettings->systembarPlacement, "DockedToToolbar"))
                {
                        if (pSettings->toolbarOnTop) SetWindowPos(hSystembarWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                        else SetWindowPos(hSystembarWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                        SetTransparency(hSystembarWnd, pSettings->toolbarTransparencyAlpha);
                }
                else
                {
                        if (pSettings->systembarOnTop) SetWindowPos(hSystembarWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                        else SetWindowPos(hSystembarWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                        SetTransparency(hSystembarWnd, pSettings->systembarTransparencyAlpha);
                }

                ShowWindow(hSystembarWnd, pSettings->systembarHidden ? SW_HIDE : SW_SHOWNOACTIVATE);
        }

        //====================

        INITCOMMONCONTROLSEX ic;
        ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
        ic.dwICC = ICC_BAR_CLASSES;

        InitCommonControlsEx(&ic);

        //====================

        if (!pSettings->systembarTooltipsDisabled) CreateTooltipsWindow();

        WinEnumeratorCount = 0;
        blockNextMouseClick = false;
        saveWidthOnRestart = false;
        ZeroMemory(&WindowList, sizeof(WindowList));
}

//===========================================================================

Systembar::~Systembar()
{
        if (pSlit && DockedToSlit) SendMessage(pSlit->hSlitWnd, SLIT_REMOVE, NULL, (LPARAM)hSystembarWnd);

        // Go through each of the elements in the WindowList array clearing each one
        for (int i=0; i < WinEnumeratorCount; i++)
        { 
                memset(&WindowList[i], 0, sizeof(TaskButton));
        }

        int msgs[] = { BB_RECONFIGURE, BB_TOGGLESYSTEMBAR, BB_ADDTASK, BB_REMOVETASK, BB_REDRAW, BB_ACTIVETASK, BB_ACTIVATESHELLWINDOW, 0 };
        SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hSystembarWnd, (LPARAM)msgs);
        if (hSystembarWnd) DestroyWindow(hSystembarWnd); // delete our window
        if (hToolTips) DestroyWindow(hToolTips);
        UnregisterClass(szSystembarName,hSystembarInstance); // unregister window class

        // Delete cached bitmaps...
        DeleteDC(cachedTaskButtonInactive);
        DeleteDC(cachedTaskButtonActive);
        DeleteDC(cachedBackground);
}

//===========================================================================

void Systembar::Initialize()
{
        EnumWindows(WinEnumerator, 0);
}

//===========================================================================

LRESULT CALLBACK SystembarWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        
        switch (message)
        {
        //====================

                case WM_PAINT:
                {
                        PAINTSTRUCT ps;
                        HDC hdc = BeginPaint(hwnd, &ps);
                        HDC buf = CreateCompatibleDC(NULL);
                        RECT r;
                        int updateRectWidth = ps.rcPaint.right - ps.rcPaint.left;
                        int updateRectHeight = ps.rcPaint.bottom - ps.rcPaint.top;

                        HBITMAP bufbmp = CreateCompatibleBitmap(hdc, pSystembar->SystembarWidth, pSystembar->SystembarHeight);
                        HBITMAP oldbuf = (HBITMAP)SelectObject(buf, bufbmp);

                        //====================

                        // If we have not yet created a cached background, let's do that...
                        if (!pSystembar->cachedBackgroundExists)
                        {
                                // Create a temporary bitmap...
                                HBITMAP tempBitmap = CreateCompatibleBitmap(hdc, pSystembar->SystembarWidth, pSystembar->SystembarHeight);

                                // Delete the previously cached gradient...
                                HBITMAP oldBitmap = (HBITMAP)SelectObject(pSystembar->cachedBackground, tempBitmap);
                                DeleteObject(oldBitmap);

                                // Paint background + border... (now using MakeGradient instead of CreateBorder + CreateGradientByRect)
                                GetClientRect(hwnd, &r);
                                MakeGradient(pSystembar->cachedBackground, r, pSettings->Toolbar->type, pSettings->Toolbar->Color, pSettings->Toolbar->ColorTo, pSettings->Toolbar->interlaced, pSettings->Toolbar->bevelstyle, pSettings->Toolbar->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, pSettings->Toolbar->borderWidth);

                                // Clean up - delete the temporary bitmap...
                                DeleteObject(tempBitmap);

                                // Set the "cached background created" indicator...
                                pSystembar->cachedBackgroundExists = true;
                        }

                        //====================

                        // Copy the cached background into the temporary buffer...
                        BitBlt(buf, ps.rcPaint.left, ps.rcPaint.top, updateRectWidth, updateRectHeight, pSystembar->cachedBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

                        // Paint the system tray and taskbar...
                        int iconPos = pSystembar->DrawSysTray(buf);

                        if (!pSystembar->SysTrayDisabled)
                        {
                                if (pTrayManager->ShowBalloonTooltip)
                                {
                                        // Create a single maximum width button for the "balloon tooltip"...
                                        r.left = r.top = pSettings->Toolbar->borderWidth + 2;
                                        r.right = pSystembar->SystembarWidth - iconPos - 2;
                                        int xmin = r.left + ICON_SIZE + 8;
                                        int xmax = r.right - 5;
                                        r.bottom = pSystembar->SystembarHeight - pSettings->Toolbar->borderWidth - 2;

                                        BYTE red, green, blue;
                                        COLORREF bColor, bColorTo, bTextColor, bShadowColor;
                                        red = GetRValue(pSettings->ActiveTask->Color);
                                        green = GetGValue(pSettings->ActiveTask->Color);
                                        blue = GetBValue(pSettings->ActiveTask->Color);
//                                      bColor = RGB((BYTE)blue, (BYTE)red, (BYTE)green);
//                                      bColor = RGB((BYTE)green, (BYTE)blue, (BYTE)red);
                                        bColor = RGB((BYTE)blue, (BYTE)green, (BYTE)red);
//                                      bColor = RGB((BYTE)red, (BYTE)green, (BYTE)blue);
                                        red = GetRValue(pSettings->ActiveTask->ColorTo);
                                        green = GetGValue(pSettings->ActiveTask->ColorTo);
                                        blue = GetBValue(pSettings->ActiveTask->ColorTo);
//                                      bColorTo = RGB((BYTE)blue, (BYTE)red, (BYTE)green);
//                                      bColorTo = RGB((BYTE)green, (BYTE)blue, (BYTE)red);
                                        bColorTo = RGB((BYTE)blue, (BYTE)green, (BYTE)red);
//                                      bColorTo = RGB((BYTE)red, (BYTE)green, (BYTE)blue);
                                        red = GetRValue(pSettings->ActiveTask->TextColor);
                                        green = GetGValue(pSettings->ActiveTask->TextColor);
                                        blue = GetBValue(pSettings->ActiveTask->TextColor);
//                                      bTextColor = RGB((BYTE)blue, (BYTE)red, (BYTE)green);
//                                      bTextColor = RGB((BYTE)green, (BYTE)blue, (BYTE)red);
                                        bTextColor = RGB((BYTE)blue, (BYTE)green, (BYTE)red);
//                                      bTextColor = RGB((BYTE)red, (BYTE)green, (BYTE)blue);
                                        red = GetRValue(pSettings->activeTaskShadowColor);
                                        green = GetGValue(pSettings->activeTaskShadowColor);
                                        blue = GetBValue(pSettings->activeTaskShadowColor);
//                                      bShadowColor = RGB((BYTE)blue, (BYTE)red, (BYTE)green);
//                                      bShadowColor = RGB((BYTE)green, (BYTE)blue, (BYTE)red);
                                        bShadowColor = RGB((BYTE)blue, (BYTE)green, (BYTE)red);
//                                      bShadowColor = RGB((BYTE)red, (BYTE)green, (BYTE)blue);

//                                      MakeGradient(buf, r, B_SOLID, 0xccffff, 0xccffff, false, BEVEL_FLAT, BEVEL1, 0, 0x000000, 1);
                                        MakeGradient(buf, r, pSettings->ActiveTask->type, bColor, bColorTo, false, pSettings->ActiveTask->bevelstyle, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
//                                      MakeGradient(buf, r, pSettings->Slit->type, pSettings->Slit->Color, pSettings->Slit->ColorTo, pSettings->Slit->interlaced, pSettings->Slit->bevelstyle, pSettings->Slit->bevelposition, pSettings->bevelWidth, pSettings->slitBorderColor, 1);
//                                      MakeGradient(buf, r, pSettings->ActiveTask->type, pSettings->Slit->Color, pSettings->Slit->ColorTo, false, pSettings->ActiveTask->bevelstyle, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->slitBorderColor, 1);

                                        SIZE size;
                                        HFONT font = CreateFont(pSettings->Toolbar->FontHeight, 0, 0, 0, pSettings->Toolbar->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, pSettings->Toolbar->Font);
                                        HGDIOBJ oldfont = SelectObject(buf, font);
                                        SetBkMode(buf, TRANSPARENT);
                                        GetTextExtentPoint32(buf, pTrayManager->BalloonTooltipText, strlen(pTrayManager->BalloonTooltipText), &size);
                                        r.left = ((r.right - r.left - size.cx)/2) + 12;
                                        r.right = r.left + size.cx;
                                        if (r.left < xmin) r.left = xmin;
                                        if (r.right > xmax) r.right = xmax;
//                                      DrawTextWithShadow(buf, pTrayManager->BalloonTooltipText, r, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, 0x000000, 0x000000, false);
                                        DrawTextWithShadow(buf, pTrayManager->BalloonTooltipText, r, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, bTextColor, bShadowColor, pSettings->ActiveTask->FontShadow);
//                                      DrawTextWithShadow(buf, pTrayManager->BalloonTooltipText, r, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->ActiveTask->TextColor, pSettings->activeTaskShadowColor, pSettings->ActiveTask->FontShadow);
                                        DeleteObject(SelectObject(buf, oldfont));

                                        RECT iconRect;
                                        iconRect.left = r.left - ICON_SIZE - 2;
                                        iconRect.right = r.left + ICON_SIZE;
                                        iconRect.top = r.top + 1;
                                        iconRect.bottom = r.top + ICON_SIZE;

                                        HICON bIcon = LoadIcon(NULL, pTrayManager->BalloonTooltipIcon);

//                                      pSystembar->DrawSatHueIcon(buf, bIcon, ICON_SIZE, iconRect, false, false);
                                        DrawIconEx(buf, iconRect.left, iconRect.top, bIcon, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
                                        DeleteObject(bIcon);

                                        if (pTrayManager->BalloonTooltipItem.hWnd != NULL)
                                        {
                                                // Inform the parent application that the balloon tooltip has been shown...
                                                PostMessage(pTrayManager->BalloonTooltipItem.hWnd, pTrayManager->BalloonTooltipItem.uCallbackMessage, pTrayManager->BalloonTooltipItem.uID, NIN_BALLOONSHOW);
                                        }
                                }
                                else
                                {
                                        r.left = r.top = pSettings->Toolbar->borderWidth + 2;
                                        r.right = pSystembar->SystembarWidth - iconPos - 2;
                                        r.bottom = pSystembar->SystembarHeight - pSettings->Toolbar->borderWidth - 2;
                                        if (!pSettings->taskbarOnToolbar) pSystembar->DrawTaskbar(buf, iconPos, r);
                                        else if (pToolbar) pToolbar->DrawWindowLabel(buf, r);
                                }
                        }
                        else
                        {
                                r.left = r.top = pSettings->Toolbar->borderWidth + 2;
                                r.right = pSystembar->SystembarWidth - iconPos - 2;
                                r.bottom = pSystembar->SystembarHeight - pSettings->Toolbar->borderWidth - 2;
                                if (!pSettings->taskbarOnToolbar) pSystembar->DrawTaskbar(buf, iconPos, r);
                                else if (pToolbar) pToolbar->DrawWindowLabel(buf, r);
                        }

                        // Finally, we copy the buffer to the display...
                        BitBlt(hdc, 0, 0, pSystembar->SystembarWidth, pSystembar->SystembarHeight, buf, 0, 0, SRCCOPY);

                        //====================

                        SelectObject(buf, oldbuf);
                        DeleteDC(buf);
                        DeleteObject(bufbmp);
                        DeleteObject(oldbuf);

                        EndPaint(hwnd, &ps);
                        return 0;
                }
                break;

                //====================

                case BB_ADDTASK:
                {
                        pSystembar->AddWindow((HWND)wParam);
                }
                break;

                //====================

                case BB_REMOVETASK:
                {
                        pSystembar->RemoveWindow((HWND)wParam);
                }
                break;

                //====================

                case BB_ACTIVETASK:
                {
                        for (int i=0; i < pSystembar->WinEnumeratorCount; i++)
                        {
                                if (pSystembar->WindowList[i].Window == (HWND)wParam)
                                {
                                        pSystembar->WindowList[i].Active = true;
//                                      SetTransparency(pSystembar->WindowList[i].Window, 255);
                                }
                                else
                                {
                                        pSystembar->WindowList[i].Active = false;
//                                      SetTransparency(pSystembar->WindowList[i].Window, 210);
                                }
                        }

                        if (pSettings->taskbarOnToolbar && pToolbar) InvalidateRect(pToolbar->hToolbarWnd, &pToolbar->winlabelRect, false);
                        else if (pSystembar) InvalidateRect(pSystembar->hSystembarWnd, &pSystembar->MainRect, false);
                }
                break;

                //====================

                case BB_REDRAW:
                {
                        for (int i=0; i < pSystembar->WinEnumeratorCount; i++)
                        {
                                if (pSystembar->WindowList[i].Window == (HWND)wParam)
                                {
                                        if (pSettings->taskbarOnToolbar && pToolbar) InvalidateRect(pToolbar->hToolbarWnd, &pSystembar->WindowList[i].r, false);
                                        else if (pSystembar) InvalidateRect(pSystembar->hSystembarWnd, &pSystembar->WindowList[i].r, false);
                                        break;
                                }
                        }
                }
                break;

                //====================

                case BB_ACTIVATESHELLWINDOW:
                {
                        for (int i=0; i < pSystembar->WinEnumeratorCount; i++)
                                pSystembar->WindowList[i].Active = false;

//                      SetForegroundWindow(pSystembar->hBlackboxWnd);

                        if (pSettings->taskbarOnToolbar && pToolbar) InvalidateRect(pToolbar->hToolbarWnd, &pToolbar->winlabelRect, false);
                        else if (pSystembar) InvalidateRect(pSystembar->hSystembarWnd, &pSystembar->MainRect, false);
                }
                break;

                //====================

                case WM_DROPFILES:
                {
                        if (pSystembar->MouseButtonOnTaskbar(hwnd, message, wParam, lParam))
                        {
                                return 0;
                        }
                        else
                        {
                                static TCHAR filename[MAX_LINE_LENGTH];
                                DragQueryFile((HDROP)wParam, 0, filename, sizeof(filename));

                                // Make sure the dropped file isn't a directory...
                                if (PathIsDirectory(filename))
                                {
                                        MessageBox(0, "Trying to fool me, eh?\n...drag'n'drop a style file instead!", "xoblite", MB_OK | MB_TOPMOST);
                                        DragFinish((HDROP)wParam);
                                        return 0;
                                }
                                else
                                {
                                        // Look for a line that should "always" exist in a style file...
                                        if (strlen(ReadString(filename, "menu.frame:", "")))
                                        {
                                                // Set new style...
                                                SendMessage(pSystembar->hBlackboxWnd, BB_SETSTYLE, 0, (LPARAM)filename);
                                                DragFinish((HDROP)wParam);
                                        }
                                        else
                                        {
                                                MessageBox(0, "Trying to fool me, eh?\n...drag'n'drop a style file instead!", "xoblite", MB_OK | MB_TOPMOST);
                                                DragFinish((HDROP)wParam);
                                        }

                                        return 0;
                                }
                        }
                }
                break;

                //====================

                case BB_RECONFIGURE:
                {
                        // If needed, get new settings, resize window etc...
                        // NOTE: If DockedToToolbar then pSystembar->UpdatePosition is called from Toolbar!
                        if (stricmp(pSettings->systembarPlacement, "DockedToToolbar")) pSystembar->UpdatePosition();
//                      if (pSystembar->DockedToSlit) SendMessage(pSlit->hSlitWnd, SLIT_UPDATE, NULL, NULL);
                }
                break;

                //====================

                case BB_TOGGLESYSTEMBAR:
                {
                        if (!pSystembar->DockedToSlit)
                        {
                                if (!pSettings->systembarHidden) pSettings->systembarHidden = true;
                                else pSettings->systembarHidden = false;

                                WriteBool(pSettings->extrcFile, "xoblite.systembar.hidden:", pSettings->systembarHidden);

                                if (!pSettings->systembarHidden) pSystembar->UpdatePosition(); // Get new setting, resize window etc...
                                ShowWindow(pSystembar->hSystembarWnd, pSettings->systembarHidden ? SW_HIDE : SW_SHOWNOACTIVATE);
                        }
                        else MessageBox(GetBBWnd(), "Please note that the systembar window can not   \nbe independently hidden while docked to the slit.   \n\n...try hiding the slit window instead.", "xoblite", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
                }
                break;

                //====================

                case WM_CLOSE:
                        return 0;

                //====================

                case WM_DISPLAYCHANGE:
                {
                        if (pSettings->systembarHidden) break;
                        // If needed, get new settings, resize window etc...
                        // NOTE: If DockedToToolbar then pSystembar->UpdatePosition is called from Toolbar!
                        if (stricmp(pSettings->systembarPlacement, "DockedToToolbar")) pSystembar->UpdatePosition(); // Gets new settings and resizes window...
                        if (!pSystembar->SysTrayDisabled) pSystembar->UpdateToolTipPositions(); // Update tooltip x/y positions...
//                      // Force re-rendering of the background and task button bitmaps...
//                      pSystembar->cachedBackgroundExists = false;
//                      pSystembar->cachedTaskButtonsExist = false;
//                      if (pSystembar->DockedToSlit) SendMessage(pSlit->hSlitWnd, SLIT_UPDATE, NULL, NULL);
//                      InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                }
                break;

                //====================
/*
                case WM_MOUSEACTIVATE:
                        return MA_NOACTIVATE;

                case WM_ACTIVATE:
                        if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) SetActiveWindow(hwnd);
                return 0;
*/
                //====================

                case WM_WINDOWPOSCHANGING:
                {
                        // Snap window to screen edges when in manual positioning mode?
                        if (pSettings->systembarPlacement[0] == 'M' && pSettings->systembarSnapToEdges)
                        {
                                if (pSystembar)
                                {
                                        if (!pSystembar->DockedToSlit && IsWindowVisible(hwnd)) SnapWindowToEdge((WINDOWPOS*)lParam, pSettings->edgeSnapThreshold, true);
                                }
                        }
                }
                break;

                //====================

                case WM_EXITSIZEMOVE:
                {
                        if (pSystembar->SystembarPosInProgress)
                        {
                                pSystembar->SystembarPosInProgress = false;

                                RECT r;
                                GetWindowRect(pSystembar->hSystembarWnd, &r);
                                pSystembar->SystembarX = r.left;
                                pSystembar->SystembarY = r.top;
                                sprintf(pSettings->systembarPlacement, "Manual x%d y%d", pSystembar->SystembarX, pSystembar->SystembarY);

                                pSystembar->UpdatePosition(); // NOTE: Including saving placement to extensions.rc
                        }

                        return 0;
                }

                //====================

                // Allow window to move if control key is being held down,
                // holding down the shift key and left clicking moves the window
                // back to the default position...
                case WM_NCHITTEST:
                {
                        if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && !pSystembar->DockedToSlit)
                        {
                                pSystembar->SystembarPosInProgress = true;
                                return HTCAPTION;
                        }
                        return HTCLIENT;
                }
                break;

                //====================

                case WM_MOUSEMOVE:
                {
                        if (pSettings->toolbarAutoHide && pToolbar)
                        {
                                if (!stricmp(pSettings->systembarPlacement, "DockedToToolbar"))
                                {
                                        if (IsInString(pSettings->toolbarPlacement, "Bottom"))
                                        {
                                                if (HIWORD(lParam) == (pSystembar->SystembarHeight - 1)) pToolbar->AutoShow();
                                        }
                                        else
                                        {
                                                if (HIWORD(lParam) == 0) pToolbar->AutoShow();
                                        }
                                }
                        }
/*
                        if (pSystembar->hToolTips) // Reroute mouse messages to the tooltip window...
                        {
                                MSG ttMsg;

                                ttMsg.message = message;
                                ttMsg.hwnd = hwnd;
                                ttMsg.wParam = wParam;
                                ttMsg.lParam = lParam;

                                SendMessage(pSystembar->hToolTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&ttMsg);
                        }
*/
                }
                break;

                //====================

                case WM_LBUTTONDOWN:
                case WM_LBUTTONUP:
                case WM_LBUTTONDBLCLK:
                case WM_RBUTTONDOWN:
                case WM_RBUTTONUP:
                case WM_RBUTTONDBLCLK:
                case WM_MBUTTONDOWN:
                case WM_MBUTTONUP:
                case WM_MBUTTONDBLCLK:
                {
                        if (pSystembar->MouseButtonOnTaskbar(hwnd, message, wParam, lParam))
                        {
                                return DefWindowProc(hwnd, message, wParam, lParam);
                        }
                        else
                        {
                                if (!pSettings->systrayHidden && !pSystembar->SysTrayDisabled)
                                {
                                        int offsetIconX = 2 + pSettings->Toolbar->borderWidth;
//                                      int offsetIconY = 3 + pSettings->Toolbar->borderWidth;
                                        int offsetIconY = pSystembar->SystembarHeight/2 - ICON_SIZE/2;

                                        // Calculate relative x position from the right...
                                        int relX = pSystembar->SystembarWidth - LOWORD(lParam) - offsetIconX;
                                        // Calculate relative y position...
                                        int relY = HIWORD(lParam);

//                                      if (relY >= offsetIconY && relY <= (pSystembar->SystembarHeight - offsetIconY))
                                        if (relY >= offsetIconY && relY <= (offsetIconY + ICON_SIZE))
                                        {
                                                for (int i = 0; i < (int)pTrayManager->trayIconList.size(); i++)
                                                {
                                                        if ((relX > (i * (ICON_SIZE + 2))) && (relX <= ((i+1) * (ICON_SIZE + 2))))
                                                        {
                                                                if (pTrayManager->trayIconList[i]->hWnd)
                                                                {
                                                                        // Set the focus to the right/left click icon menu, so that it will disappear when it looses focus
                                                                        if (message == WM_RBUTTONDOWN || message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN) SetForegroundWindow(pTrayManager->trayIconList[i]->hWnd);
                                                                        // Reroute the mouse message to the tray icon's host window...
//                                                                      SendMessage(pTrayManager->trayIconList[i]->hWnd, pTrayManager->trayIconList[i]->uCallbackMessage, pTrayManager->trayIconList[i]->uID, LPARAM(message));
                                                                        PostMessage(pTrayManager->trayIconList[i]->hWnd, pTrayManager->trayIconList[i]->uCallbackMessage, pTrayManager->trayIconList[i]->uID, LPARAM(message));
                                                                        return DefWindowProc(hwnd, message, wParam, lParam);
                                                                }
                                                        }
                                                }
                                        }
                                }

                                //====================

                                if (message == WM_LBUTTONUP)
                                {
                                        POINT pt;
                                        int pointx = LOWORD(lParam);
                                        int pointy = HIWORD(lParam);
                                        pt.x = pointx;
                                        pt.y = pointy;

                                        if (PtInRect(&pSystembar->ToggleTrayButtonRect, pt))
                                        {
                                                pSystembar->ToggleSysTray();
                                                return DefWindowProc(hwnd, message, wParam, lParam);
                                        }
                                }

                                //====================

                                if (message == WM_LBUTTONDOWN || message == WM_LBUTTONUP)
                                {
                                        pMenuMaker->Hide();
                                        if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) && !pSystembar->DockedToSlit)
                                        {
                                                if (!stricmp(pSettings->systembarPlacement, "DockedToToolbar") && pToolbar) pToolbar->UpdatePosition();
                                                else pSystembar->UpdatePosition();
                                        }
                                }

                                if (message == WM_RBUTTONUP) // Only show menus on RButtonUp...
                                {
                                        if (pSystembar->blockNextMouseClick)
                                        {
                                                pSystembar->blockNextMouseClick = false;
                                                return DefWindowProc(hwnd, message, wParam, lParam);
                                        }

                                        // Is the Alt key held down?
                                        if (GetAsyncKeyState(VK_MENU) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 0, 0);
                                        else SendMessage(GetBBWnd(), BB_MENU, 3, 0);
                                        return DefWindowProc(hwnd, message, wParam, lParam);
                                }

                                if (message == WM_MBUTTONUP)
                                {
                                        if (GetAsyncKeyState(VK_SHIFT) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);
                                        else if (stricmp(pSettings->systembarPlacement, "DockedToToolbar")) PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0);
                                        else PostMessage(GetBBWnd(), BB_TOGGLESLIT, 0, 0);
                                }

                                return DefWindowProc(hwnd, message, wParam, lParam);
                        }
                }
                break;

                //====================

                case WM_XBUTTONDOWN:
                {
                        if (pSystembar->MouseButtonOnTaskbar(hwnd, message, wParam, lParam))
                        {
                                return DefWindowProc(hwnd, message, wParam, lParam);
                        }
                        else if (HIWORD(wParam) == XBUTTON1)
                        {
                                if (!pSettings->taskbarOnToolbar)
                                {
                                        if (GetAsyncKeyState(VK_MENU) & 0x8000) pSystembar->RestoreAllWindows();
                                        else pSystembar->MinimizeAllWindows();
                                }
                                else BBExecute(GetDesktopWindow(), NULL, "control.exe", "desk.cpl,,3", NULL, SW_SHOWNORMAL, false);
                        }
                        else if (HIWORD(wParam) == XBUTTON2)
                        {
                                if (!pSettings->taskbarOnToolbar) pSystembar->RestoreAllWindows();
                                else BBExecute(GetDesktopWindow(), NULL, "control.exe", "mmsys.cpl", NULL, SW_SHOWNORMAL, false);
                        }
                }
                break;

                case WM_XBUTTONUP: { } break;

                //====================

                case WM_MOUSEWHEEL:
                {
                        // Only allow mousewheel resizing when the control key is held down...
                        if (!(GetAsyncKeyState(VK_CONTROL) & 0x8000)) break;
                        // Do not allow mousewheel resizing if DockedToToolbar or DockedToSlit...
                        if (IsInString(pSettings->systembarPlacement, "DockedTo")) break;

                        bool sizechanged = false;

                        if (GET_WHEEL_DELTA_WPARAM(wParam) < 0)
                        {
                                if (pSettings->systembarWidthPercent > 30)
                                {
                                        sizechanged = true;
                                        pSettings->systembarWidthPercent--;
                                }
                        }
                        else
                        {
                                if (pSettings->systembarWidthPercent < 100)
                                {
                                        sizechanged = true;
                                        pSettings->systembarWidthPercent++;
                                }
                        }

                        if (sizechanged)
                        {
                                pSystembar->GetSettings();
                                // Force re-rendering of the background and task button bitmaps...
                                pSystembar->cachedBackgroundExists = false;
                                pSystembar->cachedTaskButtonsExist = false;
                                InvalidateRect(pSystembar->hSystembarWnd, NULL, false);

                                MoveWindow(pSystembar->hSystembarWnd, pSystembar->SystembarX, pSystembar->SystembarY, pSystembar->SystembarWidth, pSystembar->SystembarHeight, true);
                                if (pSettings->toolbarOnTop) SetWindowPos(pSystembar->hSystembarWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                                else SetWindowPos(pSystembar->hSystembarWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
                                if (!pSystembar->SysTrayDisabled) pSystembar->UpdateToolTipPositions(); // Update tooltip x/y positions...

                                pSystembar->saveWidthOnRestart = true;
                        }

                        // Due to the number of repeated updates when using the mousewheel to change the systembar width,
                        // we do not save the systembar width to extensions.rc each time -> on restart/quit instead...
                        // WriteInt(pSettings->extrcFile, "xoblite.systembar.widthPercent:", pSettings->systembarWidthPercent);
                }
                break;

                //====================

                case WM_TIMER:
                {
                        if (wParam == SYSTEMBAR_XOBLOON_TIMER)
                        {
                                if (!pSystembar->SysTrayDisabled)
                                {
                                        if (pTrayManager->ShowBalloonTooltip)
                                        {
                                                KillTimer(pSystembar->hSystembarWnd, SYSTEMBAR_XOBLOON_TIMER);
                                                pTrayManager->ShowBalloonTooltip = false;
                                                if (pTrayManager->BalloonTooltipItem.hWnd != NULL)
                                                {
                                                        // Inform the parent application that the balloon tooltip has been dismissed on timeout...
                                                        PostMessage(pTrayManager->BalloonTooltipItem.hWnd, pTrayManager->BalloonTooltipItem.uCallbackMessage, pTrayManager->BalloonTooltipItem.uID, NIN_BALLOONTIMEOUT);
                                                }
                                                InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                                                return true;
                                        }
                                }
                        }
                        return 0;
                }
                break;

                //====================

                default:
                        return DefWindowProc(hwnd,message,wParam,lParam);

        //====================
        }
        return 0;
}

//===========================================================================
// Function: MouseButtonOnTaskbar
// Purpose: ...
//===========================================================================

bool Systembar::MouseButtonOnTaskbar(HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
        if (!SysTrayDisabled)
        {
                if (pTrayManager->ShowBalloonTooltip)
                {
                        pTrayManager->ShowBalloonTooltip = false;
                        if (pTrayManager->BalloonTooltipItem.hWnd != NULL)
                        {
                                // Inform the parent application that the balloon tooltip has been dismissed by user click...
                                PostMessage(pTrayManager->BalloonTooltipItem.hWnd, pTrayManager->BalloonTooltipItem.uCallbackMessage, pTrayManager->BalloonTooltipItem.uID, NIN_BALLOONUSERCLICK);
                        }
                        InvalidateRect(hSystembarWnd, NULL, false);
                        return true;
                }
        }

        //====================

        if (pSettings->taskbarHidden) return false;
        if (pSettings->taskbarOnToolbar && (hwnd == pSystembar->hSystembarWnd)) return false;

        //====================

        POINT pt;

        if (message == WM_DROPFILES)
        {
                // Drag'n'drop of files onto a task button...
                GetCursorPos(&pt);
                ScreenToClient(hwnd, &pt);
        }
        else
        {
                // Regular mouse clicks on a task button...
                pt.x = LOWORD(lParam);
                pt.y = HIWORD(lParam);
        }

        if (!PtInRect(&MainRect, pt)) return false;

        //====================
        
        HWND Window;
        int mousex;

        if (pSettings->taskbarCurrentOnly) NumberOfTasks = CurrentOnlyCount;
        else NumberOfTasks = WinEnumeratorCount;

        if (pSettings->taskbarMode != 2) // Bars or Bars+Icons mode
        {
            TaskWidth = MainRect.right - MainRect.left;
                if (NumberOfTasks > 0) TaskWidth = TaskWidth / NumberOfTasks;
        }
        else // Icons mode
        {
                if (pSettings->taskbarOnToolbar) TaskWidth = 18; // 11+7 -> Smaller icons when placed on the toolbar...
                else TaskWidth = 22; // 14+8 -> Larger icons when placed on the systembar...
        }

        mousex = pt.x;
        mousex = mousex - MainRect.left;
        mousex = abs(mousex / TaskWidth);
        
        if (mousex >= NumberOfTasks) return false;

        //====================

        // Is the taskbar showing only tasks on the current workspace?
        if (pSettings->taskbarCurrentOnly)
        {
                int TaskOnWorkspaceNumber;
                int counter = mousex + 1;

                for (mousex = 0; counter != 0; mousex++)
                {
                        TaskOnWorkspaceNumber = pWorkspaces->getDesktop(WindowList[mousex].Window);
                        if (TaskOnWorkspaceNumber == pWorkspaces->currentScreen || TaskOnWorkspaceNumber == 255)
                                counter--;
                }
                mousex--;
        }

        //====================

        switch (message)
    {
                //====================

                case WM_LBUTTONDOWN: // Restore and focus window
                {
                        if (pMenuMaker) pMenuMaker->Hide();

                        if (GetAsyncKeyState(VK_MENU) & 0x8000)
                        {
                                if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
                                {
                                        Window = WindowList[mousex].Window;
                                        if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                                        SetForegroundWindow(Window);
                                        SendMessage(hBlackboxWnd, BB_BRINGTOFRONT, 0, (LPARAM)Window);
                                        PostMessage(Window, WM_SYSCOMMAND, SC_MOVE, 0);
                                }
                                else RestoreAllWindows();
                        }
                        else if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) && !DockedToSlit)
                        {
                                if (!stricmp(pSettings->systembarPlacement, "DockedToToolbar") && pToolbar) pToolbar->UpdatePosition();
                                else UpdatePosition();
                        }
                        else
                        { 
                                Window = WindowList[mousex].Window;
                                if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                                SetForegroundWindow(Window);
                                SendMessage(hBlackboxWnd, BB_BRINGTOFRONT, 0, (LPARAM)Window);
                        } 
                        return true;
                }
                break;

                //====================

                case WM_LBUTTONDBLCLK: 
                case WM_RBUTTONDOWN:   // Minimize (iconify) window
                {
                        if (GetAsyncKeyState(VK_MENU) & 0x8000)
                        {
                                if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
                                {
                                        Window = WindowList[mousex].Window;
                                        if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                                        SetForegroundWindow(Window);
                                        SendMessage(hBlackboxWnd, BB_BRINGTOFRONT, 0, (LPARAM)Window);
                                        PostMessage(Window, WM_SYSCOMMAND, SC_SIZE, 0);
                                }
                                else MinimizeAllWindows();
                        }
                        else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) return true; // Pass through -> menu click on RBUTTONUP below...
                        else
                        {
                                if ((message == WM_RBUTTONDOWN) && pSettings->taskbarCurrentOnly && (NumberOfTasks == 1)) blockNextMouseClick = true;
                                Window = WindowList[mousex].Window;
                                ShowWindow(Window, SW_MINIMIZE);
                                SendMessage(hBlackboxWnd, BB_WINDOWMINIMIZE, 0, (LPARAM)Window);
                        }
                        return true;
                }
                break;

                //====================

                case WM_MBUTTONDOWN:
                {
                        Window = WindowList[mousex].Window;
                        if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                        SetForegroundWindow(Window);

                        if (GetAsyncKeyState(VK_MENU) & 0x8000)
                        {
                                // Pass through if the Alt+Shift keys are held down...
                                // (the close application command is triggered on WM_MBUTTONUP)
                                if (GetAsyncKeyState(VK_SHIFT) & 0x8000) return true;
                                // Shade the window if the Alt key is held down...
                                else pDesk->ShadeWindow();
                        }
                        else
                        {
                                // ...otherwise move the window to the next/previous workspace
                                SendMessage(hBlackboxWnd, BB_BRINGTOFRONT, 0, (LPARAM)Window);

                                MakeSticky(Window);
                                if (GetAsyncKeyState(VK_MENU) & 0x8000) pWorkspaces->DeskLeft();
                                else pWorkspaces->DeskRight();
                                RemoveSticky(Window);
                        }

                        return true;
                }
                break;

                //====================

                case WM_LBUTTONUP: 
                {
                        return true;
                }
                break;

                //====================

                case WM_RBUTTONUP: // Only show menus on RButtonUp...
                {
                        if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 3, 0);
                        return true;
                }
                break;

                //====================

                case WM_MBUTTONUP: 
                {
                        // Close the application if the Alt+Shift keys are held down...
                        if ((GetAsyncKeyState(VK_MENU) & 0x8000) && (GetAsyncKeyState(VK_SHIFT) & 0x8000))
                        {
                                Window = WindowList[mousex].Window;
                                if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                                SetForegroundWindow(Window);
                                PostMessage(Window, WM_SYSCOMMAND, SC_CLOSE, 0);
                        }

                        return true;
                }
                break;

                //====================

                case WM_XBUTTONDOWN:
                {
                        if (HIWORD(wParam) == XBUTTON1)
                        {
                                if (GetAsyncKeyState(VK_MENU) & 0x8000) RestoreAllWindows();
                                else MinimizeAllWindows();
                        }
                        else if (HIWORD(wParam) == XBUTTON2)
                        {
                                RestoreAllWindows();
                        }
                        return true;
                }
                break;

                case WM_XBUTTONUP: { } break;

                //====================

                case WM_DROPFILES:
                {
                        // Forward the drag'n'drop information to the window in question...
                        Window = WindowList[mousex].Window;
                        if (IsIconic(Window)) ShowWindow(Window, SW_RESTORE);
                        SetForegroundWindow(Window);
                        SendMessage(hBlackboxWnd, BB_BRINGTOFRONT, 0, (LPARAM)Window);

                        PostMessage(Window, WM_DROPFILES, wParam, lParam);

                        return true;
                }
                break;

                //====================
        }

        return false;
}

//===========================================================================
// Function: DrawSysTray
// Purpose: ...
//===========================================================================

int Systembar::DrawSysTray(HDC hdc)
{
        RECT iconRect;
        int offsetIconX = 2 + pSettings->Toolbar->borderWidth;
//      int offsetIconY = 3 + pSettings->Toolbar->borderWidth;
        int offsetIconY = SystembarHeight/2 - ICON_SIZE/2;
        int iconPos = 0; // This default is used by WindowListerWM_PAINT (see below) if there are no icons in the systray

        if (!SysTrayDisabled)
        {
                if (!pSettings->systrayHidden)
                {
                        for (int i=0; i < (int)pTrayManager->trayIconList.size(); i++)
                        {
                                if (!pTrayManager->trayIconList[i]->hWnd) break;
                                iconPos = (i+1) * (ICON_SIZE + 2) + offsetIconX;        // Calculate icon position...
                                if (iconPos > (SystembarWidth - offsetIconX)) break; // Check if the icon fits the toolbar...

                                iconRect.left = SystembarWidth - iconPos;
                                iconRect.top = offsetIconY;
                                iconRect.right = iconRect.left + ICON_SIZE;
                                iconRect.bottom = iconRect.top + ICON_SIZE;

                                if (pTrayManager->trayIconList[i]->hIcon) DrawSatHueIcon(hdc, pTrayManager->trayIconList[i]->hIcon, ICON_SIZE, iconRect, true, false);

                                // Save the icon rect so we can InvalidateRect for this icon only if needed (see TrayManager::ModifyTrayIcon)
                                SetRect(&pTrayManager->trayIconList[i]->iconRect, iconRect.left, iconRect.top, iconRect.right, iconRect.bottom);
                        }

                        iconPos = iconPos + 3; // Add two pixels of padding between the systray icons and the taskbar...
                        ToggleTrayButtonRect.right = SystembarWidth - iconPos;
                }
                else
                {
                        ToggleTrayButtonRect.right = SystembarWidth - pSettings->Toolbar->borderWidth - 3;
                        iconPos = pSettings->Toolbar->borderWidth + 3;
                }

                // Draw toggle tray button...
                ToggleTrayButtonRect.left = ToggleTrayButtonRect.right - 8;
                ToggleTrayButtonRect.top = pSettings->Toolbar->borderWidth + 3;
                ToggleTrayButtonRect.bottom = SystembarHeight - pSettings->Toolbar->borderWidth - 3;
                pBImage->CreateGradientByRect(hdc, ToggleTrayButtonRect, pSettings->Toolbar->type, pSettings->Toolbar->Color, pSettings->Toolbar->ColorTo, false, BEVEL_RAISED, BEVEL1, 1);

                iconPos = iconPos + 8; // Add two pixels of padding between the systray icons and the taskbar...
        }
        else iconPos = pSettings->Toolbar->borderWidth; // SysTray disabled

        TrayRect.left = SystembarWidth - iconPos;
        TrayRect.top = pSettings->Toolbar->borderWidth;
        TrayRect.right = SystembarWidth - pSettings->Toolbar->borderWidth;
        TrayRect.bottom = SystembarHeight - pSettings->Toolbar->borderWidth;

        return iconPos;
}

//===========================================================================
// Function: DrawTaskbar
// Purpose: ...
//===========================================================================

void Systembar::DrawTaskbar(HDC hdc, int iconPos, RECT taskbarRect)
{
        CopyRect(&MainRect, &taskbarRect);
        MainDC = hdc;

        // Reset flash indicator if flashing is disabled...
        if (!pSettings->taskbarFlashing) flashingHwnd = 0;

        //====================

        if (!pSettings->taskbarHidden)
        {
                if (pSettings->taskbarCurrentOnly)
                {
                        CurrentOnlyCount = 0;
                        int TaskOnWorkspaceNumber;

                        for (int x = 0; x < WinEnumeratorCount; x++)
                        {
//                              if (pWorkspaces->getDesktop(WindowList[x].Window) == pWorkspaces->currentScreen)
                                TaskOnWorkspaceNumber = pWorkspaces->getDesktop(WindowList[x].Window);
                                if (TaskOnWorkspaceNumber == pWorkspaces->currentScreen || TaskOnWorkspaceNumber == 255)
                                        CurrentOnlyCount++;
                        }

                        NumberOfTasks = CurrentOnlyCount;
                }
                else NumberOfTasks = WinEnumeratorCount;
        }

        //====================

        if (pSettings->taskbarHidden || NumberOfTasks == 0)
        {
                HFONT font = CreateFont(pSettings->Toolbar->FontHeight, 0, 0, 0, pSettings->Toolbar->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, pSettings->Toolbar->Font);
                HGDIOBJ oldfont = SelectObject(MainDC, font);
                SetBkMode(MainDC, TRANSPARENT);

                SetTextColor(MainDC, pSettings->Toolbar->TextColor);
                DrawText(MainDC, "xoblite", strlen("xoblite"), &MainRect, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS);

                DeleteObject(SelectObject(MainDC, oldfont));
        }
        else DrawTaskButtons();
}

//===========================================================================
// Function: DrawTaskButtons
// Purpose: Draws task buttons on the taskbar
//===========================================================================

int Systembar::DrawTaskButtons()
{
        // Smaller icons when placed on the toolbar,
        // larger icons when placed on the systembar...
        if (pSettings->taskbarOnToolbar) iconSize = 11;
        else iconSize = 14;

        //====================

        // Calculate button dimensions...

        if (pSettings->taskbarMode != 2)
        {
                // Bars or Bars+Icons mode...
                TaskWidth = MainRect.right - MainRect.left;
                if (NumberOfTasks > 0) TaskWidth = TaskWidth / NumberOfTasks;
                TaskWidthMinusPadding = TaskWidth - 2; // Padding 2 pixels between task buttons...
        }
        else
        {
                // Icons mode...
                if (pSettings->taskbarOnToolbar)
                {
                        TaskWidth = iconSize + 7;
                        TaskWidthMinusPadding = TaskWidth - 1; // Padding 1 pixel between task buttons...
                }
                else
                {
                        TaskWidth = iconSize + 8;
                        TaskWidthMinusPadding = TaskWidth - 2; // Padding 2 pixels between task buttons...
                }
        }

        TaskHeight = MainRect.bottom - MainRect.top;

        //====================

        // If we have not yet created cached task buttons, let's do that...
        if (!pSystembar->cachedTaskButtonsExist)
        {
                RECT r = {0, 0, TaskWidthMinusPadding, TaskHeight};

                HBITMAP tempBitmapActive = CreateCompatibleBitmap(MainDC, TaskWidthMinusPadding, TaskHeight);
                HBITMAP oldBitmapActive = (HBITMAP)SelectObject(cachedTaskButtonActive, tempBitmapActive);
                DeleteObject(oldBitmapActive);
//              MakeGradient(cachedTaskButtonActive, r, pSettings->ActiveTask->type, pSettings->ActiveTask->Color, pSettings->ActiveTask->ColorTo, pSettings->ActiveTask->interlaced, BEVEL_SUNKEN, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                MakeGradient(cachedTaskButtonActive, r, pSettings->ActiveTask->type, pSettings->ActiveTask->Color, pSettings->ActiveTask->ColorTo, pSettings->ActiveTask->interlaced, pSettings->ActiveTask->bevelstyle, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                DeleteObject(tempBitmapActive);

                HBITMAP tempBitmapInactive = CreateCompatibleBitmap(MainDC, TaskWidthMinusPadding, TaskHeight);
                HBITMAP oldBitmapInactive = (HBITMAP)SelectObject(cachedTaskButtonInactive, tempBitmapInactive);
                DeleteObject(oldBitmapInactive);
//              MakeGradient(cachedTaskButtonInactive, r, pSettings->InactiveTask->type, pSettings->InactiveTask->Color, pSettings->InactiveTask->ColorTo, pSettings->InactiveTask->interlaced, BEVEL_RAISED, pSettings->InactiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                MakeGradient(cachedTaskButtonInactive, r, pSettings->InactiveTask->type, pSettings->InactiveTask->Color, pSettings->InactiveTask->ColorTo, pSettings->InactiveTask->interlaced, pSettings->InactiveTask->bevelstyle, pSettings->InactiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                DeleteObject(tempBitmapInactive);

                // Set the "cached task buttons created" indicator...
                pSystembar->cachedTaskButtonsExist = true;
        }

        //====================

        int DrawnTasks = 0;
        int TaskOnWorkspaceNumber;

        ThisButton.bottom = MainRect.bottom;
        ThisButton.top = MainRect.top;

        for (int i = 0; i < WinEnumeratorCount; i++)
        {
                //====================

                if (pSettings->taskbarCurrentOnly)
                {
                        // Exclude tasks not on the current workspace...
                        TaskOnWorkspaceNumber = pWorkspaces->getDesktop(WindowList[i].Window);
                        if (TaskOnWorkspaceNumber != pWorkspaces->currentScreen && TaskOnWorkspaceNumber != 255)
                                continue;
                }

                //====================

                // Calculate the bounding rect of the current task button
                // and save it so we can InvalidateRect it specifically if needed...

                ThisButton.left = MainRect.left + (DrawnTasks * TaskWidth);
                ThisButton.right = ThisButton.left + TaskWidthMinusPadding;

                if (pSettings->taskbarMode != 2) // Bars or Bars+Icons mode
                {
                        // Extend the rightmost button to fit the right border of the taskbar...
                        if (DrawnTasks == (NumberOfTasks - 1)) ThisButton.right = MainRect.right;
                }
                
                if (ThisButton.right > MainRect.right) break;

                CopyRect(&WindowList[i].r, &ThisButton);

                //====================

                // Create new tooltip or update existing tooltip with new bounding rect...
                GetWindowText(WindowList[i].Window, ThisButtonWindowText, 255);
                SetToolTip((UINT)WindowList[i].Window, WindowList[i].r, ThisButtonWindowText);

                //====================

                if (WindowList[i].Window == flashingHwnd) // Flashing task button?
                {
                        // Paint border+background for this flashed taskbar button...
                        MakeGradient(MainDC, ThisButton, pSettings->ActiveTask->type, pSettings->FlashingTask->Color, pSettings->FlashingTask->ColorTo, pSettings->InactiveTask->interlaced, pSettings->ActiveTask->bevelstyle, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
//                      MakeGradient(MainDC, ThisWin, pSettings->InactiveTask->type, pSettings->FlashingTask->Color, pSettings->FlashingTask->ColorTo, pSettings->InactiveTask->interlaced, pSettings->InactiveTask->bevelstyle, pSettings->InactiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                }
                else if ((DrawnTasks == (NumberOfTasks - 1)) && (pSettings->taskbarMode != 2)) // Rightmost button on the taskbar?
                {
                        // Paint border+background for this taskbar button...
//                      if (WindowList[i].Active) MakeGradient(MainDC, ThisButton, pSettings->ActiveTask->type, pSettings->ActiveTask->Color, pSettings->ActiveTask->ColorTo, pSettings->ActiveTask->interlaced, BEVEL_SUNKEN, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
//                      else MakeGradient(MainDC, ThisButton, pSettings->InactiveTask->type, pSettings->InactiveTask->Color, pSettings->InactiveTask->ColorTo, pSettings->InactiveTask->interlaced, BEVEL_RAISED, pSettings->InactiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                        if (WindowList[i].Active) MakeGradient(MainDC, ThisButton, pSettings->ActiveTask->type, pSettings->ActiveTask->Color, pSettings->ActiveTask->ColorTo, pSettings->ActiveTask->interlaced, pSettings->ActiveTask->bevelstyle, pSettings->ActiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                        else if (pSettings->taskbarInactiveBackground) MakeGradient(MainDC, ThisButton, pSettings->InactiveTask->type, pSettings->InactiveTask->Color, pSettings->InactiveTask->ColorTo, pSettings->InactiveTask->interlaced, pSettings->InactiveTask->bevelstyle, pSettings->InactiveTask->bevelposition, pSettings->bevelWidth, pSettings->Toolbar->borderColor, 1);
                }
                else
                {
                        // Copy the cached task button bitmap into the temporary buffer...
                        if (WindowList[i].Active) BitBlt(MainDC, ThisButton.left, ThisButton.top, TaskWidthMinusPadding, TaskHeight, cachedTaskButtonActive, 0, 0, SRCCOPY);
                        else if (pSettings->taskbarInactiveBackground) BitBlt(MainDC, ThisButton.left, ThisButton.top, TaskWidthMinusPadding, TaskHeight, cachedTaskButtonInactive, 0, 0, SRCCOPY);
                }

                //====================

                // Should we draw an icon on the task button?
                if (pSettings->taskbarMode != 1) // Bars+Icons or Icons mode
                {
                        // Fetch the window icon...
                        ThisButtonWnd = WindowList[i].Window;
                    SendMessageTimeout(ThisButtonWnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 200, (PDWORD_PTR)&ThisButtonIcon);
                        if (!ThisButtonIcon) ThisButtonIcon = (HICON)GetClassLongPtr(ThisButtonWnd, GCLP_HICONSM);
                        if (!ThisButtonIcon) SendMessageTimeout(ThisButtonWnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 200, (PDWORD_PTR)&ThisButtonIcon);
                        if (!ThisButtonIcon) ThisButtonIcon = (HICON)GetClassLongPtr(ThisButtonWnd, GCLP_HICON);

                        // Calculate icon position...
                        if (pSettings->taskbarMode == 3) iconRect.left = ThisButton.left + 5; // Bars+Icons mode
                        else iconRect.left = ThisButton.left + 3; // Icons mode
//                      iconRect.top = ThisButton.top + 2;
                        iconRect.top = ThisButton.top + (TaskHeight/2) - (iconSize/2);
                        iconRect.right = iconRect.left + iconSize;
                        iconRect.bottom = iconRect.top + iconSize;

                        // Draw the icon...
                        if (ThisButtonIcon) DrawSatHueIcon(MainDC, ThisButtonIcon, iconSize, iconRect, false, WindowList[i].Active);

                        ThisButton.left = iconRect.right + 3;
                }
                else ThisButton.left = ThisButton.left + 5;

                ThisButton.right = ThisButton.right - 5;

                //====================

                // Should we draw the window label on the task button?
                if (pSettings->taskbarMode != 2) // Bars or Bars+Icons mode
                {
                        HFONT font = CreateFont(pSettings->Toolbar->FontHeight, 0, 0, 0, pSettings->Toolbar->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, pSettings->Toolbar->Font);
                        HGDIOBJ oldfont = SelectObject(MainDC, font);
                        SetBkMode(MainDC, TRANSPARENT);

                        if (WindowList[i].Window == flashingHwnd) DrawTextWithShadow(MainDC, ThisButtonWindowText, ThisButton, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->FlashingTask->TextColor, pSettings->flashingTaskShadowColor, pSettings->ActiveTask->FontShadow);
                        else if (WindowList[i].Active) DrawTextWithShadow(MainDC, ThisButtonWindowText, ThisButton, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->ActiveTask->TextColor, pSettings->activeTaskShadowColor, pSettings->ActiveTask->FontShadow);
                        else DrawTextWithShadow(MainDC, ThisButtonWindowText, ThisButton, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->InactiveTask->TextColor, pSettings->inactiveTaskShadowColor, pSettings->InactiveTask->FontShadow);

                        DeleteObject(SelectObject(MainDC, oldfont));
                }

                //====================

                if (WindowList[i].Window == flashingHwnd) flashingHwnd = 0; // Reset flash indicator...

                DrawnTasks++;
        }

        return 1;
}

//===========================================================================
// Function: DrawSatHueIcon
// Purpose: ...
//===========================================================================

void Systembar::DrawSatHueIcon(HDC hdc, HICON icon, int size, RECT r, bool trayIcon, bool activeTask)
{
        DrawIconEx(hdc, r.left, r.top, icon, size, size, 0, NULL, DI_NORMAL);

        if (activeTask && !pSettings->taskbarActiveSatHue) return;

        //====================

        int saturation, hue;

        if (trayIcon)
        {
                saturation = pSettings->systraySaturationValue;
                hue = pSettings->systrayHueIntensity;
        }
        else
        {
                saturation = pSettings->taskbarSaturationValue;
                hue = pSettings->taskbarHueIntensity;
        }

        //====================

        // Should we apply saturation or hue?
        if (saturation < 255 || hue > 0)
        {
                hueColor = pSettings->Toolbar->Color;

                HDC iconhdc = CreateCompatibleDC(hdc);
                HBITMAP iconbufbmp = ::CreateCompatibleBitmap(hdc, size, size);
                HBITMAP oldiconbuf = (HBITMAP) ::SelectObject(iconhdc, iconbufbmp);

                // First we draw a masked version of the icon to the temporary buffer...
                DrawIconEx(iconhdc, 0, 0, icon, size, size, 0, NULL, DI_MASK);

                invsat = 255 - saturation;
                invhue = 255 - hue;

                for (int y=0; y < size; y++)
                {
                        for (int x=0; x < size; x++)
                        {
                                // Check if the mask pixel is white, ie. a transparent pixel...
                                if (!GetPixel(iconhdc, x, y))
                                {
                                        // First we read the original pixel's color...
                                        pixel = GetPixel(hdc, (r.left + x), (r.top + y));
                                        red = GetRValue(pixel);
                                        green = GetGValue(pixel);
                                        blue = GetBValue(pixel);

                                        // ...then we apply saturation...
                                        if (saturation < 255)
                                        {
                                                greyscale = (BYTE)(red*0.3086 + green*0.6094 + blue*0.0820);
                                                red = (BYTE)((red*saturation + greyscale*invsat + 255)>>8);
                                                green = (BYTE)((green*saturation + greyscale*invsat + 255)>>8);
                                                blue = (BYTE)((blue*saturation + greyscale*invsat + 255)>>8);
                                        }
                                        // ...and hue according to color and intensity...
                                        if (hue > 0)
                                        {
                                                red = (BYTE)((red*invhue + GetRValue(hueColor)*hue + 255)>>8);
                                                green = (BYTE)((green*invhue + GetGValue(hueColor)*hue + 255)>>8);
                                                blue = (BYTE)((blue*invhue + GetBValue(hueColor)*hue + 255)>>8);
                                        }
                                        // ...and finally we overwrite the original pixel with the new one...
                                        SetPixel(hdc, (r.left + x), (r.top + y), RGB(red, green, blue));
                                }
                        }
                }

                SelectObject(iconhdc, oldiconbuf);
                DeleteObject(iconbufbmp);
                DeleteDC(iconhdc);
        }
}

//===========================================================================
// Function: GetSettings
// Purpose: ...
//===========================================================================

void Systembar::GetSettings()
{
        ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
        ScreenHeight = GetSystemMetrics(SM_CYSCREEN);

//      if (pSettings->usingWin2kXP)
//      {
//              ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
//              ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
//      }
//      else
//      {
//              ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
//              ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
//      }

        //====================

//      int     ToolbarHeight;
//      if (pSettings->taskbarOnToolbar) ToolbarHeight = 17 + (pSettings->Toolbar->borderWidth * 2);
//      else ToolbarHeight = 15 + (pSettings->Toolbar->borderWidth * 2);
        int     ToolbarHeight = 0;
        if (pToolbar) ToolbarHeight = pToolbar->ToolbarHeight;

//      SystembarHeight = 22  + (pSettings->Toolbar->borderWidth * 2);
        int minimumHeight = 22  + (pSettings->Toolbar->borderWidth * 2);
        if (ToolbarHeight < minimumHeight) SystembarHeight = minimumHeight;
        else SystembarHeight = ToolbarHeight;

        //====================

        if (!stricmp(pSettings->systembarPlacement, "DockedToSlit"))
        {
                // We only need to calculate the size, position is set by the slit...
                SystembarWidth = (ScreenWidth * pSettings->systembarWidthPercent) / 100;
        }
        else if (!stricmp(pSettings->systembarPlacement, "DockedToToolbar"))
        {
                // Show the toolbar if hidden, we want to dock to it...
                if (!pSettings->systembarHidden && pSettings->toolbarHidden) PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0);

                SystembarWidth = (ScreenWidth * pSettings->toolbarWidthPercent) / 100;

                if (pSettings->toolbarPlacement[0] == 'M') // Manual positioning
                {
                        if (pToolbar)
                        {
                                RECT tbRect;
                                GetWindowRect(pToolbar->hTo