/*
 ============================================================================
 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

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

#include "Slit.h" 

const char szSlitName[] = "BBSlit"; // Window class etc.

extern Slit *pSlit;
extern BImage *pBImage;
extern Settings *pSettings;
extern MenuMaker *pMenuMaker;
extern PluginManager *pPluginManager;

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

Slit::Slit(HINSTANCE hInstance)
{
        WNDCLASS wc;
        hSlitInstance = hInstance;
        hBlackboxWnd = GetBBWnd();

        cachedBackground = CreateCompatibleDC(NULL);
        cachedBackgroundExists = false;

        reconfigureLock = false;
        SlitPosInProgress = false;
        SlitWidth = SlitHeight = 0;

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

        GetSettings();

        // Register our window class...
        ZeroMemory(&wc,sizeof(wc));
        wc.lpfnWndProc = SlitWndProc;                                           // our window procedure
        wc.hInstance = hSlitInstance;           
        wc.lpszClassName = szSlitName;                                          // our window class name

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

        hSlitWnd = CreateWindowEx(
                WS_EX_TOOLWINDOW,                                                                       // exstyles 
                szSlitName,                                                                                     // our window class name
                NULL,                                                                                           // use description for a window title
                WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                SlitX, SlitY,                                                                           // position
                0, 0,                                                                                           // width & height of window
                NULL,                                                                                           // parent window
                NULL,                                                                                           // no menu
                hSlitInstance,                                                                                          // hInstance of DLL
                NULL);

        if (!hSlitWnd)
        {                                                  
                UnregisterClass(szSlitName, hSlitInstance); // unregister window class
                MessageBox(0, "Error creating window", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("Slit: Error creating window", NULL);
                return;
        }

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

        int msgs[] = { BB_TOGGLESLIT, 0 };
        SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hSlitWnd, (LPARAM)msgs);

        // Apply transparency depending on extensions.rc setting...
        SetTransparency(hSlitWnd, pSettings->slitTransparencyAlpha);
        // Make the slit window sticky...
        MakeSticky(hSlitWnd);   
        // Set window to accept doubleclicks...
        SetClassLongPtr(hSlitWnd, GCL_STYLE, CS_DBLCLKS | GetClassLongPtr(hSlitWnd, GCL_STYLE));
        // Set window to accept drag'n'drop...
        DragAcceptFiles(hSlitWnd, true);
        // AlwaysOnTop?
        if (pSettings->slitOnTop) SetWindowPos(hSlitWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);

        // Hide the slit window on startup since there are no plugins docked to it yet...
        ShowWindow(hSlitWnd, SW_HIDE);

        // Note: Autohide is called from UpdatePosition() so we do not need to call it here!
}

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

Slit::~Slit()
{
        int msgs[] = { BB_TOGGLESLIT, 0 };
        SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hSlitWnd, (LPARAM)msgs);
        DestroyWindow(hSlitWnd); // delete our window
        UnregisterClass(szSlitName, hSlitInstance); // unregister window class

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

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

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

                case WM_PAINT:
                {
                        PAINTSTRUCT ps;
                        HDC hdc = BeginPaint(pSlit->hSlitWnd, &ps);
                        HDC buf = CreateCompatibleDC(NULL);
                        HBITMAP bufbmp = CreateCompatibleBitmap(hdc, pSlit->SlitWidth, pSlit->SlitHeight);
                        HBITMAP oldbuf = (HBITMAP)SelectObject(buf, bufbmp);
                        RECT r;
                        int updateRectWidth = ps.rcPaint.right - ps.rcPaint.left;
                        int updateRectHeight = ps.rcPaint.bottom - ps.rcPaint.top;

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

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

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

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

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

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

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

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

                        // Copy the cached background into the target buffer...
                        BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, updateRectWidth, updateRectHeight, pSlit->cachedBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

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

//                      BitBlt(hdc, 0, 0, pSlit->SlitWidth, pSlit->SlitHeight, buf, 0, 0, SRCCOPY);

                        SelectObject(buf, oldbuf);
                        DeleteDC(buf);
                        DeleteObject(bufbmp);
                        EndPaint(pSlit->hSlitWnd, &ps);
                }
                break;

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

                case WM_CLOSE:
                        return 0;

                //====================
/*
                case BB_RECONFIGURE:
                {
                        // NOTE: Not needed, since the slit plugins will send SLIT_UPDATE which invalidates the window!
                        pSlit->UpdatePluginPositions();
                }
                break;
*/
                //====================

                case WM_DISPLAYCHANGE:
                {
                        if (pSettings->slitHidden) break;
                        pSlit->UpdatePosition();
                }
                break;

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

                case SLIT_ADD:
                {
                        slitPluginItem plugin;
                        plugin.hwndPlugin = (HWND) lParam;
                        pSlit->SlitPlugins.push_back(plugin);

                        if (IsWindow((HWND)lParam))
                        {
                                SetWindowLongPtr((HWND)lParam, GWL_STYLE, (GetWindowLongPtr((HWND)lParam, GWL_STYLE) & ~WS_POPUP) | WS_CHILD);
                                SetParent((HWND)lParam, pSlit->hSlitWnd);
                        }

                        pSlit->UpdatePluginPositions();
                }
                break;

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

                case SLIT_REMOVE:
                {
                        vector<slitPluginItem>::iterator slitPlugin;

                        for (slitPlugin = pSlit->SlitPlugins.begin(); slitPlugin != pSlit->SlitPlugins.end(); slitPlugin++)
                        {
                                if (slitPlugin->hwndPlugin == (HWND)lParam)
                                {
                                        pSlit->SlitPlugins.erase(slitPlugin);
                                        break;
                                }
                        }

                        pSlit->UpdatePluginPositions();

                        if (IsWindow((HWND)lParam))
                        {
                                SetWindowLongPtr((HWND)lParam, GWL_STYLE, (GetWindowLongPtr((HWND)lParam, GWL_STYLE) & ~WS_CHILD) | WS_POPUP);
                                SetParent((HWND)lParam, NULL);
                        }
                }
                break;

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

                case SLIT_UPDATE:
                {
                        pSlit->UpdatePluginPositions();
                }
                break;

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

                case BB_TOGGLESLIT:
                {
                         // Only allow toggling of the slit if plugins are docked to it!
                        if (pSlit->SlitPlugins.size() == 0) break;

                        if (pSettings->slitHidden)
                        {
                                // Show window and force update...
                                pSettings->slitHidden = false;
                                ShowWindow(pSlit->hSlitWnd, SW_SHOW);
                                pSlit->UpdatePosition();
                        }
                        else
                        {
                                // Hide window...
                                pSettings->slitHidden = true;
                                ShowWindow(pSlit->hSlitWnd, SW_HIDE);
                        }

                        WriteBool(pSettings->extrcFile, "xoblite.slit.hidden:", pSettings->slitHidden);
                }
                break;

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

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

                        return 0;
                }

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

                case WM_EXITSIZEMOVE:
                {
                        if (pSlit->SlitPosInProgress)
                        {
                                pSlit->SlitPosInProgress = false;

                                RECT r;
                                GetWindowRect(pSlit->hSlitWnd, &r);
                                pSlit->SlitX = r.left;
                                pSlit->SlitY = r.top;
                                sprintf(pSettings->slitPlacement, "Manual x%d y%d", pSlit->SlitX, pSlit->SlitY);

                                WriteString(bbrcPath(), "session.screen0.slit.placement:", pSettings->slitPlacement);

                                // Disable autohide...
                                if (pSlit->SlitAutoHideEnabled)
                                {
                                        pSlit->SlitAutoHideEnabled = false;
                                        KillTimer(pSlit->hSlitWnd, SLIT_AUTOHIDE_TIMER);
                                }
                        }

                        return 0;
                }

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

                case WM_NCHITTEST:
                {
//                      // Allow manual positioning if the control key is being held down...
//                      if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
//                      {
//                              pSlit->SlitPosInProgress = true;
//                              return HTCAPTION;
//                      }
//                      else if (!pSlit->SlitAutoHideEnabled)
//                      if (!pSlit->SlitAutoHideEnabled)
//                      {
                                POINT p;
                                GetCursorPos(&p);
                                RECT r;
                                GetWindowRect(pSlit->hSlitWnd, &r);
                                pSlit->SlitX = r.left;
                                pSlit->SlitY = r.top;

                                p.x = p.x - pSlit->SlitX;
                                p.y = p.y - pSlit->SlitY;

                                // Allow manual positioning if we're not
                                // hovering over a child (plugin) window
                                // and the control key is being held down...
                                if (ChildWindowFromPoint(pSlit->hSlitWnd, p) == pSlit->hSlitWnd)
                                {
                                        if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
                                        {
                                                pSlit->SlitPosInProgress = true;
                                                return HTCAPTION;
                                        }
                                }
//                      }

                        return HTCLIENT;
                }

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

                case WM_LBUTTONUP:
                {
                        pMenuMaker->Hide();
                        return 0;
                }

                case WM_LBUTTONDOWN: {} break;

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

                case WM_RBUTTONUP:
                case WM_NCRBUTTONUP:
                {
                        // Is the control key held down?
                        if (GetAsyncKeyState(VK_MENU) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 0, 0);
                        // Is the  shift key held down?
                        else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 1, 0);
                        else SendMessage(GetBBWnd(), BB_MENU, 4, 0);

                        return 0;
                }

                case WM_RBUTTONDOWN:
                case WM_NCRBUTTONDOWN: {} break;

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

                case WM_MBUTTONUP:
                case WM_NCMBUTTONUP:
                {
                        // Is the alt key held down?
                        if (GetAsyncKeyState(VK_MENU) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0);
                        // Is the shift key held down?
                        else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);
                        // Is the control key held down?
                        else if (GetAsyncKeyState(VK_CONTROL) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);
                        // Otherwise toggle the systembar, or the slit if the systembar is docked to it...
                        else if (!stricmp(pSettings->systembarPlacement, "DockedToSlit")) PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0);
                        else PostMessage(GetBBWnd(), BB_TOGGLESYSTEMBAR, 0, 0);
                }
                break;

                case WM_MBUTTONDOWN:
                case WM_NCMBUTTONDOWN: {} break;

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

                case WM_MOUSEWHEEL:
                {
                        POINT p;
                        GetCursorPos(&p);
                        p.x = p.x - pSlit->SlitX;
                        p.y = p.y - pSlit->SlitY;
                        HWND pluginWnd = ChildWindowFromPoint(pSlit->hSlitWnd, p);
                        PostMessage(pluginWnd, WM_MOUSEWHEEL, (WPARAM)wParam, (LPARAM)lParam);
                }
                break;

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

                case WM_DROPFILES:
                {
                        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 if (IsInString(filename, ".dll"))
                        {
                                pPluginManager->loadPlugin(filename);
                        }
                        else
                        {
                                // Look for a line that should "always" exist in a style file...
                                if (strlen(ReadString(filename, "menu.frame", "")))
                                {
                                        // Set new style...
                                        SendMessage(pSlit->hBlackboxWnd, BB_SETSTYLE, 0, (LPARAM)filename);
                                        DragFinish((HDROP)wParam);
                                        return 0;
                                }
                                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 WM_MOUSEMOVE:
                {
                        if (pSlit->SlitAutoHideEnabled) pSlit->AutoShow();
                }
                break;

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

                case WM_TIMER:
                {
                        //====================

                        if (wParam == SLIT_AUTOHIDE_TIMER)
                        {
                                if (pSlit->SlitPosInProgress || (pSettings->slitPlacement[0] == 'M'))
                                {
                                         // Manual positioning -> no auto-hide!
                                        KillTimer(pSlit->hSlitWnd, SLIT_AUTOHIDE_TIMER);
                                        return 0;
                                }

                                if (pSlit->SlitAutoShowing)
                                {
                                        if (pSlit->SlitAutoCurrentPos == pSlit->SlitAutoShowTarget)
                                        {
                                                KillTimer(pSlit->hSlitWnd, SLIT_AUTOHIDE_TIMER);
                                                pSlit->SlitAutoShown = true;
                                                pSlit->SlitAutoShowing = false;
                                                if (!SetTimer(pSlit->hSlitWnd, SLIT_MOUSEHOVER_TIMER, 100, (TIMERPROC)NULL))
                                                {
                                                        MessageBox(GetBBWnd(), "Error creating mousehover timer", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                                                        Log("Could not create Slit mousehover timer", NULL);
                                                }
                                        }
                                        else
                                        {
                                                if (pSettings->slitPlacement[0] == 'C') // Center Left/Right
                                                {
                                                        if (IsInString(pSettings->slitPlacement, "Left")) pSlit->SlitAutoCurrentPos++;
                                                        else pSlit->SlitAutoCurrentPos--;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitAutoCurrentPos, pSlit->SlitY, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                                else if (pSettings->slitPlacement[0] == 'B') // Bottom Left/Center/Right
                                                {
                                                        pSlit->SlitAutoCurrentPos--;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitX, pSlit->SlitAutoCurrentPos, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                                else // Top Left/Center/Right
                                                {
                                                        pSlit->SlitAutoCurrentPos++;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitX, pSlit->SlitAutoCurrentPos, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                        }
                                }
                                else if (pSlit->SlitAutoHiding)
                                {
                                        if (pSlit->SlitAutoCurrentPos == pSlit->SlitAutoHideTarget)
                                        {
                                                KillTimer(pSlit->hSlitWnd, SLIT_AUTOHIDE_TIMER);
                                                pSlit->SlitAutoHidden = true;
                                                pSlit->SlitAutoHiding = false;
                                                SetTransparency(pSlit->hSlitWnd, 1);
                                        }
                                        else
                                        {
                                                if (pSettings->slitPlacement[0] == 'C') // Center Left/Right
                                                {
                                                        if (IsInString(pSettings->slitPlacement, "Left")) pSlit->SlitAutoCurrentPos--;
                                                        else pSlit->SlitAutoCurrentPos++;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitAutoCurrentPos, pSlit->SlitY, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                                else if (pSettings->slitPlacement[0] == 'B') // Bottom Left/Center/Right
                                                {
                                                        pSlit->SlitAutoCurrentPos++;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitX, pSlit->SlitAutoCurrentPos, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                                else // Top Left/Center/Right
                                                {
                                                        pSlit->SlitAutoCurrentPos--;
                                                        MoveWindow(pSlit->hSlitWnd, pSlit->SlitX, pSlit->SlitAutoCurrentPos, pSlit->SlitWidth, pSlit->SlitHeight, true);
                                                }
                                        }
                                }

                                return 0;
                        }

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

                        else if (wParam == SLIT_MOUSEHOVER_TIMER)
                        {
                                if (!pSlit->CheckIfMouseHover())
                                {
                                        KillTimer(pSlit->hSlitWnd, SLIT_MOUSEHOVER_TIMER);
                                        if (pSlit->SlitAutoHideEnabled) pSlit->AutoHide();
                                }
                                return 0;
                        }

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

                        else if (wParam == SLIT_RECONFIGURE_LOCK_TIMER)
                        {
                                pSlit->ReconfigureLock(false);
                                return 0;
                        }
                }
                break;

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

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

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

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

void Slit::ReconfigureLock(bool enable)
{
        if (enable)
        {
                if (!SetTimer(hSlitWnd, SLIT_RECONFIGURE_LOCK_TIMER, 300, (TIMERPROC)NULL))
                {
                        MessageBox(GetBBWnd(), "Error creating reconfigure lock timer", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                        Log("Could not create Slit reconfigure lock timer", NULL);
                        return;
                }
                reconfigureLock = true;
                ShowWindow(hSlitWnd, SW_HIDE); // Hide the window to "hide" any display artifacts while updating all plugins...
        }
        else
        {
                KillTimer(pSlit->hSlitWnd, SLIT_RECONFIGURE_LOCK_TIMER);
                pSlit->reconfigureLock = false;
                pSlit->UpdatePluginPositions();
                if (!pSettings->slitHidden && (pSlit->SlitPlugins.size() != 0)) ShowWindow(pSlit->hSlitWnd, SW_SHOWNOACTIVATE);
        }
}

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

void Slit::GetSettings()
{
        if (pSettings->usingWin2kXP)
        {
                ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
                ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
        }
        else
        {
                ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
                ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
        }

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

        if (!_stricmp(pSettings->slitDirection, "Vertical")) SlitVertical = true;
        else SlitVertical = false;

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

        char tempPlacement[MAX_LINE_LENGTH];
        strcpy(tempPlacement, pSettings->slitPlacement);

        if (tempPlacement[0] == 'M') // Manual positioning
        {
                char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH], token3[MAX_LINE_LENGTH];
                LPSTR tokens[2];
                tokens[0] = token1;
                tokens[1] = token2;

                token1[0] = token2[0] = token3[0] = '\0';
                BBTokenize (tempPlacement, tokens, 2, token3);

                SlitX = atoi(&token2[1]); // "x123"
                SlitY = atoi(&token3[1]); // "y456"

                // Make sure the slit is inside the visible screen...
                int xmax = ScreenWidth - SlitWidth;
                int ymax = ScreenHeight - SlitHeight;
                if (SlitX < 0) SlitX = 0;
                if (SlitY < 0) SlitY = 0;
                if (SlitX > xmax) SlitX = xmax;
                if (SlitY > ymax) SlitY = ymax;

                return;
        }

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

        if (tempPlacement[0] == 'O') // OppositeToolbar positioning
        {
                if (!stricmp(pSettings->toolbarPlacement, "TopCenter")) strcpy(tempPlacement, "BottomCenter");
                else if (!stricmp(pSettings->toolbarPlacement, "BottomCenter")) strcpy(tempPlacement, "TopCenter");
                else if (!stricmp(pSettings->toolbarPlacement, "TopLeft")) strcpy(tempPlacement, "BottomLeft");
                else if (!stricmp(pSettings->toolbarPlacement, "BottomLeft")) strcpy(tempPlacement, "TopLeft");
                else if (!stricmp(pSettings->toolbarPlacement, "TopRight")) strcpy(tempPlacement, "BottomRight");
                else if (!stricmp(pSettings->toolbarPlacement, "BottomRight")) strcpy(tempPlacement, "TopRight");
        }

        // Let's try the center positions first since they are probably most common...
        if (tempPlacement[0] == 'C') // || tempPlacement[0] == 'c')
        {
                if(!_stricmp(tempPlacement, "CenterLeft"))
                {
                        SlitY = (ScreenHeight - SlitHeight) / 2;
                        SlitX = 0;
                        SlitAutoHideTarget = 1 - SlitWidth;
                        SlitAutoShowTarget = 0;
                }
                else if(!_stricmp(tempPlacement, "CenterRight"))
                {
                        SlitY = (ScreenHeight - SlitHeight) / 2;
                        SlitX = ScreenWidth - SlitWidth;
                        SlitAutoHideTarget = ScreenWidth - 2;
                        SlitAutoShowTarget = ScreenWidth - SlitWidth;
                }
        }

        // ...then the top positions...
        else if (tempPlacement[0] == 'T') // || tempPlacement[0] == 't')
        {
                if (!stricmp(tempPlacement, "TopLeft"))
                {
                        SlitX = 0;
                        SlitY = 0;
                        SlitAutoHideTarget = 1 - SlitHeight;
                        SlitAutoShowTarget = 0;
                }
                else if (!stricmp(tempPlacement, "TopCenter"))
                {
                        SlitX = (ScreenWidth - SlitWidth) / 2;
                        SlitY = 0;
                        SlitAutoHideTarget = 1 - SlitHeight;
                        SlitAutoShowTarget = 0;
                }
                else if (!_stricmp(tempPlacement, "TopRight"))
                {
                        SlitX = ScreenWidth - SlitWidth;
                        SlitY = 0;
                        SlitAutoHideTarget = 1 - SlitHeight;
                        SlitAutoShowTarget = 0;
                }
        }

        // ...and finally the bottom positions...
        else if (tempPlacement[0] == 'B') // || tempPlacement[0] == 'b')
        {
                if (!_stricmp(tempPlacement, "BottomLeft"))
                {
                        SlitX = 0;
                        SlitY = ScreenHeight - SlitHeight;
                        SlitAutoHideTarget = ScreenHeight - 1;
                        SlitAutoShowTarget = ScreenHeight - SlitHeight;
                }
                else if (!_stricmp(tempPlacement, "BottomCenter"))
                {
                        SlitX = (ScreenWidth - SlitWidth) / 2;
                        SlitY = ScreenHeight - SlitHeight;
                        SlitAutoHideTarget = ScreenHeight - 1;
                        SlitAutoShowTarget = ScreenHeight - SlitHeight;
                }
                else // (!_stricmp(tempPlacement, "BottomRight"))
                {
                        SlitX = ScreenWidth - SlitWidth;
                        SlitY = ScreenHeight - SlitHeight;
                        SlitAutoHideTarget = ScreenHeight - 1;
                        SlitAutoShowTarget = ScreenHeight - SlitHeight;
                }
        }
}

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

void Slit::UpdatePosition()
{
        // Disable autohide during the update...
        if (SlitAutoHideEnabled)
        {
                SlitAutoHideEnabled = false;
                KillTimer(hSlitWnd, SLIT_AUTOHIDE_TIMER);
                KillTimer(hSlitWnd, SLIT_MOUSEHOVER_TIMER);
        }

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

        // Fetch new settings and update window position/z-order...
        GetSettings();

        // Resize the window and move it to its new position...
        MoveWindow(hSlitWnd, SlitX, SlitY, SlitWidth, SlitHeight, true);
        // Force re-rendering of the background bitmap...
        cachedBackgroundExists = false;
        InvalidateRect(hSlitWnd, NULL, false);

        if (pSettings->slitOnTop) SetWindowPos(hSlitWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
        else SetWindowPos(hSlitWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
        SetTransparency(hSlitWnd, pSettings->slitTransparencyAlpha);

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

        SlitAutoHiding = SlitAutoShowing = false;
        SlitAutoHidden = false;
        SlitAutoShown = true;

        // Disable autohide if we're using manual positioning...
        if (pSettings->slitPlacement[0] == 'M') SlitAutoHideEnabled = false;
        // ...otherwise use the config setting found in blackbox.rc...
        else SlitAutoHideEnabled = pSettings->slitAutoHide;

        if (SlitAutoHideEnabled)
        {
                pSlit->SlitPosInProgress = false; // We need to do this to prevent manual positioning support from blocking the autohide...

                if (!CheckIfMouseHover()) AutoHide();
                else if (!SetTimer(hSlitWnd, SLIT_MOUSEHOVER_TIMER, 100, (TIMERPROC)NULL))
                {
                        MessageBox(GetBBWnd(), "Error creating mousehover timer", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                        Log("Could not create Slit mousehover timer", NULL);
                }
        }

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

        // Save placement and direction settings to blackbox.rc...
        WriteString(bbrcPath(), "session.screen0.slit.placement:", pSettings->slitPlacement);
        WriteString(bbrcPath(), "session.screen0.slit.direction:", pSettings->slitDirection);
}

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

void Slit::UpdatePluginPositions()
{
        if (reconfigureLock) return; // Is the reconfigure lock enabled?

        if (SlitPlugins.size() > 0)
        {
                RECT r = {0, 0, 0, 0};
                int maxwidth = 0, maxheight = 0;

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

                // Scan through the plugin list and calculate
                // the maximum plugin width and height...
                for (plugin = SlitPlugins.begin(); plugin != SlitPlugins.end(); plugin++)
                {
                        if (IsWindow(plugin->hwndPlugin)) // Check if the window is valid...
                        {
                                GetWindowRect(plugin->hwndPlugin, &r);

                                plugin->width = r.right - r.left;
                                plugin->height = r.bottom - r.top;

                                if (plugin->width > maxwidth) maxwidth = plugin->width;
                                if (plugin->height > maxheight) maxheight = plugin->height;
                        }
                        else // ...if not, we remove it from the list...
                        {
                                SlitPlugins.erase(plugin);
                                plugin--;
                        }
                }

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

                int padding = 2;
                int xpad, ypad;
                int offset = pSettings->Slit->borderWidth + padding + 1;
                int tempX = offset;
                int tempY = offset;
                int totalGroupWidth = 0, totalGroupHeight = 0;
                int maxwidthWithPadding = maxwidth + padding;
                int maxheightWithPadding = maxheight + padding;
                int maxwidthInGroup = 0, maxheightInGroup = 0;

                bool usePuzzlePositioning;
                if (!stricmp(pSettings->slitPositioning, "Puzzle")) usePuzzlePositioning = true;
                else usePuzzlePositioning = false;

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

                // "Puzzle positioning" == Automatic positioning of plugins within the slit...
                if (usePuzzlePositioning)
                {
                        firstInGroup = lastInGroup = SlitPlugins.begin();

                        for (plugin = SlitPlugins.begin(); plugin != SlitPlugins.end(); plugin++)
                        {
                                if (SlitVertical) // Vertical slit alignment
                                {
                                        totalGroupWidth = tempX + plugin->width + padding;

                                        if (totalGroupWidth > maxwidthWithPadding)
                                        {
                                                if (firstInGroup->width != maxwidth)
                                                {
                                                        xpad = ((maxwidth - tempX) / 2) + pSettings->Slit->borderWidth + padding;

                                                        for (member = firstInGroup; member <= lastInGroup; member++)
                                                        {
                                                                if (member->height == maxheightInGroup) ypad = 0;
                                                                else ypad = (maxheightInGroup - member->height) / 2;

                                                                member->x += xpad;
                                                                member->y += ypad;
                                                        }
                                                }

                                                tempX = offset;
                                                if (plugin != SlitPlugins.begin()) tempY = tempY + maxheightInGroup + padding;
                                                totalGroupWidth = offset + plugin->width + padding;
                                                totalGroupHeight = offset + plugin->height + padding;

                                                maxwidthInGroup = maxheightInGroup = 0;
                                                firstInGroup = plugin;
                                        }
                                }
                                else // Horizontal slit alignment
                                {
                                        totalGroupHeight = tempY + plugin->height + padding;

                                        if (totalGroupHeight > maxheightWithPadding)
                                        {
                                                if (firstInGroup->height != maxheight)
                                                {
                                                        ypad = ((maxheight - tempY) / 2) + pSettings->Slit->borderWidth + padding;

                                                        for (member = firstInGroup; member <= lastInGroup; member++)
                                                        {
                                                                if (member->width == maxwidthInGroup) xpad = 0;
                                                                else xpad = (maxwidthInGroup - member->width) / 2;

                                                                member->x += xpad;
                                                                member->y += ypad;
                                                        }
                                                }

                                                if (plugin != SlitPlugins.begin()) tempX = tempX + maxwidthInGroup + padding;
                                                tempY = offset;
                                                totalGroupWidth = offset + plugin->width + padding;
                                                totalGroupHeight = offset + plugin->height + padding;

                                                maxwidthInGroup = maxheightInGroup = 0;
                                                firstInGroup = plugin;
                                        }
                                }

                                plugin->x = tempX;
                                plugin->y = tempY;
                                lastInGroup = plugin;
                                if (plugin->width > maxwidthInGroup) maxwidthInGroup = plugin->width;
                                if (plugin->height > maxheightInGroup) maxheightInGroup = plugin->height;

                                if (SlitVertical) tempX = tempX + plugin->width + padding;
                                else tempY = tempY + plugin->height + padding;
                        }

                        // Finish alignment for the last group...
                        if (SlitVertical)
                        {
                                xpad = (maxwidth - tempX) / 2 + pSettings->Slit->borderWidth + padding;

                                for (member = firstInGroup; member <= lastInGroup; member++)
                                {
                                        if (member->height == maxheightInGroup) ypad = 0;
                                        else ypad = (maxheightInGroup - member->height) / 2;

                                        if (member->width != maxwidth) member->x += xpad;
                                        member->y += ypad;
                                }
                        }
                        else
                        {
                                ypad = (maxheight - tempY) / 2 + pSettings->Slit->borderWidth + padding;

                                for (member = firstInGroup; member <= lastInGroup; member++)
                                {
                                        if (member->width == maxwidthInGroup) xpad = 0;
                                        else xpad = (maxwidthInGroup - member->width) / 2;

                                        member->x += xpad;
                                        if (member->height != maxheight) member->y += ypad;
                                }
                        }

                        // Finally, we re-position the plugins...
                        for (plugin = SlitPlugins.begin(); plugin != SlitPlugins.end(); plugin++)
                        {
                                SetWindowPos(plugin->hwndPlugin, NULL, plugin->x, plugin->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
                        }
                }

                //====================
                        
                // Normal positioning == Plugins are placed in a single horizontal row / vertical column...
                else
                {
                        for (plugin = SlitPlugins.begin(); plugin != SlitPlugins.end(); plugin++)
                        {
                                if (SlitVertical)
                                {
                                        // Calculate necessary padding to get a centered x position...
                                        if (plugin->width == maxwidth) xpad = 0;
                                        else xpad = (maxwidth - plugin->width) / 2;
                                        SetWindowPos(plugin->hwndPlugin, NULL, (tempX + xpad), tempY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
                                        tempY = tempY + plugin->height + padding;
                                }
                                else
                                {
                                        // Calculate necessary padding to get a centered y position...
                                        if (plugin->height == maxheight) ypad = 0;
                                        else ypad = (maxheight - plugin->height) / 2;
                                        SetWindowPos(plugin->hwndPlugin, NULL, tempX, (tempY + ypad), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
                                        tempX = tempX + plugin->width + padding;
                                }
                        }
                }

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

                // Finally, we resize the slit to fit all the plugins...
                if (SlitVertical)
                {
                        SlitWidth = maxwidth + (offset * 2);
                        if (usePuzzlePositioning) SlitHeight = tempY + maxheightInGroup + offset;
                        else SlitHeight = tempY - padding + offset;
                }
                else
                {
                        if (usePuzzlePositioning) SlitWidth = tempX + maxwidthInGroup + offset;
                        else SlitWidth = tempX - padding + offset;
                        SlitHeight = maxheight + (offset * 2);
                }

                UpdatePosition();

                if (!pSettings->slitHidden) ShowWindow(hSlitWnd, SW_SHOWNOACTIVATE);
        }
        else ShowWindow(hSlitWnd, SW_HIDE); // Hide the slit if no plugins are docked to it...
}

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

void Slit::ToggleAlwaysOnTop()
{
        if (!pSettings->slitOnTop)
        {
                pSettings->slitOnTop = true;
                SetWindowPos(hSlitWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
        }
        else
        {
                pSettings->slitOnTop = false;
                SetWindowPos(hSlitWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
        }

        WriteBool(bbrcPath(), "session.screen0.slit.onTop:", pSettings->slitOnTop);
}

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

bool Slit::CheckIfMouseHover()
{
        POINT mousepos;
        GetCursorPos(&mousepos);
        if (mousepos.x >= SlitX && mousepos.x <= (SlitX + SlitWidth))
        {
                if (mousepos.y >= SlitY && mousepos.y <= (SlitY + SlitHeight)) return true;
        }

        return false;
}

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

void Slit::AutoShow()
{
//      if (reconfigureLock) return;

        if (!SlitAutoShowing && !SlitAutoShown)
        {
                if (SlitAutoHiding)
                {
                        KillTimer(hSlitWnd, SLIT_AUTOHIDE_TIMER);
                        SlitAutoHiding = false;
                }

//              if (pSettings->slitPlacement[0] == 'C') SlitAutoCurrentPos = SlitX; // Center Left/Right
//              else SlitAutoCurrentPos = SlitY; // Bottom Left/Center/Right or Top Left/Center/Right

                if (pSettings->slitPlacement[0] == 'C') // Center Left/Right
                {
                        if (IsInString(pSettings->slitPlacement, "Left"))
                        {
                                SlitAutoCurrentPos = 1 - SlitWidth;
                        }
                        else if (IsInString(pSettings->slitPlacement, "Right"))
                        {
                                SlitAutoCurrentPos = ScreenWidth - 1;
                        }
                }
                else if (pSettings->slitPlacement[0] == 'B') // Bottom Left/Center/Right
                {
                        SlitAutoCurrentPos = ScreenHeight - 1;
                }
                else // Top Left/Center/Right
                {
                        SlitAutoCurrentPos = 1 - SlitHeight;
                }

                SlitAutoHidden = false;
                SlitAutoShowing = true;
                SetTransparency(hSlitWnd, pSettings->slitTransparencyAlpha);

                if (!SetTimer(hSlitWnd, SLIT_AUTOHIDE_TIMER, pSettings->autohideSpeed, (TIMERPROC)NULL))
                {
                        MessageBox(GetBBWnd(), "Error creating autohide timer", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                        Log("Could not create Slit autohide timer", NULL);
                }
        }
}

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

void Slit::AutoHide()
{
//      if (reconfigureLock) return;

        if (!SlitAutoHiding && !SlitAutoHidden)
        {
                if (SlitAutoShowing)
                {
                        KillTimer(hSlitWnd, SLIT_AUTOHIDE_TIMER);
                        SlitAutoShowing = false;
                }

                if (pSettings->slitPlacement[0] == 'C') SlitAutoCurrentPos = SlitX; // Center Left/Right
                else SlitAutoCurrentPos = SlitY; // Bottom Left/Center/Right or Top Left/Center/Right

                SlitAutoShown = false;
                SlitAutoHiding = true;

                if (!SetTimer(hSlitWnd, SLIT_AUTOHIDE_TIMER, pSettings->autohideSpeed, (TIMERPROC)NULL))
                {
                        MessageBox(GetBBWnd(), "Error creating autohide timer", szSlitName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                        Log("Could not create Slit autohide timer", NULL);
                }
        }
}

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





syntax highlighting by

w e b c p p
web c plus plus