/*
 ============================================================================
 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 "MenuMaker.h" 
#include "Menu.h" 
#include "MenuItem.h" 
#include "Painter.h" 
#include "../BBApi.h" 
#include "../Settings.h" 

#include <algorithm> 

#ifndef GET_X_LPARAM 
#define GET_X_LPARAM(lp)   ((int)(short)LOWORD(lp)) 
#endif 

#ifndef GET_Y_LPARAM 
#define GET_Y_LPARAM(lp)   ((int)(short)HIWORD(lp)) 
#endif 

#define TRACK_MOUSE_TIMEOUT 4437 

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

const char menuName[] = "BBMenu";

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

vector<Menu*> g_Menues;

int Menu::m_nInstances = 0;

extern BImage *pBImage;
extern Settings *pSettings;
extern MenuMaker *pMenuMaker;

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

Menu::Menu(HINSTANCE hInstance)
{
        m_bValidated = false;
        m_pParent = NULL;
        m_hInstance = hInstance;
        m_hWnd = NULL;
        cachedMenuFrame = 0;
        cachedMenuFrameExists = false;

        if (m_nInstances == 0)          
        {       
                WNDCLASS wc;
                ZeroMemory(&wc, sizeof(wc));
                wc.lpfnWndProc = MenuWindowProc;
                wc.hInstance = m_hInstance;                     
                wc.lpszClassName = menuName;
                wc.hCursor = LoadCursor(NULL, IDC_ARROW);

                if (!RegisterClass(&wc))
                {
                        MessageBox(0, "Error registering menu window class", menuName, MB_OK);
                        Log("Error registering menu window class", NULL);
                        return;
                }
        }

        g_Menues.push_back(this);

        m_hWnd =  CreateWindowEx(
                WS_EX_TOOLWINDOW | WS_EX_TOPMOST,                                       // exstyles
                menuName,                                                                                       // our window class name
                NULL,                                                                                           // use description for a window title
                WS_POPUP,
                0, 0, 0, 0,                                                                                     // position + width & height of window
                GetBBWnd(),                                                                                     // parent window
                NULL,                                                                                           // no menu
                m_hInstance,                                                                            // hInstance
                NULL);

        if (!m_hWnd)
        {
                MessageBox(0, "Error creating menu window", menuName, MB_OK);
                Log("Error creating menu window", NULL);
                return;
        }

        MakeSticky(m_hWnd);
        m_nInstances++;
        m_bPinned = false;
        m_bMoved = false;
        m_bMouseOver = false;

        // Apply transparency depending on extensions.rc setting...
        SetTransparency(m_hWnd, pSettings->menuTransparencyAlpha);

        HANDLE hFind = FindFirstFile(pSettings->menuFile, &data);
        FindClose(hFind);
}

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

Menu::~Menu()
{
        // Remove this object from the global window-object map...
        for (unsigned int i=0; i<g_Menues.size(); i++)
        {
                if (g_Menues[i] == this)
                {       
                        g_Menues.erase(g_Menues.begin()+i);
                        break;
                }
        }

        // Destroy the window...
        if (m_hWnd) DestroyWindow(m_hWnd);
        m_hWnd = NULL;

        // Delete the cached menu.frame gradient...
        if (cachedMenuFrame) DeleteDC(cachedMenuFrame);

        DeleteMenuItems();

        // Decrease the count of popupmenu objects...
        m_nInstances--;
        if (m_nInstances == 0)
        {
                // This is the last instance of the menu so we can 
                // unregister the window class...
                UnregisterClass(menuName, m_hInstance);
        }
}

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

void Menu::Paint()
{
        PAINTSTRUCT ps;
        BeginPaint(m_hWnd, &ps);

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

        RECT menuFrameRect;
        GetClientRect(m_hWnd, &menuFrameRect);
        menuFrameRect.top = pMenuMaker->m_nTitleHeight;
        menuFrameRect.left = pMenuMaker->m_nBorderWidth;
        menuFrameRect.right = menuFrameRect.right - pMenuMaker->m_nBorderWidth;
        menuFrameRect.bottom = menuFrameRect.bottom - pMenuMaker->m_nBorderWidth;
        int menuFrameWidth = menuFrameRect.right - menuFrameRect.left;
        int menuFrameHeight = menuFrameRect.bottom - menuFrameRect.top;

        // If we have not yet created a cached menu.frame gradient, let's do that...
        if (!cachedMenuFrameExists)
        {
                // Delete the old cache DC and create a new one...
                if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
                cachedMenuFrame = CreateCompatibleDC(NULL);

                // Create a temporary bitmap...
                RECT r = {0, 0, menuFrameWidth, menuFrameHeight};
                HBITMAP tempBitmap = CreateCompatibleBitmap(ps.hdc, menuFrameWidth, menuFrameHeight);

                // Delete the previously cached gradient...
                HBITMAP oldBitmap = (HBITMAP)SelectObject(cachedMenuFrame, tempBitmap);
                DeleteObject(oldBitmap);

                // Paint gradient...
                pBImage->CreateGradientByRect(cachedMenuFrame,
                        r,
                        pSettings->MenuFrame->type,
                        pSettings->MenuFrame->Color,
                        pSettings->MenuFrame->ColorTo,
                        pSettings->MenuFrame->interlaced,
                        pSettings->MenuFrame->bevelstyle,
                        pSettings->MenuFrame->bevelposition,
                        pSettings->bevelWidth);

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

                // Set the "cached menu.frame created" indicator...
                cachedMenuFrameExists = true;
        }

        // Copy the cached background into the display buffer...
        BitBlt(ps.hdc, menuFrameRect.left, menuFrameRect.top, menuFrameWidth, menuFrameHeight, cachedMenuFrame, 0, 0, SRCCOPY);

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

        RECT itemRect;
        RECT tempRect;
        MENUITERATOR i; 

        for (i=m_MenuItems.begin(); i != m_MenuItems.end(); i++)
        {
                (*i)->GetItemRect(&itemRect);

                // Only call paint if the item is within the update region...
                if (IntersectRect(&tempRect, &ps.rcPaint, &itemRect))
                        (*i)->Paint(ps.hdc);
        }

        if (pMenuMaker->m_nBorderWidth) PaintBorder(ps.hdc);

        EndPaint(m_hWnd, &ps);
}

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

void Menu::PaintBorder(HDC hDC)
{
        if (!pMenuMaker->m_nBorderWidth) return;

        RECT r;

        // Get the menu dimensions (title + body)...
        GetClientRect(m_hWnd, &r);

        // Paint a border around the entire menu...
        pBImage->CreateBorder(hDC, r, pMenuMaker->m_nBorderColor, pMenuMaker->m_nBorderWidth);

        // Get the total height of the menu title (title + two borders)...
        r.bottom = pMenuMaker->m_nTitleHeight;

        // Paint a border around the menu title...
        pBImage->CreateBorder(hDC, r, pMenuMaker->m_nBorderColor, pMenuMaker->m_nBorderWidth);
}

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

void Menu::Show(int x, int y)
{
        OnShow(true);

        RECT r;
        GetWindowRect(GetWindow(), &r);

        int height = r.bottom - r.top;
        int width = r.right - r.left;

        //====================
/*
        int screenHeight = GetSystemMetrics(SM_CYSCREEN);
        int screenWidth = GetSystemMetrics(SM_CXSCREEN);

        // Make sure the menu is on the screen...
        if (y + height > screenHeight)
                y = screenHeight - height;
        if ((x + width) > screenWidth)
                x = screenWidth - width;

        if (x < 0) x = 0;
        if (y < 0) y = 0;
*/
        POINT pt = {x ,y};

        HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);

        MONITORINFO mi;
        mi.cbSize = sizeof(mi);

        if (GetMonitorInfo(hMon, &mi))
        {
                r.left = mi.rcMonitor.left;
                r.top = mi.rcMonitor.top;
                r.right = mi.rcMonitor.right;
                r.bottom = mi.rcMonitor.bottom;
        }
        else
        {
                r.left = 0;
                r.top = 0;

                if (pSettings->usingWin2kXP)
                {
                        r.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
                        r.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
                }
                else
                {
                        r.right = GetSystemMetrics(SM_CXSCREEN);
                        r.bottom = GetSystemMetrics(SM_CYSCREEN);
                }
        }

        if (y + height > r.bottom)
                y = r.bottom - height;

        if (x + width > r.right)
                x = r.right - width;

        if (x < r.left)
                x = r.left;

        if (y < r.top)
                y = r.top;

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

        SetWindowPos(m_hWnd, NULL, x, y, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSIZE);
}

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

