/*
 ============================================================================
 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 "BBApi.h" 
#include "Workspaces.h" 
#include "Settings.h" 
#include "Toolbar.h" 

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

typedef void (__stdcall *STTWTYPE)(HWND, int);
extern STTWTYPE BBSwitchToThisWindow;

extern Settings *pSettings;
extern Workspaces *pWorkspaces;
extern Toolbar *pToolbar;
extern Systembar *pSystembar;

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

const char szWorkspacesName[] = "Workspaces"; // Window class etc.

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

Workspaces::Workspaces(HINSTANCE hMainInstance)
{
        WNDCLASS wc;

        if (pSettings->usingWin2kXP)
        {
                ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
                ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
        }
        else
        {
                // SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN
                // are not supported under Windows 95 or NT4
                ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
                ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
        }

        currentScreen=0;
        lastScreen = 0;

        flushNames();

        inst = hMainInstance;

        // Register our window class
        ZeroMemory(&wc,sizeof(wc));
        wc.lpfnWndProc = WndProc;                               // our window procedure
        wc.hInstance = inst;            
        wc.lpszClassName = szWorkspacesName;    // our window class name

        if (!RegisterClass(&wc))
        {
                MessageBox(0,"Error registering window class.",szWorkspacesName,MB_OK);
                Log("Could not register Workspaces Class", NULL);
        }

        hWorkWnd = CreateWindowEx(
                WS_EX_TOOLWINDOW,                                               // exstyles 
                szWorkspacesName,                                               // our window class name
                NULL,                                                                   // use description for a window title
                0,
                0, 0,                                                                   // position
                0, 0,                                                                   // width & height of window
                NULL,                                                                   // parent window
                NULL,                                                                   // no menu
                inst,                                                                   // hInstance of DLL
                0);                                                                             // no window creation data

        if (!hWorkWnd)
        {                                                  
                UnregisterClass(szWorkspacesName,inst); // unregister window class
                MessageBox(0,"Error creating window",szWorkspacesName,MB_OK);
                Log("Could not create the BBWorkspace Window", NULL);
                return;
        }

        int msgs[] = { BB_WORKSPACE, BB_RECONFIGURE, BB_SWITCHTON, BB_ACTIVETASK, BB_LISTDESKTOPS, BB_BRINGTOFRONT, 0 };
        SendMessage(GetBBWnd(), BB_REGISTERMESSAGE, (WPARAM)hWorkWnd, (LPARAM)msgs);

        MakeSticky(hWorkWnd);   
}

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

Workspaces::~Workspaces()
{
        GatherWindows();

        int msgs[] = { BB_WORKSPACE, BB_RECONFIGURE, BB_SWITCHTON, BB_ACTIVETASK, BB_LISTDESKTOPS, BB_BRINGTOFRONT, 0 };
        SendMessage(GetBBWnd(), BB_UNREGISTERMESSAGE, (WPARAM)hWorkWnd, (LPARAM)msgs);
        DestroyWindow(hWorkWnd); // delete our window
        UnregisterClass(szWorkspacesName,inst); // unregister window class
        deskNames.clear();
}

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

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

                case BB_RECONFIGURE:
                        pWorkspaces->flushNames();
                break;

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

                case BB_SWITCHTON:
                {
                        int i = (int)wParam;
                        
                        if ((pWorkspaces->currentScreen != i))
                                pWorkspaces->switchToDesktop(i);
                        break;
                }

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

                case BB_ACTIVETASK:
                {
                        // Change workspaces automatically when active task changes (Alt+Tab)
                        if (pSettings->followActive) pWorkspaces->switchToDesktop(pWorkspaces->getDesktop((HWND)wParam));
                }

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

                case BB_LISTDESKTOPS:
                {
                        DesktopInfo deskInfo;
                        for(int i = 0; i < pSettings->workspaces; i++)
                        {
                                deskInfo.isCurrent = (pWorkspaces->currentScreen == i);
                                deskInfo.number = i;

                                if(pWorkspaces->deskNames[i]=="null")
                                        sprintf(deskInfo.name, "Workspace %d", i + 1);
                                else
                                        strcpy(deskInfo.name, pWorkspaces->deskNames[i].c_str());

                                SendMessage((HWND)wParam, BB_DESKTOPINFO, 0, (LPARAM) &deskInfo);
                        }
                        break;
                }

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

                case BB_BRINGTOFRONT: 
                {
                        // Change window focus via message (change desktop if needed)
                        int n = pWorkspaces->getDesktop((HWND)lParam);
                        if (n != pWorkspaces->currentScreen) 
                                pWorkspaces->switchToDesktop(n);
                        BBSwitchToThisWindow((HWND)lParam, 1);
                        break;
                }

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

                case BB_WORKSPACE:
                {
                        switch (wParam)
                        {
                                case 0:
                                        pWorkspaces->DeskLeft();
                                break;
                                case 1:
                                        pWorkspaces->DeskRight();
                                break;
                                case 2:
                                        pWorkspaces->AddDesktop();
                                break;
                                case 3:
                                        pWorkspaces->DelDesktop();
                                break;
                                case 4:
                                        pWorkspaces->DeskSwitch((int)lParam);
                                break;
                                case 5:
                                        pWorkspaces->GatherWindows();
                                break;
                                case 6:
                                        pWorkspaces->MoveWindowToPrev((HWND)lParam);
                                break;
                                case 7:
                                        pWorkspaces->MoveWindowToNext((HWND)lParam);
                                break;
                                case 8:
                                        pWorkspaces->PrevWindow((int)lParam);
                                break;
                                case 9:
                                        pWorkspaces->NextWindow((int)lParam);
                                break;
                                case 10:
                                        pWorkspaces->LastDesk();
                                break;
                                //====================
                                default:
                                        pWorkspaces->DeskLeft();
                        }
                }
                break;

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

                case WM_DISPLAYCHANGE:
                {
                        if (pSettings->usingWin2kXP)
                        {
                                pWorkspaces->ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
                                pWorkspaces->ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
                        }
                        else
                        {
                                pWorkspaces->ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
                                pWorkspaces->ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
                        }
                }

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

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

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

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

const long magicDWord = 0x49474541;

BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam )
{
        int width;
        int addH = (INT)lParam;
        int ScreenWidth = pWorkspaces->ScreenWidth;
        RECT r;

        if (!(GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE))
                return true;
        if (pWorkspaces->CheckWindow(hwnd))
                return true;
        if (CheckSticky(hwnd))
                return true;
        if (GetWindowLong(hwnd, GWLP_USERDATA) == magicDWord)   // for backwards compatibility only
                return true;

        GetWindowRect(hwnd, &r);

        if (addH == -1)
        {
                width = abs(r.right - r.left);

                // Initial x/width position calculation
                if (r.left > ScreenWidth)
                {
                        r.left %= (ScreenWidth + 10);
                        if (r.left > ScreenWidth)
                                r.left -= (ScreenWidth + 10);
                        r.right = r.left + width;
                }
                else if (r.left < -10)
                {
                        r.left %= (ScreenWidth + 10);
                        while (r.left < -10)
                                r.left += (ScreenWidth + 10);;
                        r.right = r.left + width;
                }
        }
        else
        {
                r.left-=addH;
                r.right-=addH;
        }

        pWorkspaces->dwp = DeferWindowPos(pWorkspaces->dwp, hwnd, NULL, r.left, r.top, r.right-r.left, r.bottom-r.top, SWP_NOZORDER|SWP_NOACTIVATE);

        return true;
}

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

bool Workspaces::CheckWindow(HWND window)
{
        char appName[MAX_PATH], entryName[MAX_PATH];
//      strcpy(appName, GetAppByWindow(window)); // GetAppByWindow is not supported in the xoblite version of the API, using GetWindowText instead...
        GetWindowText(window, appName, MAX_PATH);
        strlwr(appName);

        for (int i = 0; i < (int)stickyList.size(); i++)
        {
                strcpy(entryName, stickyList[i].c_str());
                strlwr(entryName);

                if (!strcmp(appName, entryName))
                        return true;
        }

        return CheckSticky(window);
}

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

void Workspaces::switchToDesktop(int vw)
{
        if (vw != currentScreen && vw >= 0 && vw < pSettings->workspaces)
        {
                INT addH = ((vw % pSettings->workspaces) - (currentScreen % pSettings->workspaces)) * (ScreenWidth + 10);

                dwp = BeginDeferWindowPos(1);
                EnumWindows(EnumWindowsProc, (LPARAM)addH);
                EndDeferWindowPos(dwp);

                lastScreen = currentScreen;
                currentScreen = vw;
        }

        if (!pSettings->accessLock)
        {
                // Tell the toolbar+systembar that we have switched workspace...
                if (pToolbar) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);
                if (pSystembar && pSettings->taskbarCurrentOnly) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
        }
}

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

void Workspaces::GatherWindows()
{
        dwp = BeginDeferWindowPos(1);
        EnumWindows(EnumWindowsProc, (LPARAM)-1);
        EndDeferWindowPos(dwp);

        if (!pSettings->accessLock)
        {
                if (pSystembar && pSettings->taskbarCurrentOnly) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
        }
}

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

int Workspaces::getDesktop(HWND h)
{
        if (!h || IsIconic(h)) return 255;
        RECT r;
        GetWindowRect(h, &r);
        return (getDesktopByRect(r));
}

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

int Workspaces::getDesktopByRect(RECT r)
{
        int offsetx = currentScreen % pSettings->workspaces;
        int offsety = currentScreen / pSettings->workspaces;
        int desk;
        RECT tmpRect, desktopRect;

        desktopRect.left = desktopRect.top = 0;
        desktopRect.right = ScreenWidth;
        desktopRect.bottom = ScreenHeight;

        if (IntersectRect(&tmpRect, &r, &desktopRect))
                return currentScreen;

        r.left += offsetx * (ScreenWidth + 10);
        r.top += offsety * (ScreenHeight + 10);

        offsetx = ((r.left + 10) / (ScreenWidth + 10));
        offsety = ((r.top + 10) / (ScreenHeight + 10));

        desk = (offsety * pSettings->workspaces) + offsetx;

        if (desk < 0 || desk > pSettings->workspaces)
                desk = currentScreen;

        return desk;
}

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

void Workspaces::AddDesktop()
{
        // 254 is used as a wildcard to e.g. list tasks on all workspaces
        // 255 is used for minimized tasks
        if (pSettings->workspaces == 253) return;

        pSettings->workspaces++;
        flushNames();

        WriteInt(bbrcPath(), "session.screen0.workspaces:", pSettings->workspaces);
}

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

void Workspaces::DelDesktop()
{
        if (pSettings->workspaces != 1)
        {
                if (currentScreen == pSettings->workspaces - 1)
                        switchToDesktop(currentScreen-1);
                pSettings->workspaces--;
                flushNames();
        }

        WriteInt(bbrcPath(), "session.screen0.workspaces:", pSettings->workspaces);
}

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

void Workspaces::DeskLeft()
{
        int vwm = (currentScreen > 0) ? (currentScreen - 1) : (pSettings->workspaces - 1);
        switchToDesktop(vwm);
}

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

void Workspaces::DeskRight()
{
        int vwm = (currentScreen < pSettings->workspaces - 1) ? (currentScreen + 1) : 0;
        switchToDesktop(vwm);
}

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

void Workspaces::LastDesk()
{       
        switchToDesktop(lastScreen);
}

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

void Workspaces::DeskSwitch(int i)
{       
        switchToDesktop(i);
}

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

void Workspaces::MoveWindowToSpecified(HWND window, int workspace)
{
        if (CheckSticky(window)) return;

        switchToDesktop(getDesktop(window));
        BBSwitchToThisWindow(window, true);

        MakeSticky(window);
        switchToDesktop(workspace);
        RemoveSticky(window);

        if (!pSettings->accessLock)
        {
                if (!pSettings->taskbarOnToolbar && pSystembar && pSettings->taskbarCurrentOnly) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                else if (pToolbar && pSettings->taskbarCurrentOnly) InvalidateRect(pToolbar->hToolbarWnd, NULL, false);
        }
}

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

void Workspaces::MoveWindowToPrev(HWND window)
{
        if (currentScreen == 0) MoveWindowToSpecified(window, pSettings->workspaces);
        else MoveWindowToSpecified(window, currentScreen-1);
}

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

void Workspaces::MoveWindowToNext(HWND window)
{
        if (currentScreen == pSettings->workspaces) MoveWindowToSpecified(window, 0);
        else MoveWindowToSpecified(window, currentScreen+1);
}

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

void Workspaces::PrevWindow(int allDesktops)
{
        // First we fetch the bottom window...
        HWND BBWnd = GetBBWnd();
        HWND hTempWnd = GetWindow(BBWnd, GW_HWNDLAST);
        HWND hTempWndLast = GetWindow(BBWnd, GW_HWNDFIRST);
        HWND hProgManWnd = FindWindow("Progman", "Program Manager");
        if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDPREV);

        while (!(IsWindowVisible(hTempWnd)) | IsIconic(hTempWnd) | !(GetWindowTextLength(hTempWnd)))
        {
                hTempWnd = GetWindow(hTempWnd, GW_HWNDPREV);

                if (allDesktops == 0)
                {
                        while (getDesktop(hTempWnd) != currentScreen)
                        {
                                hTempWnd = GetWindow(hTempWnd, GW_HWNDPREV);
                                if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDPREV);
                        }
                }
                else if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDPREV);

                if (hTempWnd == hTempWndLast) break;
        }

        // ...then we make it the new top window and active
        if (IsWindow(hTempWnd))
        {
                SetWindowPos(hTempWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 
                // Change desktop if needed...
                SendMessage(BBWnd, BB_BRINGTOFRONT, 0, (LPARAM)hTempWnd);
        }
}

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

void Workspaces::NextWindow(int allDesktops)
{
        // First we fetch the top window...
        HWND BBWnd = GetBBWnd();
        HWND hTempWnd = GetWindow(BBWnd, GW_HWNDFIRST);
        HWND hProgManWnd = FindWindow("Progman", "Program Manager");
        if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDNEXT);

        while (!(IsWindowVisible(hTempWnd)) | IsIconic(hTempWnd) | !(GetWindowTextLength(hTempWnd)))
        {
                hTempWnd = GetWindow(hTempWnd, GW_HWNDNEXT);

                if (hTempWnd == NULL) break;

                if (allDesktops == 0)
                {
                        while (getDesktop(hTempWnd) != currentScreen)
                        {
                                hTempWnd = GetWindow(hTempWnd, GW_HWNDNEXT);
                                if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDNEXT);
                                if (hTempWnd == NULL) break;
                        }
                }
                else if (hTempWnd == hProgManWnd) hTempWnd = GetWindow(hTempWnd, GW_HWNDNEXT);
        }

        if (IsWindow(hTempWnd))
        {
                // ...then we fetch the bottom window, and put the top behind that...
                HWND hTempWndBottom = GetWindow(BBWnd, GW_HWNDLAST);
                if (hTempWndBottom == hProgManWnd) hTempWndBottom = GetWindow(hTempWndBottom, GW_HWNDPREV);

                while (!(IsWindowVisible(hTempWndBottom)) | IsIconic(hTempWndBottom) | !(GetWindowTextLength(hTempWndBottom)))
                {
                        hTempWndBottom = GetWindow(hTempWndBottom, GW_HWNDPREV);

                        if (hTempWndBottom == NULL) break;

                        if (allDesktops == 0)
                        {
                                while (getDesktop(hTempWndBottom) != currentScreen)
                                {
                                        hTempWndBottom = GetWindow(hTempWndBottom, GW_HWNDPREV);
                                        if (hTempWndBottom == hProgManWnd) hTempWndBottom = GetWindow(hTempWndBottom, GW_HWNDPREV);
                                        if (hTempWndBottom == NULL) break;
                                }
                        }
                        else
                        {
                                if (hTempWndBottom == hProgManWnd) hTempWndBottom = GetWindow(hTempWndBottom, GW_HWNDPREV);
                        }
                }

                // .. then we get the next highest window...
                HWND hTempWndNext = GetWindow(hTempWnd, GW_HWNDNEXT);
                if (hTempWndNext == hProgManWnd) hTempWndNext = GetWindow(hTempWndNext, GW_HWNDNEXT);

                while (!(IsWindowVisible(hTempWndNext)) | IsIconic(hTempWndNext) | !(GetWindowTextLength(hTempWndNext)))
                {
                        hTempWndNext = GetWindow(hTempWndNext, GW_HWNDNEXT);

                        if (hTempWndNext == NULL) break;

                        if (allDesktops == 0)
                        {
                                while (getDesktop(hTempWndNext) != currentScreen)
                                {
                                        hTempWndNext = GetWindow(hTempWndNext, GW_HWNDNEXT);
                                        if (hTempWndNext == hProgManWnd) hTempWndNext = GetWindow(hTempWndNext, GW_HWNDNEXT);
                                        if (hTempWndNext == NULL) break;
//                                      if (hTempWndNext == hTempWndBottom) break;
                                }
                        }
                        else if (hTempWndNext == hProgManWnd) hTempWndNext = GetWindow(hTempWndNext, GW_HWNDNEXT);

//                      if (hTempWndNext == hTempWndBottom)
//                      {
//                              hTempWndNext = NULL;
//                              break;
//                      }
                }

                if (IsWindow(hTempWnd) && IsWindow(hTempWndBottom))
                {
                        SetWindowPos(hTempWnd, hTempWndBottom, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING);

                        if (IsWindow(hTempWndNext))
                        {
                                //... and make it active...
                                SetWindowPos(hTempWndNext, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                                SendMessage(BBWnd, BB_BRINGTOFRONT, 0, (LPARAM)hTempWndNext);
                        }
                }
        }
}

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

void Workspaces::flushNames()
{
        deskNames.clear();
        char buffer[MAX_LINE_LENGTH], szBuffer[MAX_PATH];
        strcpy(buffer, pSettings->workspaceNames);
        for (int i = 0; i < pSettings->workspaces; i++)
        {
                strcpy(buffer, Tokenize(buffer, szBuffer, ","));
                if (!strcmp(szBuffer,"")) strcpy(szBuffer, "null");
                deskNames.push_back(string(szBuffer));
        }
}

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





syntax highlighting by

w e b c p p
web c plus plus