Index: dll/win32/shell32/lang/en-US.rc =================================================================== --- dll/win32/shell32/lang/en-US.rc (revision 57871) +++ dll/win32/shell32/lang/en-US.rc (working copy) @@ -805,4 +805,14 @@ IDS_BYTES_FORMAT "bytes" IDS_UNKNOWN_APP "Unknown application" IDS_EXE_DESCRIPTION "Description:" + + IDS_SM_PROGRAMS "Programs" + IDS_SM_DOCUMENTS "Documents" + IDS_SM_FAVORITES "Favorites" + IDS_SM_SETTINGS "Settings" + IDS_SM_SEARCH "Search" + IDS_SM_HELP_AND_SUPPORT "Help and Support" + IDS_SM_RUN "Run..." + IDS_SM_LOGOFF "Log Off" + IDS_SM_TURN_OFF "Turn Off Computer" END Index: dll/win32/shell32/shresdef.h =================================================================== --- dll/win32/shell32/shresdef.h (revision 57871) +++ dll/win32/shell32/shresdef.h (working copy) @@ -184,6 +184,17 @@ #define IDS_DESCRIPTION 331 #define IDS_COPY_OF 332 +/* Start Menu*/ +#define IDS_SM_PROGRAMS 333 +#define IDS_SM_DOCUMENTS 334 +#define IDS_SM_FAVORITES 335 +#define IDS_SM_SETTINGS 336 +#define IDS_SM_SEARCH 337 +#define IDS_SM_HELP_AND_SUPPORT 338 +#define IDS_SM_RUN 339 +#define IDS_SM_LOGOFF 341 +#define IDS_SM_TURN_OFF 342 + /* Note: those strings are referenced from the registry */ #define IDS_RECYCLEBIN_FOLDER_NAME 8964 #define IDS_ADMINISTRATIVETOOLS 22982 Index: dll/win32/shell32/startmenu.cpp =================================================================== --- dll/win32/shell32/startmenu.cpp (revision 57871) +++ dll/win32/shell32/startmenu.cpp (working copy) @@ -2,6 +2,7 @@ * Start menu object * * Copyright 2009 Andrew Hill + * Copyright 2012 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,10 +23,958 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell32start); +/* NOTE: The following constants may *NOT* be changed because + they're hardcoded and need to be the exact values + in order to get the start menu to work! */ +#define IDM_PROGRAMS 504 +#define IDM_FAVORITES 507 +#define IDM_DOCUMENTS 501 +#define IDM_SETTINGS 508 +#define IDM_CONTROLPANEL 505 +#define IDM_SECURITY 5001 +#define IDM_NETWORKCONNECTIONS 557 +#define IDM_PRINTERSANDFAXES 510 +#define IDM_TASKBARANDSTARTMENU 413 +#define IDM_SEARCH 520 +#define IDM_HELPANDSUPPORT 503 +#define IDM_RUN 401 +#define IDM_SYNCHRONIZE 553 +#define IDM_LOGOFF 402 +#define IDM_DISCONNECT 5000 +#define IDM_UNDOCKCOMPUTER 410 +#define IDM_SHUTDOWN 506 +#define IDM_LASTSTARTMENU_SEPARATOR 450 + +static const +TCHAR szStartMenuWndClass[] = TEXT("DV2ControlHost"); + +static const INT StartMenuIconIDs[STARTMENUITEM_COUNT] = +{ + IDI_SHELL_PROGRAMS_FOLDER, + IDI_SHELL_RECENT_DOCUMENTS, + IDI_SHELL_FAVORITES, + IDI_SHELL_RUN, + IDI_SHELL_SEARCH, + IDI_SHELL_HELP, + IDI_SHELL_RUN2, + 0, + IDI_SHELL_LOGOFF, + IDI_SHELL_TURN_OFF, +}; + +static const INT StartMenuTextIDs[STARTMENUITEM_COUNT] = +{ + IDS_SM_PROGRAMS, + IDS_SM_DOCUMENTS, + IDS_SM_FAVORITES, + IDS_SM_SETTINGS, + IDS_SM_SEARCH, + IDS_SM_HELP_AND_SUPPORT, + IDS_SM_RUN, + 0, + IDS_SM_LOGOFF, + IDS_SM_TURN_OFF, +}; + +static const BOOL StartMenuItemHasSubMenu[STARTMENUITEM_COUNT] = +{ + TRUE, + TRUE, + TRUE, + TRUE, + TRUE, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, +}; + +/* is it a separator index? */ +#define STARTMENU_ISSEPINDEX(i) (!StartMenuIconIDs[i]) + +static VOID +PostTrayCommand(UINT nCommandID) +{ + HWND hTrayWnd; + hTrayWnd = FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL); + PostMessage(hTrayWnd, WM_COMMAND, MAKEWPARAM(nCommandID, 0), 0); +} + +static VOID +StartMenu_CalcClientSize(CStartMenu *pMenu, LPSIZE psiz) +{ + HFONT hFont; + HDC hdc; + HGDIOBJ hFontOld; + INT i; + SIZE siz, sizMax; + + sizMax.cx = sizMax.cy = 0; + hdc = CreateCompatibleDC(NULL); + if (hdc != NULL) + { + hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); + hFontOld = SelectObject(hdc, hFont); + for(i = 0; i < STARTMENUITEM_COUNT; i++) + { + GetTextExtentPoint32( + hdc, + pMenu->m_aszStartMenuTexts[i], + lstrlen(pMenu->m_aszStartMenuTexts[i]), + &siz); + if (sizMax.cx < siz.cx) sizMax.cx = siz.cx; + if (sizMax.cy < siz.cy) sizMax.cy = siz.cy; + } + SelectObject(hdc, hFontOld); + DeleteDC(hdc); + } + + INT nIconSize; + if (pMenu->m_iIcon == BMICON_LARGE) + nIconSize = 32; + else + nIconSize = 16; + pMenu->m_nMenuHeight = max(nIconSize, (INT)sizMax.cy); + pMenu->m_nSepHeight = 8; + + psiz->cx = pMenu->m_sizBitmap.cx + nIconSize + sizMax.cx + 16; + psiz->cy = pMenu->m_nMenuHeight * 8 + pMenu->m_nSepHeight; +} + +static INT +StartMenu_HitTest(HWND hwnd, CStartMenu *pMenu, POINT pt) +{ + RECT rc, rcBanner, rcClient; + INT i, y, cy; + + GetClientRect(hwnd, &rcClient); + if (pMenu->m_hBitmap != NULL) + { + SetRect(&rcBanner, + 0, 0, + pMenu->m_sizBitmap.cx, rcClient.bottom - rcClient.top); + if (PtInRect(&rcBanner, pt)) + return -2; /* Clicked the banner */ + } + + for(i = y = 0; i < STARTMENUITEM_COUNT; i++) + { + if (STARTMENU_ISSEPINDEX(i)) + { + cy = pMenu->m_nSepHeight; + } + else + { + cy = pMenu->m_nMenuHeight; + SetRect(&rc, + rcClient.left + pMenu->m_sizBitmap.cx, y, + rcClient.right, y + cy); + if (PtInRect(&rc, pt)) + return i; /* index of Start Menu items */ + } + y += cy; + } + + return -1; /* Out of Start Menu */ +} + +static VOID +StartMenu_OnPaint(HWND hwnd, CStartMenu *pMenu) +{ + PAINTSTRUCT ps; + HDC hdc, hdcMem; + RECT rc, rcClient; + POINT pt; + HGDIOBJ hbmOld; + HBRUSH hbr; + INT i, y, cy; + + GetClientRect(hwnd, &rcClient); + GetCursorPos(&pt); + ScreenToClient(hwnd, &pt); + + hdc = BeginPaint(hwnd, &ps); + if (hdc != NULL) + { + if (pMenu->m_hBitmap != NULL) + { + /* fill the banner background */ + hbr = CreateSolidBrush(RGB(138, 178, 219)); + if (hbr != NULL) + { + rcClient.right = rcClient.left + pMenu->m_sizBitmap.cx; + FillRect(hdc, &rcClient, hbr); + DeleteObject(hbr); + } + GetClientRect(hwnd, &rcClient); + + /* draw the banner */ + hdcMem = CreateCompatibleDC(hdc); + if (hdcMem != NULL) + { + hbmOld = SelectObject(hdcMem, pMenu->m_hBitmap); + BitBlt(hdc, + 0, rcClient.bottom - pMenu->m_sizBitmap.cy, + pMenu->m_sizBitmap.cx, pMenu->m_sizBitmap.cy, + hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + GdiFlush(); + DeleteDC(hdcMem); + } + } + + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + SetBkMode(hdc, TRANSPARENT); + + /* draw icons and texts */ + HGDIOBJ hPenOld; + HPEN hGrayPen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128)); + HPEN hLtGrayPen = CreatePen(PS_SOLID, 1, RGB(192, 192, 192)); + for(y = i = 0; i < STARTMENUITEM_COUNT; i++) + { + if (STARTMENU_ISSEPINDEX(i)) + { + cy = pMenu->m_nSepHeight; + + /* draw separator */ + hPenOld = SelectObject(hdc, hGrayPen); + MoveToEx(hdc, + rcClient.left + pMenu->m_sizBitmap.cx + 8, + rcClient.top + y + pMenu->m_nSepHeight / 2, + NULL); + LineTo(hdc, + rcClient.right - 8, + rcClient.top + y + pMenu->m_nSepHeight / 2); + + SelectObject(hdc, hLtGrayPen); + MoveToEx(hdc, + rcClient.left + pMenu->m_sizBitmap.cx + 8 + 1, + rcClient.top + y + pMenu->m_nSepHeight / 2 + 1, + NULL); + LineTo(hdc, + rcClient.right - 8 + 1, + rcClient.top + y + pMenu->m_nSepHeight / 2 + 1); + + SelectObject(hdc, hPenOld); + } + else + { + cy = pMenu->m_nMenuHeight; + + /* fill background */ + SetRect(&rc, + rcClient.left + pMenu->m_sizBitmap.cx, y, + rcClient.right, y + cy); + if (pMenu->m_iMenuSelected == i) + { + FillRect(hdc, &rc, (HBRUSH)(COLOR_HIGHLIGHT + 1)); + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + else if (PtInRect(&rc, pt)) + { + FillRect(hdc, &rc, (HBRUSH)(COLOR_HIGHLIGHT + 1)); + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + else + { + FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE + 1)); + SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); + } + + if (StartMenuItemHasSubMenu[i]) + { + /* draw a black triangle */ + POINT apt[3] = + { + {rcClient.right - 8, y + cy / 2}, + {rcClient.right - 12, y + cy / 2 - 4}, + {rcClient.right - 12, y + cy / 2 + 4}, + }; + SelectObject(hdc, GetStockObject(NULL_PEN)); + SelectObject(hdc, GetStockObject(BLACK_BRUSH)); + Polygon(hdc, apt, 3); + } + + /* draw an icon */ + INT nIconSize; + if (pMenu->m_iIcon == BMICON_LARGE) + nIconSize = 32; + else + nIconSize = 16; + DrawIconEx(hdc, pMenu->m_sizBitmap.cx, y, pMenu->m_ahIcons[i], + nIconSize, nIconSize, 0, NULL, DI_NORMAL); + + /* draw a text */ + SetRect(&rc, + rcClient.left + pMenu->m_sizBitmap.cx + nIconSize, y, + rcClient.right, y + cy); + DrawText(hdc, pMenu->m_aszStartMenuTexts[i], -1, + &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + } + y += cy; + } + DeleteObject(hGrayPen); + DeleteObject(hLtGrayPen); + + EndPaint(hwnd, &ps); + } +} + +static BOOL +StartMenu_OnCreate(HWND hwnd, CStartMenu *pMenu) +{ + INT i; + + /* load icons and texts */ + for(i = 0; i < STARTMENUITEM_COUNT; i++) + { + if (StartMenuIconIDs[i] != 0) + { + pMenu->m_ahIcons[i] = LoadIcon( + shell32_hInstance, + MAKEINTRESOURCE(StartMenuIconIDs[i])); + LoadString(shell32_hInstance, StartMenuTextIDs[i], + pMenu->m_aszStartMenuTexts[i], + sizeof(pMenu->m_aszStartMenuTexts[i]) / + sizeof(pMenu->m_aszStartMenuTexts[i][0])); + } + else + { + pMenu->m_ahIcons[i] = NULL; + pMenu->m_aszStartMenuTexts[i][0] = 0; + } + } + + return TRUE; +} + +static bool +GetFileList(LPCTSTR pszDir, CTinyVector& list) +{ + tstring str(pszDir), str2; + FILEITEMDATA data; + + str2 = str + _T("\\*"); + + WIN32_FIND_DATA find; + HANDLE hFind = FindFirstFile(str2.c_str(), &find); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (lstrcmp(find.cFileName, TEXT(".")) != 0 && + lstrcmp(find.cFileName, TEXT("..")) != 0) + { + data.path = str; + data.name = find.cFileName; + data.is_folder = true; + list.push_back(data); + } + } + else + { + data.path = str; + data.name = find.cFileName; + data.is_folder = false; + list.push_back(data); + } + } while(FindNextFile(hFind, &find)); + FindClose(hFind); + return true; + } + if (GetLastError() == ERROR_ACCESS_DENIED) + return false; + return true; +} + +static VOID +AddBackslash(tstring& str) +{ + size_t size = str.size(); +#ifdef UNICODE + if (str[size - 1] != TEXT('\\')) + str += TEXT("\\"); +#else + LPSTR pch = CharPrevA(str.c_str(), str.c_str() + size); + if (*pch != '\\') + str += "\\"; +#endif +} + +static VOID +AddPathToMenu(CStartMenu *pMenu, HMENU hMenu, LPCTSTR pszPath) +{ + INT nCount = 0; + size_t i = pMenu->m_list.size(); + + if (!GetFileList(pszPath, pMenu->m_list)) + { + AppendMenu(hMenu, MF_GRAYED | MF_STRING, 0, TEXT("(Access Denied)")); + return; + } + + for(; i < pMenu->m_list.size(); i++) + { + if (pMenu->m_list[i].is_folder) + { + HMENU hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + tstring path(pMenu->m_list[i].path); + AddBackslash(path); + path += pMenu->m_list[i].name; + pMenu->m_map.insert(make_pair(hSubMenu, path)); + AppendMenu(hMenu, + MF_ENABLED | MF_POPUP, + (UINT_PTR)hSubMenu, + pMenu->m_list[i].name.c_str()); + nCount++; + } + } + else + { + AppendMenu(hMenu, + MF_ENABLED | MF_STRING, + i + 1, + pMenu->m_list[i].name.c_str()); + nCount++; + } + } + + if (nCount == 0) + { + AppendMenu(hMenu, MF_GRAYED | MF_STRING, 0, TEXT("(Empty)")); + } +} + +static bool +GetPathFromID( + CStartMenu *pMenu, + UINT_PTR nID, + tstring& path, + tstring& name, + bool& is_folder) +{ + path.clear(); + name.clear(); + is_folder = false; + + if (nID > (UINT_PTR)pMenu->m_list.size() || nID == 0) + return false; + + nID--; + path = pMenu->m_list[nID].path; + name = pMenu->m_list[nID].name; + is_folder = pMenu->m_list[nID].is_folder; + return true; +} + +static void +StartMenu_OnMenuSelect( + HWND hwnd, + CStartMenu *pMenu, + WPARAM wParam, + LPARAM lParam) +{ + if (HIWORD(wParam) & MF_POPUP) + { + HMENU hSubMenu = GetSubMenu((HMENU)lParam, LOWORD(wParam)); + if (hSubMenu != NULL && ::GetMenuItemCount(hSubMenu) == 0) + { + CTinyMap::iterator it = pMenu->m_map.find(hSubMenu); + if (it != pMenu->m_map.end()) + { + AddPathToMenu(pMenu, hSubMenu, it->second.c_str()); + } + } + } +} + +bool ShowFileMenu( + HWND hwnd, + CStartMenu *pMenu, + LPCTSTR pszDir1, + LPCTSTR pszDir2, + POINT pt, + UINT uFlags, + tstring& path, + tstring& name) +{ + HMENU hMenu = CreatePopupMenu(); + if (hMenu == NULL) + return false; + + pMenu->m_map.clear(); + pMenu->m_list.clear(); + AddPathToMenu(pMenu, hMenu, pszDir1); + if (pszDir2 != NULL) + AddPathToMenu(pMenu, hMenu, pszDir2); + + pMenu->m_bTracking = TRUE; + UINT nID = TrackPopupMenu(hMenu, uFlags | TPM_RETURNCMD, + pt.x, pt.y, NULL, hwnd, NULL); + pMenu->m_bTracking = FALSE; + + bool is_folder; + bool b = GetPathFromID(pMenu, (UINT_PTR)nID, path, name, is_folder); + pMenu->m_map.clear(); + pMenu->m_list.clear(); + DestroyMenu(hMenu); + + if (b) + { + /* menu item selected */ + ReleaseCapture(); + ShowWindow(pMenu->m_hWnd, SW_HIDE); + } + else + { + /* no item selected */ + if (GetKeyState(VK_LBUTTON) < 0) + { + /* left button pressed */ + RECT rcClient; + GetCursorPos(&pt); + ScreenToClient(pMenu->m_hWnd, &pt); + GetClientRect(pMenu->m_hWnd, &rcClient); + if (PtInRect(&rcClient, pt)) + { + /* in client area */ + PostMessage(pMenu->m_hWnd, WM_LBUTTONDOWN, + 0, MAKELPARAM(pt.x, pt.y)); + } + else + { + /* out of client area */ + ReleaseCapture(); + ShowWindow(pMenu->m_hWnd, SW_HIDE); + } + } + } + + return b; +} + +static HMENU +CreateDocumentsMenu(CStartMenu *pMenu) +{ + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, IDM_DOCUMENTS, TEXT("My Documents")); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_GRAYED | MF_STRING, -1, TEXT("(Empty)")); + return hMenu; +} + +static HMENU +CreateSettingsMenu(CStartMenu *pMenu) +{ + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, IDM_CONTROLPANEL, TEXT("Control Panel")); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_STRING, IDM_NETWORKCONNECTIONS, TEXT("Network Connections")); + AppendMenu(hMenu, MF_STRING, IDM_PRINTERSANDFAXES, TEXT("Printers and Faxes")); + AppendMenu(hMenu, MF_STRING, IDM_TASKBARANDSTARTMENU, TEXT("Taskbar and Start Menu")); + return hMenu; +} + +static HMENU +CreateSearchMenu(CStartMenu *pMenu) +{ + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, IDM_SEARCH, TEXT("For Files or Folders")); + AppendMenu(hMenu, MF_GRAYED | MF_STRING, -1, TEXT("On the Internet...")); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_GRAYED | MF_STRING, -1, TEXT("For People...")); + return hMenu; +} + +static VOID +StartMenu_PopupSubMenu(CStartMenu *pMenu, INT i) +{ + RECT rcClient; + INT j, x, y, cy; + + GetClientRect(pMenu->m_hWnd, &rcClient); + x = rcClient.right; + for(j = y = 0; j < STARTMENUITEM_COUNT; j++) + { + if (STARTMENU_ISSEPINDEX(j)) + cy = pMenu->m_nSepHeight; + else + cy = pMenu->m_nMenuHeight; + if (j == i) + break; + y += cy; + } + + POINT pt = {x, y}; + ClientToScreen(pMenu->m_hWnd, &pt); + + TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; + tstring path, name; + HMENU hMenu; + + switch(i) + { + case 0: /* Programs */ + SHGetSpecialFolderPath(pMenu->m_hWnd, szPath1, CSIDL_COMMON_PROGRAMS, FALSE); + SHGetSpecialFolderPath(pMenu->m_hWnd, szPath2, CSIDL_PROGRAMS, FALSE); + if (ShowFileMenu( + pMenu->m_hWnd, + pMenu, + szPath1, + szPath2, + pt, + TPM_LEFTBUTTON | TPM_LEFTALIGN, + path, + name)) + { + tstring fullpath(path); + AddBackslash(fullpath); + fullpath += name; + + ShellExecute( + pMenu->m_hWnd, + NULL, + fullpath.c_str(), + NULL, + NULL, + SW_SHOWNORMAL); + } + return; + + case 1: /* Documents */ + hMenu = CreateDocumentsMenu(pMenu); + break; + + case 2: /* Favorites */ + SHGetSpecialFolderPath(pMenu->m_hWnd, szPath1, CSIDL_FAVORITES, FALSE); + if (ShowFileMenu( + pMenu->m_hWnd, + pMenu, + szPath1, + NULL, + pt, + TPM_LEFTBUTTON | TPM_LEFTALIGN, + path, + name)) + { + tstring fullpath(path); + AddBackslash(fullpath); + fullpath += name; + + ShellExecute( + pMenu->m_hWnd, + NULL, + fullpath.c_str(), + NULL, + NULL, + SW_SHOWNORMAL); + } + return; + + case 3: /* Settings */ + hMenu = CreateSettingsMenu(pMenu); + break; + + case 4: /* Search */ + hMenu = CreateSearchMenu(pMenu); + break; + + default: + return; + } + + pMenu->m_bTracking = TRUE; + UINT nCmdID = (UINT)TrackPopupMenuEx(hMenu, + TPM_LEFTBUTTON | TPM_LEFTALIGN | TPM_RETURNCMD, + pt.x, pt.y, pMenu->m_hWnd, NULL); + DestroyMenu(hMenu); + pMenu->m_bTracking = FALSE; + + pMenu->m_iMenuSelected = -1; + InvalidateRect(pMenu->m_hWnd, NULL, FALSE); + + if (nCmdID == 0) + { + /* no item selected */ + if (GetKeyState(VK_LBUTTON) < 0) + { + /* left button pressed */ + GetCursorPos(&pt); + ScreenToClient(pMenu->m_hWnd, &pt); + if (PtInRect(&rcClient, pt)) + { + /* in client area */ + PostMessage(pMenu->m_hWnd, WM_LBUTTONDOWN, + 0, MAKELPARAM(pt.x, pt.y)); + } + else + { + /* out of client area */ + ReleaseCapture(); + ShowWindow(pMenu->m_hWnd, SW_HIDE); + } + } + } + else + { + /* menu item selected */ + ReleaseCapture(); + ShowWindow(pMenu->m_hWnd, SW_HIDE); + } +} + +static VOID +StartMenu_ExecuteItem(HWND hwnd, CStartMenu *pMenu, INT i) +{ + UINT nCommandID; + + switch(i) + { + case 4: nCommandID = IDM_SEARCH; break; + case 5: nCommandID = IDM_HELPANDSUPPORT; break; + case 6: nCommandID = IDM_RUN; break; + case 8: nCommandID = IDM_LOGOFF; break; + case 9: nCommandID = IDM_SHUTDOWN; break; + default: return; + } + ShowWindow(hwnd, SW_HIDE); + PostTrayCommand(nCommandID); +} + +static VOID +StartMenu_OnKeyDown(HWND hwnd, CStartMenu *pMenu, UINT vk) +{ + POINT pt; + RECT rc; + + GetCursorPos(&pt); + GetWindowRect(hwnd, &rc); + + switch(vk) + { + case VK_ESCAPE: + ShowWindow(hwnd, SW_HIDE); + break; + + case VK_UP: + if (PtInRect(&rc, pt)) + return; + if (-1 == pMenu->m_iMenuSelected) + { + pMenu->m_iMenuSelected = STARTMENUITEM_COUNT - 1; + } + else if (pMenu->m_iMenuSelected - 1 >= 0) + { + (pMenu->m_iMenuSelected)--; + if (STARTMENU_ISSEPINDEX(pMenu->m_iMenuSelected)) + (pMenu->m_iMenuSelected)--; + } + else + { + pMenu->m_iMenuSelected = STARTMENUITEM_COUNT - 1; + } + break; + + case VK_DOWN: + if (PtInRect(&rc, pt)) + return; + if (-1 == pMenu->m_iMenuSelected) + { + pMenu->m_iMenuSelected = 0; + } + else if (pMenu->m_iMenuSelected + 1 < STARTMENUITEM_COUNT) + { + (pMenu->m_iMenuSelected)++; + if (STARTMENU_ISSEPINDEX(pMenu->m_iMenuSelected)) + (pMenu->m_iMenuSelected)++; + } + else + { + pMenu->m_iMenuSelected = 0; + } + break; + + case VK_LEFT: + if (PtInRect(&rc, pt)) + return; + if (-1 != pMenu->m_iMenuSelected) + { + if (StartMenuItemHasSubMenu[pMenu->m_iMenuSelected]) + { + StartMenu_PopupSubMenu(pMenu, pMenu->m_iMenuSelected); + } + } + break; + + case VK_SPACE: + case VK_RETURN: /* [Enter] key */ + if (-1 != pMenu->m_iMenuSelected) + { + InvalidateRect(hwnd, NULL, FALSE); + if (StartMenuItemHasSubMenu[pMenu->m_iMenuSelected]) + { + StartMenu_PopupSubMenu(pMenu, pMenu->m_iMenuSelected); + } + else + { + StartMenu_ExecuteItem(hwnd, pMenu, pMenu->m_iMenuSelected); + } + } + break; + } + + InvalidateRect(hwnd, NULL, FALSE); +} + +static VOID +StartMenu_OnLButtonDown(HWND hwnd, CStartMenu *pMenu, LPARAM lParam) +{ + POINT pt; + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + + INT i = StartMenu_HitTest(hwnd, pMenu, pt); + if (i == -1) + { + ShowWindow(hwnd, SW_HIDE); + } + else if (i != -2) + { + pMenu->m_iMenuSelected = i; + InvalidateRect(hwnd, NULL, FALSE); + if (StartMenuItemHasSubMenu[i]) + { + StartMenu_PopupSubMenu(pMenu, i); + } + else + { + StartMenu_ExecuteItem(hwnd, pMenu, i); + } + } +} + +static LRESULT CALLBACK +StartMenu_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static CStartMenu *pMenu = NULL; + + switch(uMsg) + { + case WM_CREATE: + pMenu = (CStartMenu *)((LPCREATESTRUCT)lParam)->lpCreateParams; + pMenu->m_hWnd = hwnd; + if (!StartMenu_OnCreate(hwnd, pMenu)) + return -1; + return 0; + + case WM_SHOWWINDOW: + if (wParam) + SetCapture(hwnd); + else + ReleaseCapture(); + pMenu->m_iMenuSelected = -1; + break; + + case WM_MOUSEMOVE: + InvalidateRect(hwnd, NULL, FALSE); + break; + + case WM_PAINT: + StartMenu_OnPaint(hwnd, pMenu); + break; + + case WM_KEYDOWN: + StartMenu_OnKeyDown(hwnd, pMenu, (UINT)wParam); + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + StartMenu_OnLButtonDown(hwnd, pMenu, lParam); + break; + + case WM_MENUCHAR: + /* FIXME: Enable Left key */ + break; + + case WM_MENUSELECT: + StartMenu_OnMenuSelect(hwnd, pMenu, wParam, lParam); + break; + + case WM_KILLFOCUS: + lParam = wParam; + /* FALL THRU */ + + case WM_CAPTURECHANGED: + if (!pMenu->m_bTracking) + { + TCHAR szClsName[32]; + GetClassName((HWND)lParam, szClsName, 32); + if (lstrcmpi(szClsName, TEXT("BUTTON")) == 0) + { + DWORD PID; + GetWindowThreadProcessId((HWND)lParam, &PID); + if (GetCurrentProcessId() == PID) + break; + } + ShowWindow(hwnd, SW_HIDE); + } + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return 0; +} + +static BOOL +StartMenu_Register(VOID) +{ + WNDCLASSEX wcx; + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DBLCLKS; + wcx.lpfnWndProc = StartMenu_WndProc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = sizeof(CStartMenu*); + wcx.hInstance = shell32_hInstance; + wcx.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wcx.hCursor = LoadCursor(NULL, IDC_ARROW); + wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = szStartMenuWndClass; + wcx.hIconSm = (HICON)LoadImage(NULL, IDI_WINLOGO, IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); + + return RegisterClassEx(&wcx) != 0; +} + +static HWND +StartMenu_Create(CStartMenu *pMenu) +{ + DWORD dwStyle, dwExStyle; + + dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_POPUP; + dwExStyle = WS_EX_TOPMOST | WS_EX_WINDOWEDGE; + return CreateWindowEx(dwExStyle, szStartMenuWndClass, TEXT("Start Menu"), + dwStyle, 0, 0, 0, 0, NULL, NULL, shell32_hInstance, pMenu); +} + CStartMenu::CStartMenu() { m_pBandSite = NULL; m_pUnkSite = NULL; + m_hWnd = NULL; + m_iIcon = BMICON_LARGE; + m_hBitmap = NULL; + m_sizBitmap.cx = m_sizBitmap.cy = 0; + m_iMenuSelected = -1; + m_bTracking = FALSE; } CStartMenu::~CStartMenu() @@ -40,8 +989,10 @@ HRESULT STDMETHODCALLTYPE CStartMenu::GetWindow(HWND *phwnd) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (phwnd == NULL) + return E_POINTER; + *phwnd = m_hWnd; + return S_OK; } HRESULT STDMETHODCALLTYPE CStartMenu::GetClient(IUnknown **ppunkClient) @@ -73,8 +1024,34 @@ HRESULT STDMETHODCALLTYPE CStartMenu::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags) { - UNIMPLEMENTED; - return E_NOTIMPL; + RECT rc, rcWorkArea; + SIZE siz; + + StartMenu_CalcClientSize(this, &siz); + rc.left = rc.top = 0; + rc.right = siz.cx; rc.bottom = siz.cy; + DWORD dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_POPUP; + DWORD dwExStyle = WS_EX_TOPMOST | WS_EX_WINDOWEDGE; + AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle); + siz.cx = rc.right - rc.left; + siz.cy = rc.bottom - rc.top; + + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0); + + /* FIXME: Check ppt and prcExclude */ + MoveWindow(m_hWnd, + 0, rcWorkArea.bottom - (rc.bottom - rc.top), + siz.cx, siz.cy, + TRUE); + + if (IsWindowVisible(m_hWnd)) + ShowWindow(m_hWnd, SW_HIDE); + else + ShowWindow(m_hWnd, SW_SHOW); + + InvalidateRect(m_hWnd, NULL, FALSE); + + return S_OK; } HRESULT STDMETHODCALLTYPE CStartMenu::SetSubMenu(IMenuPopup *pmp, BOOL fSet) @@ -112,6 +1089,9 @@ TRACE("(%p)\n", this); + if (!StartMenu_Register() || !StartMenu_Create(this)) + return E_FAIL; + //pBandSiteObj = new CComObject(); ATLTRY (pBandSiteObj = new CComObject); if (pBandSiteObj == NULL) @@ -119,23 +1099,84 @@ hr = pBandSiteObj->QueryInterface(IID_IBandSite, (VOID**)&m_pBandSite); if (FAILED(hr)) - return NULL; + return E_FAIL; return m_pBandSite->AddBand((IMenuBand*)this); } HRESULT STDMETHODCALLTYPE CStartMenu::IsMenuMessage(MSG *pmsg) { + if (pmsg->message == WM_CREATE) + return S_OK; + + if (IsWindow(m_hWnd) && pmsg->hwnd == m_hWnd) + { + if (PeekMessage(pmsg, m_hWnd, 0, 0, PM_REMOVE)) + { + TranslateMessage(pmsg); + DispatchMessage(pmsg); + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE CStartMenu::TranslateMenuMessage(MSG *pmsg, LRESULT *plRet) +{ + if (plRet == NULL) + return S_FALSE; + + if (pmsg->message == WM_CREATE) + return S_OK; + + if (IsWindow(m_hWnd) && pmsg->hwnd == m_hWnd) + { + if (PeekMessage(pmsg, m_hWnd, 0, 0, PM_REMOVE)) + { + TranslateMessage(pmsg); + DispatchMessage(pmsg); + *plRet = 0; + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE CStartMenu::SetIconSize(DWORD iIcon) +{ UNIMPLEMENTED; return E_NOTIMPL; } -HRESULT STDMETHODCALLTYPE CStartMenu::TranslateMenuMessage(MSG *pmsg, LRESULT *plRet) +HRESULT STDMETHODCALLTYPE CStartMenu::GetIconSize(DWORD* piIcon) { UNIMPLEMENTED; return E_NOTIMPL; } +HRESULT STDMETHODCALLTYPE CStartMenu::SetBitmap(HBITMAP hBitmap) +{ + BITMAP bm; + if (GetObject(hBitmap, sizeof(BITMAP), &bm)) + { + m_hBitmap = hBitmap; + m_sizBitmap.cx = bm.bmWidth; + m_sizBitmap.cy = bm.bmHeight; + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE CStartMenu::GetBitmap(HBITMAP* phBitmap) +{ + if (phBitmap == NULL) + return E_FAIL; + *phBitmap = m_hBitmap; + return S_OK; +} + CMenuBandSite::CMenuBandSite() { m_pObjects = NULL; Index: dll/win32/shell32/startmenu.h =================================================================== --- dll/win32/shell32/startmenu.h (revision 57871) +++ dll/win32/shell32/startmenu.h (working copy) @@ -21,22 +21,203 @@ #ifndef _STARTMENU_H_ #define _STARTMENU_H_ +void* operator new(size_t size, void* ptr); + +class CTinyString { +protected: + LPTSTR m_psz; +public: + CTinyString() { m_psz = _tcsdup(_T("")); } + CTinyString(LPCTSTR psz) { m_psz = _tcsdup(psz); } + CTinyString(const CTinyString& str) { m_psz = _tcsdup(str.m_psz); } + virtual ~CTinyString() { free(m_psz); } + LPCTSTR c_str() { return m_psz; } + size_t size() const { return _tcslen(m_psz); } + TCHAR operator[](size_t i) const { + assert(i <= _tcslen(m_psz)); + return m_psz[i]; + } + bool operator==(LPCTSTR psz) const { return _tcscmp(m_psz, psz) == 0; } + bool operator!=(LPCTSTR psz) const { return _tcscmp(m_psz, psz) != 0; } + bool operator==(const CTinyString& s) const { + return _tcscmp(m_psz, s.m_psz) == 0; + } + bool operator!=(const CTinyString& s) const { + return _tcscmp(m_psz, s.m_psz) != 0; + } + CTinyString& operator=(LPCTSTR psz) { + free(m_psz); + m_psz = _tcsdup(psz); + return *this; + } + CTinyString& operator=(const CTinyString& s) { + free(m_psz); + m_psz = _tcsdup(s.m_psz); + return *this; + } + CTinyString& operator+=(LPCTSTR psz) { + LPTSTR pszNew = (LPTSTR)malloc((size() + _tcslen(psz) + 1) * sizeof(TCHAR)); + if (pszNew != NULL) { + _tcscpy(pszNew, m_psz); + _tcscat(pszNew, psz); + delete[] m_psz; + m_psz = pszNew; + } + return *this; + } + CTinyString& operator+=(const CTinyString& s) { + LPTSTR pszNew = (LPTSTR)malloc((size() + s.size() + 1) * sizeof(TCHAR)); + if (pszNew != NULL) { + _tcscpy(pszNew, m_psz); + _tcscat(pszNew, s.m_psz); + delete[] m_psz; + m_psz = pszNew; + } + return *this; + } + friend CTinyString operator+(const CTinyString& s1, LPCTSTR psz) { + CTinyString s(s1); + s += psz; + return s; + } + friend CTinyString operator+(const CTinyString& s1, const CTinyString& s2) { + CTinyString s(s1); + s += s2; + return s; + } + void clear() { m_psz[0] = 0; } +}; + +template +class CTinyVector { +protected: + size_t m_size; + T *m_ptr; +public: + CTinyVector() { m_size = 0; m_ptr = NULL; } + virtual ~CTinyVector() { clear(); } + size_t size() const { return m_size; } + + T& operator[](size_t i) { + assert(i < m_size); + return m_ptr[i]; } + const T& operator[](size_t i) const { + assert(i < m_size); + return m_ptr[i]; + } + + void push_back(const T& t) { + T *ptr = (T *)malloc((m_size + 1) * sizeof(T)); + if (ptr != NULL) { + for(size_t i = 0; i < m_size + 1; i++) + new((void *)&ptr[i]) T(); + for(size_t i = 0; i < m_size; i++) + ptr[i] = m_ptr[i]; + ptr[m_size] = t; + for(size_t i = 0; i < m_size; i++) m_ptr[i].~T(); + free(m_ptr); + m_ptr = ptr; + m_size++; + } + } + void clear() { + for(size_t i = 0; i < m_size; i++) m_ptr[i].~T(); + free(m_ptr); + m_size = 0; m_ptr = NULL; + } +}; + +template +struct CTinyPair { + KeyT first; + ValueT second; +}; + +template +class CTinyMap { +protected: + CTinyVector > m_pairs; +public: + typedef CTinyPair* iterator; +public: + CTinyMap() { } + virtual ~CTinyMap() { } + void insert(const CTinyPair& pair) { + m_pairs.push_back(pair); + } + iterator end() { return (iterator) NULL; } + iterator find(const KeyT& key) { + for(size_t i = 0; i < m_pairs.size(); i++) { + if (m_pairs[i].first == key) + return &m_pairs[i]; + } + return end(); + } + void clear() { m_pairs.clear(); } +}; + +template +inline CTinyPair make_pair(const KeyT& key, const ValueT& value) +{ + CTinyPair pair = {key, value}; + return pair; +} + +#if 0 + #include + #include + #include + using namespace std; + + #ifdef UNICODE + typedef std::wstring tstring; + #else + typedef std::string tstring; + #endif +#else + typedef CTinyString tstring; +#endif + +struct FILEITEMDATA +{ + tstring path; + tstring name; + bool is_folder; +}; + +#define STARTMENUITEM_COUNT 10 + class CStartMenu : public CComCoClass, public CComObjectRootEx, public IMenuPopup, public IObjectWithSite, public IInitializeObject, - public IMenuBand // FIXME + public IMenuBand, // FIXME + public IBanneredBar { private: IBandSite *m_pBandSite; IUnknown *m_pUnkSite; public: - CStartMenu(); - ~CStartMenu(); + HWND m_hWnd; + DWORD m_iIcon; /* BMICON_* */ + HBITMAP m_hBitmap; + SIZE m_sizBitmap; + INT m_iMenuSelected; + INT m_nMenuHeight; + INT m_nSepHeight; + HICON m_ahIcons[STARTMENUITEM_COUNT]; + TCHAR m_aszStartMenuTexts[STARTMENUITEM_COUNT][64]; + BOOL m_bTracking; + CTinyMap m_map; + CTinyVector m_list; +public: + CStartMenu(); + ~CStartMenu(); + // *** IOleWindow methods *** virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); @@ -62,6 +243,13 @@ virtual HRESULT STDMETHODCALLTYPE IsMenuMessage(MSG *pmsg); virtual HRESULT STDMETHODCALLTYPE TranslateMenuMessage(MSG *pmsg, LRESULT *plRet); + // *** IBanneredBar methods *** + virtual HRESULT STDMETHODCALLTYPE SetIconSize(DWORD iIcon); + virtual HRESULT STDMETHODCALLTYPE GetIconSize(DWORD* piIcon); + virtual HRESULT STDMETHODCALLTYPE SetBitmap(HBITMAP hBitmap); + virtual HRESULT STDMETHODCALLTYPE GetBitmap(HBITMAP* phBitmap); + + DECLARE_REGISTRY_RESOURCEID(IDR_STARTMENU) DECLARE_NOT_AGGREGATABLE(CStartMenu) @@ -71,9 +259,10 @@ COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) COM_INTERFACE_ENTRY_IID(IID_IDeskBar, IDeskBar) COM_INTERFACE_ENTRY_IID(IID_IMenuPopup, IMenuPopup) - COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) - COM_INTERFACE_ENTRY_IID(IID_IInitializeObject, IInitializeObject) - COM_INTERFACE_ENTRY_IID(IID_IMenuBand, IMenuBand) // FIXME: Win does not export it + COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) + COM_INTERFACE_ENTRY_IID(IID_IInitializeObject, IInitializeObject) + COM_INTERFACE_ENTRY_IID(IID_IMenuBand, IMenuBand) // FIXME: Win does not export it + COM_INTERFACE_ENTRY_IID(IID_IBanneredBar, IBanneredBar) END_COM_MAP() };