void Menu::Show()
{
        POINT p;
        GetCursorPos(&p);

        if (m_pParent != NULL)
        {
                RECT r;
                GetWindowRect(m_pParent->GetWindow(), &r);
                int width = r.right - r.left;
                p.x -= width/2;
                p.y -= pMenuMaker->m_nTitleHeight / 2;
                Show(r.right - 5, p.y - 5);
        }
        else
        {
                RECT r;
                GetWindowRect(GetWindow(), &r);
                int width = r.right - r.left;
                p.x -= width/2;
                p.y -= pMenuMaker->m_nTitleHeight / 2;
                Show(p.x, p.y);
        }
}

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

HWND Menu::GetWindow()
{
        return m_hWnd;
}

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

void Menu::Mouse(UINT nMsg, int x, int y)
{       
        MENUITERATOR item;

        // By checking for the empty list we keep BB from crashing if a message box is
        // opened, BB reconfigured, and the message box closed... / Tres`ni
        for (item = m_MenuItems.begin(); item != m_MenuItems.end() && !m_MenuItems.empty(); item++)
        {
                (*item)->Mouse(nMsg, x, y);
        }

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

        if (nMsg == WM_MOUSEMOVE)
        {
                RECT r;
                POINT point = {x, y};
                m_bMoved = false;
                GetWindowRect(m_hWnd, &r);
                ClientToScreen(m_hWnd, &point);

                int screenTop;
                int screenHeight;

                if (pSettings->usingWin2kXP)
                {
                        screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
                        screenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
                }
                else
                {
                        screenHeight = GetSystemMetrics(SM_CYSCREEN);
                        screenTop = 0;
                }

                if ((r.bottom > screenHeight) && (point.y + 1 >= screenHeight))
                {
                        // The popup is larger than the screen and the
                        // cursor is at the bottom -> scroll down a bit...
                        r.top -= pSettings->scrollSpeed;
                        m_bMoved = true;
                }
                else if ((r.top < screenTop) && (point.y <= screenTop))
                {
                        // The popup is larger than the screen and the
                        // cursor is at the top -> scroll up at bit...
                        r.top += pSettings->scrollSpeed;
                        m_bMoved = true;
                }

                if (m_bMoved)
                {
                        SetWindowPos(m_hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING);
                        Paint();
                }

                // Track mouse ..
                if (!m_bMouseOver)
                {
                        m_bMouseOver = true;
                        SetTimer(m_hWnd, TRACK_MOUSE_TIMEOUT, 100, Menu::TrackMouseProc);
                }
        }
        
        //====================

        else if (nMsg == WM_MOUSEWHEEL)
        {       
                RECT r;
                GetWindowRect(m_hWnd, &r);

                // Only allow mousewheel scrolling if the menu is taller than the screen...
                int height = r.bottom - r.top;
                int screenHeight;
                if (pSettings->usingWin2kXP) screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
                else screenHeight = GetSystemMetrics(SM_CYSCREEN);
                if (height < screenHeight) return;

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

                if (x > 0) // Scrolling upwards...
                {
                        r.top += pSettings->wheelSpeed;
                        r.bottom = r.top + height;

                        if (r.top > 0)
                        {
                                r.top = 0;
                                r.bottom = height;
                        }
                }
                else // Scrolling downwards...
                {
                        r.top -= pSettings->wheelSpeed;
                        r.bottom = r.top + height;

                        if (r.bottom < screenHeight)
                        {
                                r.top = screenHeight - height;
                                r.bottom = screenHeight;
                        }
                }

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

                SetWindowPos(m_hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING);
                Paint();
        }
}

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

