#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,
menuName,
NULL,
WS_POPUP,
0, 0, 0, 0,
GetBBWnd(),
NULL,
m_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;
SetTransparency(m_hWnd, pSettings->menuTransparencyAlpha);
HANDLE hFind = FindFirstFile(pSettings->menuFile, &data);
FindClose(hFind);
}
Menu::~Menu()
{
for (unsigned int i=0; i<g_Menues.size(); i++)
{
if (g_Menues[i] == this)
{
g_Menues.erase(g_Menues.begin()+i);
break;
}
}
if (m_hWnd) DestroyWindow(m_hWnd);
m_hWnd = NULL;
if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
DeleteMenuItems();
m_nInstances--;
if (m_nInstances == 0)
{
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 (!cachedMenuFrameExists)
{
if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
cachedMenuFrame = CreateCompatibleDC(NULL);
RECT r = {0, 0, menuFrameWidth, menuFrameHeight};
HBITMAP tempBitmap = CreateCompatibleBitmap(ps.hdc, menuFrameWidth, menuFrameHeight);
HBITMAP oldBitmap = (HBITMAP)SelectObject(cachedMenuFrame, tempBitmap);
DeleteObject(oldBitmap);
pBImage->CreateGradientByRect(cachedMenuFrame,
r,
pSettings->MenuFrame->type,
pSettings->MenuFrame->Color,
pSettings->MenuFrame->ColorTo,
pSettings->MenuFrame->interlaced,
pSettings->MenuFrame->bevelstyle,
pSettings->MenuFrame->bevelposition,
pSettings->bevelWidth);
DeleteObject(tempBitmap);
cachedMenuFrameExists = true;
}
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);
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;
GetClientRect(m_hWnd, &r);
pBImage->CreateBorder(hDC, r, pMenuMaker->m_nBorderColor, pMenuMaker->m_nBorderWidth);
r.bottom = pMenuMaker->m_nTitleHeight;
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;
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;
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))
{
r.top -= pSettings->scrollSpeed;
m_bMoved = true;
}
else if ((r.top < screenTop) && (point.y <= screenTop))
{
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();
}
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);
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)
{
r.top += pSettings->wheelSpeed;
r.bottom = r.top + height;
if (r.top > 0)
{
r.top = 0;
r.bottom = height;
}
}
else
{
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_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(¬Used);
m_bMouseOver = false;
}
void Menu::AddMenuItem(MenuItem* m)
{
m_MenuItems.push_back(m);
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;
for (item=m_MenuItems.begin(); item != m_MenuItems.end(); ++item)
delete *item;
m_MenuItems.clear();
}
void Menu::Activate(int fActive, HWND hWnd)
{
}
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;
}
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);
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;
}
}
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(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;
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
for (m = m_MenuItems.begin(); m != m_MenuItems.end(); m++)
{
SIZE size;
char* pszText = (*m)->GetSortString();
int extra = pSettings->bevelWidth;
if (pszText != NULL)
{
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);
}
w = max((*m)->GetWidth(), w);
}
DeleteDC(hDC);
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;
SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, w, h, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOCOPYBITS|SWP_NOSENDCHANGING);
cachedMenuFrameExists = false;
if (cachedMenuFrame) DeleteDC(cachedMenuFrame);
cachedMenuFrame = 0;
InvalidateRect(m_hWnd, NULL, false);
m_bValidated = true;
}
LRESULT CALLBACK MenuWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
POINT point;
Menu* p = NULL;
LRESULT ret = 0;
for (unsigned int i=0; i < g_Menues.size(); i++)
{
if (hwnd == g_Menues[i]->GetWindow())
{
p = g_Menues[i];
break;
}
}
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:
{
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);
}
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_PAINT:
{
p->Paint();
}
break;
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)
{
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)
{
pMenuMaker->Hide();
pMenuMaker->ShowDesktopMenu();
}
else if (wParam == 2)
{
pMenuMaker->Hide();
pMenuMaker->ShowToolbarMenu();
}
else if (wParam == 3)
{
pMenuMaker->Hide();
pMenuMaker->ShowSystembarMenu();
}
else if (wParam == 4)
{
pMenuMaker->Hide();
pMenuMaker->ShowSlitMenu();
}
else if (wParam == 5)
{
pMenuMaker->Hide();
pMenuMaker->ShowStylesMenu();
}
else if (wParam == 6)
{
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