//void Menu::TrackMouseProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
void Menu::TrackMouseProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
        POINT pt;
        HWND hWndNew;

        GetCursorPos(&pt);
        hWndNew = WindowFromPoint(pt);

        if (hWnd != GetForegroundWindow()) SetForegroundWindow(hWnd);

        if (::GetParent(hWndNew) != hWnd)
        {
                RECT rect;
                        
                GetClientRect(hWnd, &rect);
                MapWindowPoints(hWnd, NULL, (LPPOINT)&rect,2);
                
                if (!PtInRect(&rect, pt) || (hWndNew != hWnd)) 
                {
                        KillTimer(hWnd, idEvent);
                        PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
                }
        }
}

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

void Menu::MouseLeave()
{
        POINT p;
        HWND hWnd;
        MENUITERATOR notUsed;

        GetCursorPos(&p);
        hWnd = WindowFromPoint(p);

        if (!IsChildWindow(hWnd)) FindActiveAndDeactivate(&notUsed);

        m_bMouseOver = false;
}

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

void Menu::AddMenuItem(MenuItem* m)
{
        m_MenuItems.push_back(m);

        // Notify the menu item that it has been attached...
        m->Attached(this);
}

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

void Menu::AddMenuItem(MenuItem *m, bool (*pfnSort)(void *, MenuItem *, MenuItem *), void *pvSortParam)
{
        MENUITERATOR i;

        for (i=m_MenuItems.begin(); i != m_MenuItems.end(); i++)
        {
                if (!pfnSort(pvSortParam, m, *i))
                {
                        m_MenuItems.insert(i, m);
                        break;
                }
        }

        if (i == m_MenuItems.end()) m_MenuItems.push_back(m);

        m->Attached(this);
}

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

void Menu::DeleteMenuItems()
{       
        MENUITERATOR item;

        // Remove the menu from the set of global menues...
        for (item=m_MenuItems.begin(); item != m_MenuItems.end(); ++item)
                delete *item;
        m_MenuItems.clear();
}

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

void Menu::Activate(int fActive, HWND hWnd)
{
        // Nothing to do here... :)
}

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

bool Menu::IsChildWindow(HWND hWnd)
{
        for (unsigned int i=0; i < m_Children.size(); i++)
        {
                if (hWnd == m_Children[i]->m_hWnd)
                        return true;
                else if (m_Children[i]->IsChildWindow(hWnd))
                        return true;
        }

        return false;
}

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

bool Menu::IsActive()
{
        return GetActiveWindow() == m_hWnd;// || bRet;
}

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

void Menu::Time(int nTimer)
{
        MENUITERATOR item;
        for (item=m_MenuItems.begin(); item !=  m_MenuItems.end(); item++)
                (*item)->Timer(nTimer);

        OnTimer(nTimer);
}

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

void Menu::Hide(int h)
{
        MENUITERATOR item;

        if(!IsPinned())
        {
                if (IsWindowVisible(m_hWnd))
                {
                        ShowWindow(m_hWnd, SW_HIDE);
                        OnShow(false);

                        // Destroy the cached menu.frame gradient to save GDI objects + memory...
                        cachedMenuFrameExists = false;
                        if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
                        cachedMenuFrame = 0;
                }
        }

        for (item=m_MenuItems.begin(); item!=m_MenuItems.end(); item++)
                (*item)->Active(false);

        if (h == HIDE_CHILDREN) 
        {
                for (unsigned int i=0; i<m_Children.size(); i++)
                {
                        if (IsWindowVisible(m_Children[i]->GetWindow()))
                                m_Children[i]->Hide(h);
                }
        }
        else if (h == HIDE_PARENTS)
        {
                if (m_pParent)
                {
                        if (IsWindowVisible(m_pParent->GetWindow()))
                                m_pParent->Hide(h);
                }
        }
}

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

LRESULT Menu::NcHitTest(int x, int y)
{
        LRESULT r;
        MENUITERATOR item;

        for (item=m_MenuItems.begin(); item != m_MenuItems.end(); item++)
        {
                if ((r = (*item)->NcHitTest(x, y)) != 0) return r;
        }

        return 0;
}

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

void Menu::SetParent(Menu* pParent)
{
        m_pParent = pParent;
}

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

void Menu::AddChild(Menu* pChild)
{
        m_Children.push_back(pChild);
}

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

void Menu::RemoveChild(Menu* pChild)
{
        for (unsigned int i=0; i<m_Children.size(); i++)
        {
                if (m_Children[i] == pChild)
                        m_Children.erase(m_Children.begin()+i);
        }
}

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

void Menu::Moving()
{       
        MENUITERATOR item;
        HCURSOR hCurs = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEALL));

        for (item=m_MenuItems.begin(); item != m_MenuItems.end(); item++)
        {
                (*item)->Moving();
                SetCursor(hCurs);
        }
}

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

Menu* Menu::GetParent()
{
        return m_pParent;
}

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

bool Menu::OnUser(int nMessage, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
{
        for (MENUITERATOR i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
        {
                if ((*i)->OnUser(nMessage, wParam, lParam, lResult))
                        return true;
        }

        return false;
}

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

bool Menu::FindActiveAndDeactivate(MENUITERATOR* pItem)
{
        for (*pItem = m_MenuItems.begin(); *pItem != m_MenuItems.end(); ++(*pItem))
        {
                if ((**pItem)->IsActive())
                {
                        (**pItem)->Active(false);
                        return true;
                }
        }
        // If we did not find any active item...
        return false;
}

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

LRESULT Menu::Command(WPARAM wParam, LPARAM lParam)
{
        LRESULT l=0;
        MENUITERATOR item;

        for (item=m_MenuItems.begin(); item != m_MenuItems.end(); ++item)
        {
                l = (*item)->Command(wParam, lParam);
                if (l != 0) break;
        }

        return l;
}

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

void Menu::Sort(int beginOffset, int endOffset)
{
        // Sort the menu items...
        sort(m_MenuItems.begin()+beginOffset, m_MenuItems.end()-endOffset, MenuItem::Compare);
}

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

void Menu::Invalidate()
{
        m_bValidated = false;
}

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

void Menu::Validate()
{
        if(m_bValidated) return;

        int w=0, h=0;
        MENUITERATOR m;
        HDC hDC;
        bool bTitleFinished = false; // db: damn, menu title counts as an item, can't add another borderwidth to height until its drawn...

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

        hDC = CreateDC("DISPLAY", NULL, NULL, NULL);

        for (m = m_MenuItems.begin(); m != m_MenuItems.end(); m++)
        {
                SIZE size;
                char* pszText = (*m)->GetSortString();
                // int extra = 25;
                int extra = pSettings->bevelWidth;

                if (pszText != NULL)
                {
                        // Set the desired width of this menu item...
                        HFONT hFontSaved = (HFONT) SelectObject(hDC, (*m)->GetPainter()->GetFont());
                        GetTextExtentPoint32(hDC, pszText, strlen(pszText), &size);
                        if (m == m_MenuItems.begin())
                                (*m)->SetWidth(size.cx + (2 * pMenuMaker->m_nBorderWidth) + (2 * pSettings->bevelWidth) + 4);
                        else
                                (*m)->SetWidth(size.cx + (*m)->GetIndent() + (*m)->GetRightIndent() + extra);
                        SelectObject(hDC, hFontSaved);
                }

                // Find the widest element...
                w = max((*m)->GetWidth(), w);
        }

        DeleteDC(hDC);

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

        // Make sure that w is not wider than max (300 pixels)...
        w = (pMenuMaker->m_nBorderWidth * 2) + min(w, 300);

        for (m = m_MenuItems.begin(); m != m_MenuItems.end(); m++)
        {
                (*m)->SetWidth(w);
                (*m)->SetPosition(0, h); 

                if (!bTitleFinished)
                        bTitleFinished = true;

                h += (*m)->GetHeight();
        }

        h = h + pMenuMaker->m_nBorderWidth;

        //SetWindowRgn(m_hWnd, rgnWnd, true);
        SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, w, h, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOCOPYBITS|SWP_NOSENDCHANGING);

        // Refresh menu.frame gradient cache and repaint the window...
        cachedMenuFrameExists = false;
        if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
        cachedMenuFrame = 0;
        InvalidateRect(m_hWnd, NULL, false);

        m_bValidated = true;
}

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

// Window procedure for the popup menues
LRESULT CALLBACK MenuWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        POINT point;
        Menu* p = NULL;
        LRESULT ret = 0;

        // Find the menu object that is interested in the message...
        for (unsigned int i=0; i < g_Menues.size(); i++)
        {
                if (hwnd == g_Menues[i]->GetWindow())
                {
                        p = g_Menues[i];
                        break;
                }
        }

        // If no popup menu object was found then simply call the defwindowproc...
        if (p == NULL) return DefWindowProc(hwnd, uMsg, wParam, lParam);
        
        if (uMsg >= WM_USER)
        {
                LRESULT lResult = 0;

                if (p->OnUser((int) uMsg, wParam, lParam, lResult)) return lResult;
        }

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

        switch (uMsg)
        {
                case WM_NCHITTEST:
                {
                        point.x = GET_X_LPARAM(lParam);
                        point.y = GET_Y_LPARAM(lParam);
                        ScreenToClient(hwnd, &point);
                        ret = p->NcHitTest(point.x, point.y);
                        if (ret == 0) ret = HTCLIENT;
                }
                break;

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

                case WM_NCMOUSEMOVE:
                {
                        point.x = GET_X_LPARAM(lParam);
                        point.y = GET_Y_LPARAM(lParam);
                        ScreenToClient(hwnd, &point);
                        p->Mouse(WM_MOUSEMOVE, point.x, point.y);
                }
                break;

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

                case WM_NCLBUTTONUP:                    
                case WM_NCRBUTTONUP:                    
                case WM_NCMBUTTONUP:                    
                {
                        point.x = GET_X_LPARAM(lParam);
                        point.y = GET_Y_LPARAM(lParam);
                        ScreenToClient(hwnd, &point);
                        p->Mouse(uMsg, point.x, point.y);
                }
                break;

                case WM_NCLBUTTONDOWN:
                case WM_NCRBUTTONDOWN:
                case WM_NCMBUTTONDOWN:
                {
                        return DefWindowProc(hwnd, uMsg, wParam, lParam);
                }
                break;

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

                case WM_MOUSEMOVE:
                {
                        // Hack to make keyboard navigation work
                        // when the mouse is above the window...
                        static int lastX=0;
                        static int lastY=0;

                        if (lastX == GET_X_LPARAM(lParam) && lastY == GET_Y_LPARAM(lParam))
                                return 0;

                        lastX = GET_X_LPARAM(lParam);
                        lastY = GET_Y_LPARAM(lParam);
                }

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

                // Fall through...
                case WM_LBUTTONUP:
                case WM_RBUTTONUP:
                case WM_MBUTTONUP:
                {
                        p->Mouse(uMsg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
                }
                break;

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

                case WM_MOUSEWHEEL:
                {
                        if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) p->Mouse(uMsg, 0, 0);
                        else p->Mouse(uMsg, 1, 0);
                }
                break;

                //====================
/*
                case WM_KEYDOWN:
                {
                        switch(wParam)
                        {
                                case VK_DOWN:   // Down
                                case VK_NEXT:   // PageDown (x items down)
                                case VK_UP:             // Up
                                case VK_PRIOR:  // PageUp (x items up)
                                case VK_HOME:   // First item
                                case VK_END:    // Last item
                                case VK_RIGHT:  // Open submenu (expand folder)
                                case VK_LEFT:   // Close submenu
                                case VK_RETURN: // Execute command
                                case VK_SPACE:  // Execute command
                                case VK_ESCAPE: // Close menu

                                default:
                                        return 0;
                        }
                }
                break;
*/
                //====================

                case WM_PAINT:
                {
                        p->Paint();
                }
                break;

                //====================
/*
                case WM_CTLCOLOREDIT:
                {
                        const HDC hDC = (HDC)wParam;
                        SetTextColor(hDC, pSettings->menuFrameTextColor);
                        SetBkColor(hDC, pSettings->menuFrameColor);
                        SetBkMode(hDC, TRANSPARENT); // OPAQUE
                        return 0; // Should return the handle of a brush 
                }
*/
                //====================

                case WM_CLOSE:
                        return 0;

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

                case WM_ACTIVATE:
                {
                        p->Activate(LOWORD(wParam), (HWND)lParam);
                }
                break;

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

                case WM_TIMER:
                {
                        p->Time(wParam);
                }
                break;

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

                case WM_MOVING:
                {
                        p->Moving();
                }
                break;

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

                case WM_WINDOWPOSCHANGING:
                {
                        if (IsWindowVisible(hwnd)) SnapWindowToEdge((WINDOWPOS*)lParam, pSettings->edgeSnapThreshold, true);
                }
                break;

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

                case WM_ERASEBKGND:
                return true;

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

                case WM_COMMAND:
                {
                        p->Command(wParam, lParam);
                }
                break;

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

                case WM_MOUSELEAVE:
                {
                        p->MouseLeave();
                }
                break;

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

                case BB_MENU:
                {
                        if (wParam == 0) // Main menu
                        {
                                WIN32_FIND_DATA oldData = p->data;
                                pMenuMaker->Hide();
                                HANDLE hFind = FindFirstFile(pSettings->menuFile, &p->data);
                                FindClose(hFind);
                                if (CompareFileTime(&p->data.ftLastWriteTime, &oldData.ftLastWriteTime) == 1) pMenuMaker->ParseMenu();
                                pMenuMaker->ShowRootMenu();
                        }
                        else if (wParam == 1) // Workspaces menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowDesktopMenu();
                        }
                        else if (wParam == 2) // Toolbar menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowToolbarMenu();
                        }
                        else if (wParam == 3) // Systembar menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowSystembarMenu();
                        }
                        else if (wParam == 4) // Slit menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowSlitMenu();
                        }
                        else if (wParam == 5) // Styles menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowStylesMenu();
                        }
                        else if (wParam == 6) // Themes menu
                        {
                                pMenuMaker->Hide();
                                pMenuMaker->ShowThemesMenu();
                        }
                }
                break;

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

                case BB_HIDEMENU:
                {
                        pMenuMaker->Hide();
                        p->Hide(HIDE_CHILDREN);
                        p->Hide(HIDE_PARENTS);
                }
                break;

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

                default:
                {
                        ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
                }

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

        return ret;
}

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





syntax highlighting by

w e b c p p
web c plus plus