Index: dll/win32/shell32/CMakeLists.txt =================================================================== --- dll/win32/shell32/CMakeLists.txt (revision 58817) +++ dll/win32/shell32/CMakeLists.txt (working copy) @@ -69,6 +69,7 @@ folder_options.cpp filedefext.cpp drvdefext.cpp + fakemenu.cpp ${CMAKE_CURRENT_BINARY_DIR}/shell32_stubs.c ${CMAKE_CURRENT_BINARY_DIR}/shell32.def) Index: dll/win32/shell32/fakemenu.cpp =================================================================== --- dll/win32/shell32/fakemenu.cpp (revision 0) +++ dll/win32/shell32/fakemenu.cpp (working copy) @@ -0,0 +1,1032 @@ +/* + * Fakemenus (menu-like windows) + * + * 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include "precomp.h" + +static const WCHAR g_szFakeMenuClsName[] = L"BaseBar"; + +static HFAKEMENU g_hTopMenu = NULL; + +static BOOL +IsSameOrDecendant(HWND hwnd, HWND hwndDecendant) +{ + if (hwnd == NULL || hwndDecendant == NULL) + return FALSE; + if (hwnd == hwndDecendant || IsChild(hwnd, hwndDecendant)) + return TRUE; + return IsSameOrDecendant(hwnd, GetParent(hwndDecendant)); +} + +INT APIENTRY +GetFakeMenuItemCount(HFAKEMENU hMenu) +{ + if (hMenu->uFlags & MF_POPUP) + { + return (INT)hMenu->children.size(); + } + return 0; +} + +UINT APIENTRY +GetFakeMenuItemID(HFAKEMENU hMenu, INT nPos) +{ + if (hMenu->uFlags & MF_POPUP) + { + if (0 <= nPos && nPos < (INT)hMenu->children.size()) + return (INT)hMenu->children[nPos]->uID; + } + return 0xFFFFFFFF; +} + +BOOL APIENTRY +AppendFakeMenu(HFAKEMENU hMenu, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCWSTR lpNewItem, + LPCWSTR path/* = NULL*/) +{ + HFAKEMENU hSubMenu; + if (uFlags & MF_POPUP) + { + hSubMenu = (HFAKEMENU)uIDNewItem; + hSubMenu->uID = 0xFFFFFFFF; + } + else + { + hSubMenu = CreatePopupFakeMenu(); + hSubMenu->uID = uIDNewItem; + } + if (lpNewItem) + hSubMenu->text = lpNewItem; + hSubMenu->uFlags = uFlags; + hSubMenu->nDepth = hMenu->nDepth + 1; + hSubMenu->parent = hMenu; + if (path) + hSubMenu->path = path; + hMenu->children.push_back(hSubMenu); + return TRUE; +} + +BOOL APIENTRY +DestroyFakeMenu(HFAKEMENU hMenu) +{ + for (size_t i = 0; i < hMenu->children.size(); i++) + { + DestroyFakeMenu(hMenu->children[i]); + } + if (hMenu->hIcon) + DestroyIcon(hMenu->hIcon); + if (hMenu->hIconSm) + DestroyIcon(hMenu->hIconSm); + hMenu->~FAKEMENU(); + return HeapFree(GetProcessHeap(), 0, hMenu); +} + +HFAKEMENU APIENTRY +GetSubFakeMenu(HFAKEMENU hMenu, INT nPos) +{ + if (hMenu->uFlags & MF_POPUP) + { + if (0 <= nPos && nPos < (INT)hMenu->children.size()) + return hMenu->children[(size_t)nPos]; + } + return NULL; +} + +static VOID +FakeMenu_ChooseLocation(HWND hwnd, + INT x, INT y, INT cx, INT cy, + LPPOINT ppt, + INT nReserved, + LPCRECT prc) +{ + ppt->x = x; + ppt->y = y; + HMONITOR hmon = MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL); + if (hmon == NULL) + { + hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + } + + MONITORINFO minf; + minf.cbSize = sizeof(minf); + GetMonitorInfo(hmon, &minf); + + if ((DWORD)nReserved == 0xDEADFACE && prc) + { + RECT rc; + rc.left = prc->left; + rc.top = prc->top - cy; + rc.right = rc.left + cx; + rc.bottom = rc.top + cy; + if (rc.top < minf.rcMonitor.top) + { + rc.top = prc->bottom; + rc.bottom = rc.top + cy; + } + if (minf.rcMonitor.right < rc.right) + { + rc.right = minf.rcMonitor.right; + rc.left = rc.right - cx; + } + if (rc.left < minf.rcMonitor.left) + { + rc.left = minf.rcMonitor.left; + rc.right = rc.left + cx; + } + ppt->x = rc.left; + ppt->y = rc.top; + } + else if (prc == NULL) + { + if (ppt->y > minf.rcMonitor.bottom - cy) + { + ppt->y = minf.rcMonitor.bottom - cy; + } + + if (ppt->x > minf.rcMonitor.right - cx) + { + ppt->x = ppt->x - cx; + } + + if (ppt->y < minf.rcMonitor.top) + { + ppt->y = minf.rcMonitor.top; + } + + if (ppt->x < minf.rcMonitor.left) + { + ppt->x = minf.rcMonitor.left; + } + } + else + { + RECT rc; + rc.left = prc->right; + rc.top = prc->top; + rc.right = rc.left + cx; + rc.bottom = rc.top + cy; + if (minf.rcMonitor.right < rc.right) + { + rc.left = prc->left - cx; + rc.right = prc->left; + } + if (minf.rcMonitor.bottom < rc.bottom) + { + rc.bottom = minf.rcMonitor.bottom; + rc.top = rc.bottom - cy; + } + if (rc.top < minf.rcMonitor.top) + { + rc.top = minf.rcMonitor.top; + rc.bottom = rc.top + cy; + } + ppt->x = rc.left; + ppt->y = rc.top; + } +} + +static BOOL +FakeMenu_GetItemRect(HFAKEMENU hMenu, HWND hwnd, INT i, LPRECT prc) +{ + RECT rc, rcClient; + GetClientRect(hwnd, &rcClient); + + rc.left = rcClient.left + hMenu->sizBitmap.cx; + rc.right = rcClient.right; + + INT y = 0, cy; + INT j, nCount = GetFakeMenuItemCount(hMenu); + for (j = 0; j < nCount; j++) + { + if (hMenu->children[j]->uFlags & MF_SEPARATOR) + cy = hMenu->cySep; + else + cy = hMenu->cyItem; + rc.top = rcClient.top + y; + rc.bottom = rc.top + cy; + if (i == j) + { + *prc = rc; + return TRUE; + } + y += cy; + } + + return FALSE; +} + +static VOID +FakeMenu_CalcClientSize(HFAKEMENU hMenu, LPSIZE psiz) +{ + HDC hdc = CreateCompatibleDC(NULL); + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + hMenu->cySep = 4; + + BITMAP bm; + hMenu->sizBitmap.cx = hMenu->sizBitmap.cy = 0; + if (hMenu->hbm && GetObject(hMenu->hbm, sizeof(bm), &bm)) + { + if (hMenu->nIconSize == 32) + { + hMenu->sizBitmap.cx = bm.bmWidth; + hMenu->sizBitmap.cy = bm.bmHeight; + } + } + + SIZE siz; + INT cx = 0, cy = 0; + INT i, nCount = GetFakeMenuItemCount(hMenu); + for (i = 0; i < nCount; i++) + { + if (hMenu->children[i]->uFlags & MF_SEPARATOR) + { + siz.cx = 0; + siz.cy = hMenu->cySep; + } + else + { + GetTextExtentPoint32(hdc, + hMenu->children[i]->text.c_str(), + hMenu->children[i]->text.size(), + &siz); + if (hMenu->nIconSize == 32) + { + if (siz.cy < 32 + 4) + siz.cy = 32 + 4; + } + else + { + if (siz.cy < 16 + 4) + siz.cy = 16 + 4; + } + } + if (cx < siz.cx) + cx = siz.cx; + cy += siz.cy; + } + DeleteDC(hdc); + + if (hMenu->nIconSize == 32) + cx += hMenu->sizBitmap.cx + 32 + 4 + 32; + else + cx += hMenu->sizBitmap.cx + 16 + 4 + 16; + hMenu->cyItem = siz.cy; + psiz->cx = cx; + psiz->cy = cy; +} + +BOOL APIENTRY +TrackPopupFakeMenuNoLock(HFAKEMENU hMenu, + UINT uFlags, + INT x, INT y, + INT nReserved, + HWND hwndOwner, + LPCRECT prc) +{ + if (GetCurrentThreadId() != GetWindowThreadProcessId(hwndOwner, NULL)) + { + return FALSE; + } + + hMenu->uSelected = 0xFFFFFFFF; + hMenu->selected = NULL; + hMenu->result = NULL; + hMenu->uPopupped = 0xFFFFFFFF; + hMenu->popupped = NULL; + hMenu->hwndOwner = hwndOwner; + + DWORD dwStyle = WS_POPUP | WS_BORDER; + DWORD dwExStyle = WS_EX_TOOLWINDOW | + WS_EX_DLGMODALFRAME | + WS_EX_WINDOWEDGE | + WS_EX_TOPMOST; + + SIZE siz; + FakeMenu_CalcClientSize(hMenu, &siz); + + RECT rc; + rc.left = 0; + rc.top = 0; + rc.right = siz.cx; + rc.bottom = siz.cy; + AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle); + + POINT pt; + siz.cx = rc.right - rc.left; + siz.cy = rc.bottom - rc.top; + FakeMenu_ChooseLocation(hwndOwner, x, y, siz.cx, siz.cy, &pt, nReserved, prc); + + if (hMenu->hwnd == NULL) + { + hMenu->hwnd = CreateWindowExW(dwExStyle, + g_szFakeMenuClsName, + NULL, + dwStyle, + pt.x, pt.y, + siz.cx, siz.cy, + hwndOwner, + NULL, + shell32_hInstance, + hMenu); + ShowWindow(hMenu->hwnd, SW_SHOW); + } + else + { + MoveWindow(hMenu->hwnd, + pt.x, pt.y, siz.cx, siz.cy, + FALSE); + ShowWindow(hMenu->hwnd, SW_SHOW); + SetCapture(hMenu->hwnd); + + g_hTopMenu = hMenu; + } + + return TRUE; +} + +INT FakeMenu_OnCreate(HWND hwnd, HFAKEMENU hMenu) +{ + hMenu->hwnd = hwnd; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)hMenu); + return 0; +} + +VOID FakeMenu_OnPaint(HFAKEMENU hMenu, HWND hwnd) +{ + UINT uCount = hMenu->children.size(); + if (!uCount) + return; + + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + if (hdc == NULL) + return; + + INT y, cy; + RECT rcClient, rc, rcIntersect; + GetClientRect(hwnd, &rcClient); + + if (hMenu->hbm != NULL && hMenu->nIconSize == 32) + { + /* fill the banner background */ + HBRUSH hbr = CreateSolidBrush(RGB(138, 178, 219)); + if (hbr != NULL) + { + rcClient.right = rcClient.left + hMenu->sizBitmap.cx; + FillRect(hdc, &rcClient, hbr); + DeleteObject(hbr); + } + GetClientRect(hwnd, &rcClient); + rcClient.left += hMenu->sizBitmap.cx; + + /* draw the banner */ + HDC hdcMem = CreateCompatibleDC(hdc); + if (hdcMem) + { + HGDIOBJ hbmOld = SelectObject(hdcMem, hMenu->hbm); + BitBlt(hdc, + 0, rcClient.bottom - hMenu->sizBitmap.cy, + hMenu->sizBitmap.cx, hMenu->sizBitmap.cy, + hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + GdiFlush(); + DeleteDC(hdcMem); + } + } + + y = 0; + SetBkMode(hdc, TRANSPARENT); + HPEN hGrayPen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128)); + HPEN hLtGrayPen = CreatePen(PS_SOLID, 1, RGB(192, 192, 192)); + for (UINT i = 0; i < uCount; i++) + { + HFAKEMENU hChildMenu = hMenu->children[i]; + if (hChildMenu->uFlags & MF_SEPARATOR) + { + /* draw separator */ + cy = hMenu->cySep; + HGDIOBJ hPenOld = SelectObject(hdc, hGrayPen); + MoveToEx(hdc, + rcClient.left + 4, + rcClient.top + y + hMenu->cySep / 2, + NULL); + LineTo(hdc, + rcClient.right - 4, + rcClient.top + y + hMenu->cySep / 2); + + SelectObject(hdc, hLtGrayPen); + MoveToEx(hdc, + rcClient.left + 4 + 1, + rcClient.top + y + hMenu->cySep / 2 + 1, + NULL); + LineTo(hdc, + rcClient.right - 4 + 1, + rcClient.top + y + hMenu->cySep / 2 + 1); + SelectObject(hdc, hPenOld); + } + else + { + cy = hMenu->cyItem; + rc.left = rcClient.left; + rc.top = rcClient.top + y; + rc.right = rcClient.right; + rc.bottom = rc.top + cy; + if (IntersectRect(&rcIntersect, &rc, &ps.rcPaint)) + { + /* fill background */ + if (hMenu->uSelected == i || hMenu->uPopupped == i) + { + FillRect(hdc, &rc, (HBRUSH)(COLOR_HIGHLIGHT + 1)); + } + else + { + FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE + 1)); + } + + /* draw icon */ + if (hMenu->nIconSize == 32) + { + if (hChildMenu->hIcon == NULL && !hChildMenu->path.empty()) + { + SHFILEINFO sfi; + HIMAGELIST himl = reinterpret_cast( + SHGetFileInfo(hChildMenu->path.c_str(), + 0, + &sfi, + sizeof(sfi), + SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX)); + hChildMenu->hIcon = ImageList_ExtractIcon(0, himl, sfi.iIcon); + } + if (hChildMenu->hIcon) + { + DrawIconEx(hdc, + rc.left + (hMenu->cyItem - hMenu->nIconSize) / 2, + rc.top + (hMenu->cyItem - hMenu->nIconSize) / 2, + hChildMenu->hIcon, hMenu->nIconSize, hMenu->nIconSize, + 0, NULL, DI_NORMAL); + } + } + else + { + if (hChildMenu->hIconSm == NULL && !hChildMenu->path.empty()) + { + SHFILEINFO sfi; + HIMAGELIST himl = reinterpret_cast( + SHGetFileInfo(hChildMenu->path.c_str(), + 0, + &sfi, + sizeof(sfi), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SYSICONINDEX)); + hChildMenu->hIconSm = ImageList_ExtractIcon(0, himl, sfi.iIcon); + } + if (hChildMenu->hIconSm != NULL) + { + DrawIconEx(hdc, + rc.left + (hMenu->cyItem - hMenu->nIconSize) / 2, + rc.top + (hMenu->cyItem - hMenu->nIconSize) / 2, + hChildMenu->hIconSm, hMenu->nIconSize, hMenu->nIconSize, + 0, NULL, DI_NORMAL); + } + } + rc.left += hMenu->nIconSize + 4; + + if (hChildMenu->uFlags & MF_POPUP) + { + /* draw triangle */ + LOGFONTW lf; + ZeroMemory(&lf, sizeof(lf)); + lf.lfHeight = cy * 2 / 3; + lf.lfCharSet = DEFAULT_CHARSET; + lstrcpyW(lf.lfFaceName, L"Marlett"); + if (hMenu->uSelected == i || hMenu->uPopupped == i) + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + else + SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT)); + HGDIOBJ hFontOld = SelectObject(hdc, CreateFontIndirectW(&lf)); + TextOutW(hdc, rcClient.right - cy * 2 / 3, y + cy / 6, L"8", 1); + DeleteObject(SelectObject(hdc, hFontOld)); + } + + /* draw text */ + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + if (hChildMenu->uFlags & MF_GRAYED) + SetTextColor(hdc, RGB(255, 255, 255)); + else if (hMenu->uSelected == i || hMenu->uPopupped == i) + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + else + SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT)); + DrawText(hdc, hChildMenu->text.c_str(), -1, &rc, + DT_LEFT | DT_VCENTER | DT_SINGLELINE); + if (hChildMenu->uFlags & MF_GRAYED) + { + OffsetRect(&rc, 1, 1); + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + DrawTextW(hdc, hChildMenu->text.c_str(), -1, &rc, + DT_LEFT | DT_VCENTER | DT_SINGLELINE); + } + } + } + y += cy; + } + + DeleteObject(hGrayPen); + DeleteObject(hLtGrayPen); + EndPaint(hwnd, &ps); +} + +static VOID +FakeMenu_ChangeSel(HFAKEMENU hMenu, HWND hwnd, UINT uSelected) +{ + if (hMenu->uSelected != uSelected) + { + UINT uSelectedOld = hMenu->uSelected; + hMenu->uSelected = uSelected; + if (uSelected != 0xFFFFFFFF) + hMenu->selected = hMenu->children[uSelected]; + else + hMenu->selected = NULL; + + RECT rc; + if (uSelectedOld != 0xFFFFFFFF) + { + FakeMenu_GetItemRect(hMenu, hwnd, uSelectedOld, &rc); + InvalidateRect(hwnd, &rc, FALSE); + } + if (uSelected != 0xFFFFFFFF) + { + FakeMenu_GetItemRect(hMenu, hwnd, uSelected, &rc); + InvalidateRect(hwnd, &rc, FALSE); + } + } +} + +static UINT +FakeMenu_HitTest(HFAKEMENU hMenu, POINT pt) +{ + RECT rc; + GetClientRect(hMenu->hwnd, &rc); + if (PtInRect(&rc, pt)) + { + for (UINT i = 0; i < hMenu->children.size(); i++) + { + FakeMenu_GetItemRect(hMenu, hMenu->hwnd, i, &rc); + if (PtInRect(&rc, pt)) + { + return i; + } + } + } + return 0xFFFFFFFF; +} + +VOID FakeMenu_OnMouseMove(HFAKEMENU hMenu, HWND hwnd, INT x, INT y) +{ + POINT pt = {x, y}; + UINT uSelected = FakeMenu_HitTest(hMenu, pt); + FakeMenu_ChangeSel(hMenu, hwnd, uSelected); +} + +VOID FakeMenu_OnLButtonUp(HFAKEMENU hMenu, HWND hwnd, INT x, INT y) +{ + FakeMenu_OnMouseMove(hMenu, hwnd, x, y); + + if (hMenu->uSelected != 0xFFFFFFFF && hMenu->selected) + { + RECT rc; + HFAKEMENU hSubMenu = hMenu->selected; + if (hSubMenu->uFlags & MF_SEPARATOR) + return; + + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), + reinterpret_cast(hMenu)); + + if (hMenu->popupped && IsWindow(hMenu->popupped->hwnd)) + { + DestroyWindow(hMenu->popupped->hwnd); + + UINT uPopuppedOld = hMenu->uPopupped; + hMenu->popupped = NULL; + hMenu->uPopupped = 0xFFFFFFFF; + FakeMenu_GetItemRect(hMenu, hwnd, uPopuppedOld, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + if (uPopuppedOld == hMenu->uSelected) + { + SetActiveWindow(hMenu->hwnd); + return; + } + } + + if (hSubMenu->uFlags & MF_POPUP) + { + hMenu->popupped = hSubMenu; + hMenu->uPopupped = hMenu->uSelected; + FakeMenu_GetItemRect(hMenu, hwnd, hMenu->uPopupped, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + TrackPopupFakeMenuNoLock(hSubMenu, TPM_LEFTBUTTON, + rc.left, rc.top, 1, hwnd, &rc); + } + else if (!(hSubMenu->uFlags & MF_GRAYED)) + { + g_hTopMenu->result = hMenu->selected; + SendMessage(g_hTopMenu->hwnd, WM_CLOSE, 0, 0); + } + } + else + { + RECT rc; + HFAKEMENU h = hMenu; + POINT pt = {x, y}; + ClientToScreen(hwnd, &pt); + + while (h) + { + GetClientRect(h->hwnd, &rc); + MapWindowPoints(h->hwnd, NULL, (LPPOINT)&rc, 2); + if (PtInRect(&rc, pt)) + { + while (hMenu != h) + { + if (IsWindow(hMenu->hwnd)) + DestroyWindow(hMenu->hwnd); + if (hMenu->parent == NULL) + break; + hMenu = hMenu->parent; + } + SetActiveWindow(h->hwnd); + return; + } + h = h->parent; + } + + g_hTopMenu->result = NULL; + SendMessage(g_hTopMenu->hwnd, WM_CLOSE, 0, 0); + } +} + +VOID FakeMenu_OnKeyDown(HFAKEMENU hMenu, HWND hwnd, INT vk) +{ + RECT rc; + UINT uSelected, uCount = (UINT)GetFakeMenuItemCount(hMenu); + switch(vk) + { + case VK_ESCAPE: + if (hMenu->parent) + { + if (IsWindow(hMenu->hwnd)) + DestroyWindow(hMenu->hwnd); + SetActiveWindow(hMenu->parent->hwnd); + } + else + { + g_hTopMenu->result = NULL; + SendMessage(g_hTopMenu->hwnd, WM_CLOSE, 0, 0); + } + break; + + case VK_RETURN: + uSelected = hMenu->uSelected; + if (uSelected != 0xFFFFFFFF && hMenu->selected) + { + if (hMenu->popupped && IsWindow(hMenu->popupped->hwnd)) + { + DestroyWindow(hMenu->popupped->hwnd); + + UINT uPopuppedOld = hMenu->uPopupped; + hMenu->popupped = NULL; + hMenu->uPopupped = 0xFFFFFFFF; + FakeMenu_GetItemRect(hMenu, hwnd, uPopuppedOld, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + if (uPopuppedOld == uSelected) + { + SetActiveWindow(hMenu->hwnd); + return; + } + } + + if (hMenu->selected->uFlags & MF_POPUP) + { + HFAKEMENU hSubMenu = hMenu->selected; + + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), + reinterpret_cast(hMenu)); + + hMenu->popupped = hSubMenu; + hMenu->uPopupped = uSelected; + FakeMenu_GetItemRect(hMenu, hwnd, uSelected, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + TrackPopupFakeMenuNoLock(hSubMenu, TPM_LEFTBUTTON, + rc.left, rc.top, 1, hwnd, &rc); + } + else + { + g_hTopMenu->result = hMenu->selected; + SendMessage(g_hTopMenu->hwnd, WM_CLOSE, 0, 0); + } + } + break; + + case VK_UP: + uSelected = hMenu->uSelected; + if (uSelected == 0xFFFFFFFF) + { + uSelected = uCount - 1; + } + else + { + do + { + if (hMenu->uSelected > 0) + { + uSelected--; + } + else + { + uSelected = uCount - 1; + } + } while (hMenu->children[uSelected]->uFlags & MF_SEPARATOR); + } + FakeMenu_ChangeSel(hMenu, hwnd, uSelected); + break; + + case VK_DOWN: + uSelected = hMenu->uSelected; + if (uSelected == 0xFFFFFFFF) + { + uSelected = 0; + } + else + { + do + { + if (hMenu->uSelected + 1 < uCount) + { + uSelected++; + } + else + { + uSelected = 0; + } + } while (hMenu->children[uSelected]->uFlags & MF_SEPARATOR); + } + FakeMenu_ChangeSel(hMenu, hwnd, uSelected); + break; + + case VK_LEFT: + if (hMenu->parent) + { + if (IsWindow(hMenu->hwnd)) + DestroyWindow(hMenu->hwnd); + SetActiveWindow(hMenu->parent->hwnd); + } + break; + + case VK_RIGHT: + uSelected = hMenu->uSelected; + if (uSelected != 0xFFFFFFFF && hMenu->selected) + { + if (hMenu->popupped && IsWindow(hMenu->popupped->hwnd)) + { + DestroyWindow(hMenu->popupped->hwnd); + + UINT uPopuppedOld = hMenu->uPopupped; + hMenu->popupped = NULL; + hMenu->uPopupped = 0xFFFFFFFF; + FakeMenu_GetItemRect(hMenu, hwnd, uPopuppedOld, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + if (uPopuppedOld == uSelected) + { + SetActiveWindow(hMenu->hwnd); + return; + } + } + + if (hMenu->selected->uFlags & MF_POPUP) + { + HFAKEMENU hSubMenu = hMenu->selected; + + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), + reinterpret_cast(hMenu)); + + hMenu->popupped = hSubMenu; + hMenu->uPopupped = uSelected; + FakeMenu_GetItemRect(hMenu, hwnd, hMenu->uPopupped, &rc); + InvalidateRect(hwnd, &rc, FALSE); + + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + TrackPopupFakeMenuNoLock(hSubMenu, TPM_LEFTBUTTON, + rc.left, rc.top, 1, hwnd, &rc); + } + } + break; + } +} + +static LPITEMIDLIST +PidlFromPath(HWND hwnd, LPCWSTR pszPath, IShellFolder *psfFolder) +{ + LPITEMIDLIST pidl; + ULONG ulEaten; + DWORD dwAttributes; + HRESULT hres; + + hres = psfFolder->ParseDisplayName(hwnd, NULL, + const_cast(pszPath), + &ulEaten, &pidl, &dwAttributes); + if (FAILED(hres)) + return NULL; + return pidl; +} + +VOID FakeMenu_OnContextMenu(HFAKEMENU hMenu, + HWND hwnd, + INT x, INT y) +{ + { + POINT pt = {x, y}; + ScreenToClient(hwnd, &pt); + FakeMenu_OnMouseMove(hMenu, hwnd, pt.x, pt.y); + } + + if (hMenu->uSelected == 0xFFFFFFFF || hMenu->selected == NULL) + return; + + RECT rc; + if (x == -1 && y == -1) + { + FakeMenu_GetItemRect(hMenu, hwnd, hMenu->uSelected, &rc); + x = (rc.left + rc.right) / 2; + y = (rc.top + rc.bottom) / 2; + } + + if (hMenu->selected->path.empty()) + return; + + IShellFolder *psfFolder; + if (FAILED(SHGetDesktopFolder(&psfFolder))) + return; + + LPITEMIDLIST pidl; + pidl = PidlFromPath(hwnd, hMenu->selected->path.c_str(), psfFolder); + if (pidl) + { + IContextMenu *pcm = NULL; + HRESULT hr = psfFolder->GetUIObjectOf(hwnd, 1, const_cast(&pidl), + IID_IContextMenu, NULL, + reinterpret_cast(&pcm)); + if (SUCCEEDED(hr)) + { + HMENU hPopup = CreatePopupMenu(); + hr = pcm->QueryContextMenu(hPopup, 0, 1, 0x7fff, CMF_NORMAL | CMF_EXPLORE); + if (SUCCEEDED(hr)) + { + UINT idCmd = TrackPopupMenu(hPopup, + TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, + x, y, 0, hwnd, NULL); + if (idCmd != 0) + { + CMINVOKECOMMANDINFO cmi; + cmi.cbSize = sizeof(cmi); + cmi.fMask = 0; + cmi.hwnd = GetDesktopWindow(); + cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - 1); + cmi.lpParameters = NULL; + cmi.lpDirectory = NULL; + cmi.nShow = SW_SHOWNORMAL; + cmi.dwHotKey = 0; + cmi.hIcon = NULL; + hr = pcm->InvokeCommand(&cmi); + if (SUCCEEDED(hr)) + { + ; + } + SendMessage(g_hTopMenu->hwnd, WM_CLOSE, 0, 0); + } + } + DestroyMenu(hPopup); + pcm->Release(); + } + CoTaskMemFree(pidl); + } + psfFolder->Release(); +} + +static LRESULT CALLBACK +FakeMenu_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HFAKEMENU hMenu = + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + + switch(uMsg) + { + case WM_CREATE: + return FakeMenu_OnCreate( + hwnd, + (HFAKEMENU)((LPCREATESTRUCT)lParam)->lpCreateParams); + + case WM_MOUSEMOVE: + FakeMenu_OnMouseMove(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + FakeMenu_OnLButtonUp(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + case WM_CONTEXTMENU: + FakeMenu_OnContextMenu(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + FakeMenu_OnKeyDown(hMenu, hwnd, (INT)wParam); + break; + + case WM_MOUSEACTIVATE: + return MA_NOACTIVATE; + + case WM_PAINT: + FakeMenu_OnPaint(hMenu, hwnd); + break; + + case WM_MENUSELECT: + SendMessage(GetParent(hwnd), uMsg, wParam, lParam); + break; + + case WM_COMMAND: + SendMessage(GetParent(hwnd), uMsg, wParam, lParam); + break; + + case WM_NCDESTROY: + hMenu->hwnd = NULL; + hMenu->uSelected = 0xFFFFFFFF; + hMenu->selected = NULL; + hMenu->popupped = NULL; + hMenu->uPopupped = 0xFFFFFFFF; + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + return 0; +} + +BOOL APIENTRY +FakeMenu_Register(HINSTANCE hInstance) +{ + WNDCLASSEXW wc = { sizeof(wc) }; + wc.lpfnWndProc = FakeMenu_WndProc; + wc.hInstance = hInstance; + wc.hCursor = + reinterpret_cast(LoadImageW(NULL, IDC_ARROW, IMAGE_CURSOR, + 0, 0, LR_DEFAULTSIZE)); + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.lpszClassName = g_szFakeMenuClsName; + return RegisterClassEx(&wc) != 0; +} + +HFAKEMENU APIENTRY +CreatePopupFakeMenu(VOID) +{ + HFAKEMENU hMenu; + hMenu = reinterpret_cast(HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(FAKEMENU))); + + ::new(reinterpret_cast(hMenu)) FAKEMENU; + hMenu->uFlags = MF_ENABLED | MF_POPUP; + hMenu->nIconSize = 16; + hMenu->parent = NULL; + hMenu->uSelected = 0xFFFFFFFF; + hMenu->uPopupped = 0xFFFFFFFF; + + return hMenu; +} Index: dll/win32/shell32/fakemenu.h =================================================================== --- dll/win32/shell32/fakemenu.h (revision 0) +++ dll/win32/shell32/fakemenu.h (working copy) @@ -0,0 +1,94 @@ +/* + * Fakemenus (menu-like windows) + * + * 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef FAKEMENU_H +#define FAKEMENU_H + +#include +#include + +#include "tiny.h" + +struct FAKEMENU +{ + HWND hwnd; + HWND hwndOwner; + UINT uID; + UINT uFlags; + UINT uSelected; + FAKEMENU *selected; + FAKEMENU *parent; + FAKEMENU *result; + UINT uPopupped; + FAKEMENU *popupped; + CTinyString text; + CTinyVector children; + INT cyItem; + INT cySep; + INT nDepth; + HICON hIcon; + HICON hIconSm; + CTinyString path; + INT nIconSize; + HBITMAP hbm; + SIZE sizBitmap; +}; +typedef FAKEMENU *HFAKEMENU; + +BOOL APIENTRY +FakeMenu_Register(HINSTANCE hInstance); + +HFAKEMENU APIENTRY +CreatePopupFakeMenu(VOID); + +BOOL APIENTRY +DestroyFakeMenu(HFAKEMENU hMenu); + +INT APIENTRY +GetFakeMenuItemCount(HFAKEMENU hMenu); + +HFAKEMENU APIENTRY +GetSubFakeMenu(HFAKEMENU hMenu, INT nPos); + +UINT APIENTRY +GetFakeMenuItemID(HFAKEMENU hMenu, INT nPos); + +BOOL APIENTRY +AppendFakeMenu(HFAKEMENU hMenu, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCWSTR lpNewItem, + LPCWSTR path = NULL); + +BOOL APIENTRY +TrackPopupFakeMenuNoLock(HFAKEMENU hMenu, + UINT uFlags, + INT x, INT y, + INT nReserved, + HWND hwndOwner, + LPCRECT prc); + +INT FakeMenu_OnCreate(HWND hwnd, HFAKEMENU hMenu); +VOID FakeMenu_OnMouseMove(HFAKEMENU hMenu, HWND hwnd, INT x, INT y); +VOID FakeMenu_OnLButtonUp(HFAKEMENU hMenu, HWND hwnd, INT x, INT y); +VOID FakeMenu_OnKeyDown(HFAKEMENU hMenu, HWND hwnd, INT vk); +VOID FakeMenu_OnPaint(HFAKEMENU hMenu, HWND hwnd); +VOID FakeMenu_OnContextMenu(HFAKEMENU hMenu, HWND hwnd, INT x, INT y); + +#endif /* FAKEMENU_H */ Index: dll/win32/shell32/icon_res.rc =================================================================== --- dll/win32/shell32/icon_res.rc (revision 58817) +++ dll/win32/shell32/icon_res.rc (working copy) @@ -72,6 +72,8 @@ IDI_SHELL_SINGLE_CLICK_TO_OPEN ICON "res/icons/186.ico" IDI_SHELL_DOUBLE_CLICK_TO_OPEN ICON "res/icons/187.ico" +IDI_RESTART_ICON ICON "res/icons/188.ico" + /* TODO: 174.ico, 175.ico, 176.ico, 177.ico, 178.ico, 179.ico, 180.ico, 181.ico */ IDI_SHELL_EMPTY_RECYCLE_BIN1 ICON "res/icons/191.ico" IDI_SHELL_FULL_RECYCLE_BIN1 ICON "res/icons/192.ico" Index: dll/win32/shell32/lang/en-US.rc =================================================================== --- dll/win32/shell32/lang/en-US.rc (revision 58817) +++ dll/win32/shell32/lang/en-US.rc (working copy) @@ -805,4 +805,27 @@ IDS_BYTES_FORMAT "bytes" IDS_UNKNOWN_APP "Unknown application" IDS_EXE_DESCRIPTION "Description:" + + IDS_SM_PROGRAMS "Programs" + IDS_SM_MYDOCUMENTS "My Documents" + IDS_SM_RECENTDOCUMENTS "Recent 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_RESTART "Restart" + IDS_SM_SHUTDOWN "Shutdown" + IDS_SM_CONTROLPANEL "Control Panel" + IDS_SM_NETWORKCONNECTIONS "Network Connections" + IDS_SM_PRINTERSANDFAXES "Printers and Faxes" + IDS_SM_TASKBARANDSTARTMENU "Taskbar and Start Menu" + IDS_SM_FORFILESANDFOLDERS "For Files or Folders" + IDS_SM_ONTHEINTERNET "On the Internet..." + IDS_SM_FORPEOPLE "For People..." + + IDS_EMPTY "(Empty)" + IDS_ACCESSDENIED "(Access Denied)" + IDS_YOUSELECTEDRESTART "You selected restart." END Index: dll/win32/shell32/shresdef.h =================================================================== --- dll/win32/shell32/shresdef.h (revision 58817) +++ dll/win32/shell32/shresdef.h (working copy) @@ -21,6 +21,8 @@ #define IDC_STATIC -1 +#define IDM_RESTART 1000 + /* * Bitmaps */ @@ -184,6 +186,30 @@ #define IDS_DESCRIPTION 331 #define IDS_COPY_OF 332 +/* Start Menu*/ +#define IDS_SM_PROGRAMS 333 +#define IDS_SM_MYDOCUMENTS 334 +#define IDS_SM_RECENTDOCUMENTS 335 +#define IDS_SM_FAVORITES 336 +#define IDS_SM_SETTINGS 337 +#define IDS_SM_SEARCH 338 +#define IDS_SM_HELP_AND_SUPPORT 339 +#define IDS_SM_RUN 340 +#define IDS_SM_LOGOFF 341 +#define IDS_SM_RESTART 342 +#define IDS_SM_SHUTDOWN 343 +#define IDS_SM_CONTROLPANEL 344 +#define IDS_SM_NETWORKCONNECTIONS 345 +#define IDS_SM_PRINTERSANDFAXES 346 +#define IDS_SM_TASKBARANDSTARTMENU 347 +#define IDS_SM_FORFILESANDFOLDERS 348 +#define IDS_SM_ONTHEINTERNET 349 +#define IDS_SM_FORPEOPLE 350 + +#define IDS_EMPTY 351 +#define IDS_ACCESSDENIED 352 +#define IDS_YOUSELECTEDRESTART 353 + /* Note: those strings are referenced from the registry */ #define IDS_RECYCLEBIN_FOLDER_NAME 8964 #define IDS_ADMINISTRATIVETOOLS 22982 @@ -454,6 +480,8 @@ #define IDI_SHELL_SINGLE_CLICK_TO_OPEN 186 #define IDI_SHELL_DOUBLE_CLICK_TO_OPEN 187 +#define IDI_RESTART_ICON 188 + /* * AVI resources * Index: dll/win32/shell32/startmenu.cpp =================================================================== --- dll/win32/shell32/startmenu.cpp (revision 58821) +++ 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,9 +23,450 @@ 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 WCHAR szStartMenuWndClass[] = L"DV2ControlHost"; + +static LPWSTR +LoadStringDx(INT id) +{ + static WCHAR sz[512]; + LoadStringW(shell32_hInstance, id, sz, 512); + return sz; +} + +static HICON +LoadIconDx(INT id) +{ + return (HICON)LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(id), + IMAGE_ICON, 32, 32, 0); +} + +static HICON +LoadIconSmDx(INT id) +{ + return (HICON)LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(id), + IMAGE_ICON, 16, 16, 0); +} + +static HWND +GetTrayWnd(VOID) +{ + HWND hTrayWnd; + hTrayWnd = FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL); + return hTrayWnd; +} + +static VOID +PostTrayCommand(UINT nCommandID) +{ + PostMessageW(GetTrayWnd(), WM_COMMAND, MAKEWPARAM(nCommandID, 0), 0); +} + +static bool +GetFileList(LPCWSTR pszDir, CTinyVector& list) +{ + CTinyString str(pszDir), str2; + FILEITEMDATA data; + + str2 = str + L"\\*"; + + WIN32_FIND_DATAW find; + HANDLE hFind = FindFirstFileW(str2.c_str(), &find); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (lstrcmpW(find.cFileName, L".") != 0 && + lstrcmpW(find.cFileName, L"..") != 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 (FindNextFileW(hFind, &find)); + FindClose(hFind); + return true; + } + return GetLastError() == ERROR_SUCCESS; +} + +static VOID +AddBackslash(CTinyString& str) +{ + size_t size = str.size(); + if (size > 0 && str[size - 1] != L'\\') + str += L"\\"; +} + +static VOID +AddPathToFakeMenu(CStartMenu *pMenu, HFAKEMENU hMenu, LPCWSTR pszPath) +{ + INT nCount = 0; + size_t i = pMenu->m_list.size(); + + if (!GetFileList(pszPath, pMenu->m_list) && GetLastError() == ERROR_ACCESS_DENIED) + { + AppendFakeMenu(hMenu, + MF_GRAYED | MF_STRING, + 0, + LoadStringDx(IDS_ACCESSDENIED)); + return; + } + + CTinyString path, name; + for (; i < pMenu->m_list.size(); i++) + { + path = pMenu->m_list[i].path; + AddBackslash(path); + path += pMenu->m_list[i].name; + + if (pMenu->m_list[i].is_folder) + { + HFAKEMENU hSubMenu = CreatePopupFakeMenu(); + if (hSubMenu != NULL) + { + pMenu->m_map.insert(make_pair(hSubMenu, path)); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, + pMenu->m_list[i].name.c_str(), + path.c_str()); + nCount++; + } + } + else + { + name = pMenu->m_list[i].name; + + /* remove .lnk */ + size_t size = name.size(); + if (size > 4) + { + if (lstrcmpiW(name.c_str() + size - 4, L".lnk") == 0) + name.resize(size - 4); + } + + AppendFakeMenu(hMenu, MF_STRING, i + 1, + name.c_str(), + path.c_str()); + nCount++; + } + } + + if (nCount == 0) + { + AppendFakeMenu(hMenu, MF_GRAYED | MF_STRING, 0, LoadStringDx(IDS_EMPTY)); + } +} + +static VOID +CreateSettingsMenu(HFAKEMENU hMenu) +{ + AppendFakeMenu(hMenu, MF_STRING, IDM_CONTROLPANEL, LoadStringDx(IDS_SM_CONTROLPANEL)); + AppendFakeMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendFakeMenu(hMenu, MF_STRING, IDM_NETWORKCONNECTIONS, LoadStringDx(IDS_SM_NETWORKCONNECTIONS)); + AppendFakeMenu(hMenu, MF_STRING, IDM_PRINTERSANDFAXES, LoadStringDx(IDS_SM_PRINTERSANDFAXES)); + AppendFakeMenu(hMenu, MF_STRING, IDM_TASKBARANDSTARTMENU, LoadStringDx(IDS_SM_TASKBARANDSTARTMENU)); + + UINT i = 0; + assert(i < hMenu->children.size()); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_CONTROL_PANEL); + assert(i < hMenu->children.size()); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_NETWORK); + assert(i < hMenu->children.size()); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_PRINTERS_FOLDER); + assert(i < hMenu->children.size()); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_TSKBAR_STARTMENU); +} + +static VOID +CreateSearchMenu(HFAKEMENU hMenu) +{ + AppendFakeMenu(hMenu, MF_STRING, IDM_SEARCH, LoadStringDx(IDS_SM_FORFILESANDFOLDERS)); + AppendFakeMenu(hMenu, MF_GRAYED | MF_STRING, -1, LoadStringDx(IDS_SM_ONTHEINTERNET)); + AppendFakeMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendFakeMenu(hMenu, MF_GRAYED | MF_STRING, -1, LoadStringDx(IDS_SM_FORPEOPLE)); + + UINT i = 0; + assert(i < hMenu->children.size()); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_FIND_IN_FILE); + assert(i < hMenu->children.size()); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_ENTIRE_NETWORK); + assert(i < hMenu->children.size()); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_USERS); +} + +static VOID +CreateProgramsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + WCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPathW(hMenu->hwnd, szPath, CSIDL_COMMON_PROGRAMS, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + SHGetSpecialFolderPathW(hMenu->hwnd, szPath, CSIDL_PROGRAMS, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateMyDocumentsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + WCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPathW(hMenu->hwnd, szPath, CSIDL_PERSONAL, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateRecentDocumentsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + WCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPathW(hMenu->hwnd, szPath, CSIDL_RECENT, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateFavoritesMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + WCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPathW(hMenu->hwnd, szPath, CSIDL_FAVORITES, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static void +StartMenu_OnMenuSelect( + HWND hwnd, + CStartMenu *pMenu, + WPARAM wParam, + LPARAM lParam) +{ + if (HIWORD(wParam) & MF_POPUP) + { + HFAKEMENU hSubMenu = + GetSubFakeMenu(reinterpret_cast(lParam), LOWORD(wParam)); + if (hSubMenu != NULL && GetFakeMenuItemCount(hSubMenu) == 0) + { + CTinyMap::iterator it; + it = pMenu->m_map.find(hSubMenu); + if (it != pMenu->m_map.end()) + { + AddPathToFakeMenu(pMenu, hSubMenu, it->second.c_str()); + } + } + } +} + +static VOID +StartMenu_OnCommand(HWND hwnd, CStartMenu *pMenu, WPARAM wParam, LPARAM lParam) +{ + if (HIWORD(wParam) != 0xDEAD) + return; + + HFAKEMENU hMenu = reinterpret_cast(lParam); + if (hMenu->result) + { + if (hMenu->result->path.empty()) + { + UINT nID = LOWORD(wParam); + switch(nID) + { + case IDM_LOGOFF: + LogoffWindowsDialog(GetDesktopWindow()); + return; + + case IDM_RESTART: + RestartDialog(GetDesktopWindow(), + LoadStringDx(IDS_YOUSELECTEDRESTART), + EWX_REBOOT); + return; + } + PostTrayCommand(nID); + } + else + { + ShellExecute(NULL, NULL, hMenu->result->path.c_str(), + NULL, NULL, SW_SHOWNORMAL); + } + } +} + +static INT +StartMenu_OnCreate(HWND hwnd, LPARAM lParam) +{ + LPCREATESTRUCT pcs = reinterpret_cast(lParam); + CStartMenu *pMenu = reinterpret_cast(pcs->lpCreateParams); + pMenu->m_hWnd = hwnd; + SetWindowLongPtr(hwnd, 0, (LONG_PTR)pMenu); + return 0; +} + +static VOID +StartMenu_OnClose(HWND hwnd, CStartMenu *pMenu, HFAKEMENU hMenu) +{ + if (hMenu->popupped && IsWindow(hMenu->popupped->hwnd)) + { + DestroyWindow(hMenu->popupped->hwnd); + hMenu->popupped = NULL; + } + + if (GetCapture() == hwnd) + ReleaseCapture(); + ShowWindow(hMenu->hwnd, SW_HIDE); + + if (hMenu->result) + { + SendMessage(hMenu->hwnd, WM_COMMAND, + MAKEWPARAM(hMenu->result->uID, 0xDEAD), + reinterpret_cast(hMenu)); + hMenu->result = NULL; + } +} + +static LRESULT CALLBACK +StartMenu_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CStartMenu *pMenu = + reinterpret_cast(GetWindowLongPtr(hwnd, 0)); + HFAKEMENU hMenu = + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + + switch(uMsg) + { + case WM_CREATE: + return StartMenu_OnCreate(hwnd, lParam); + + case WM_MOUSEMOVE: + FakeMenu_OnMouseMove(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + FakeMenu_OnLButtonUp(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + FakeMenu_OnKeyDown(hMenu, hwnd, (INT)wParam); + break; + + case WM_MOUSEACTIVATE: + return MA_NOACTIVATE; + + case WM_PAINT: + FakeMenu_OnPaint(hMenu, hwnd); + break; + + case WM_MENUSELECT: + StartMenu_OnMenuSelect(hwnd, pMenu, wParam, lParam); + break; + + case WM_COMMAND: + StartMenu_OnCommand(hwnd, pMenu, wParam, lParam); + break; + + case WM_CLOSE: + StartMenu_OnClose(hwnd, pMenu, hMenu); + break; + + case WM_DESTROY: + if (pMenu->m_hMenu) + { + DestroyFakeMenu(pMenu->m_hMenu); + pMenu->m_hMenu = NULL; + } + break; + + case WM_CONTEXTMENU: + FakeMenu_OnContextMenu(hMenu, hwnd, (SHORT)LOWORD(lParam), + (SHORT)HIWORD(lParam)); + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return 0; +} + +static BOOL +StartMenu_Register(VOID) +{ + WNDCLASSEXW 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); + + if (RegisterClassExW(&wcx) == 0) + return FALSE; + + return FakeMenu_Register(shell32_hInstance); +} + +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 CreateWindowExW(dwExStyle, szStartMenuWndClass, LoadStringDx(IDS_STARTMENU), + dwStyle, 0, 0, 0, 0, NULL, NULL, shell32_hInstance, pMenu); +} + CStartMenu::CStartMenu() : m_pBandSite(NULL), - m_pUnkSite(NULL) + m_pUnkSite(NULL), + m_iIcon(BMICON_LARGE), + m_hBitmap(NULL), + m_hMenu(NULL), + m_hWnd(NULL) { } @@ -40,8 +482,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 +517,113 @@ HRESULT STDMETHODCALLTYPE CStartMenu::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags) { - UNIMPLEMENTED; - return E_NOTIMPL; + HFAKEMENU hMenu, hSubMenu; + + m_map.clear(); + m_list.clear(); + + if (m_hMenu) + { + DestroyFakeMenu(m_hMenu); + m_hMenu = NULL; + } + + hMenu = CreatePopupFakeMenu(); + if (m_iIcon == BMICON_LARGE) + hMenu->nIconSize = 32; + else + hMenu->nIconSize = 16; + hMenu->hbm = m_hBitmap; + + hSubMenu = CreatePopupFakeMenu(); + CreateProgramsMenu(this, hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_PROGRAMS)); + + hSubMenu = CreatePopupFakeMenu(); + CreateMyDocumentsMenu(this, hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_MYDOCUMENTS)); + + hSubMenu = CreatePopupFakeMenu(); + CreateRecentDocumentsMenu(this, hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_RECENTDOCUMENTS)); + + hSubMenu = CreatePopupFakeMenu(); + CreateFavoritesMenu(this, hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_FAVORITES)); + + hSubMenu = CreatePopupFakeMenu(); + CreateSettingsMenu(hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_SETTINGS)); + + hSubMenu = CreatePopupFakeMenu(); + CreateSearchMenu(hSubMenu); + AppendFakeMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, LoadStringDx(IDS_SM_SEARCH)); + + AppendFakeMenu(hMenu, MF_STRING, IDM_HELPANDSUPPORT, LoadStringDx(IDS_SM_HELP_AND_SUPPORT)); + AppendFakeMenu(hMenu, MF_STRING, IDM_RUN, LoadStringDx(IDS_SM_RUN)); + AppendFakeMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendFakeMenu(hMenu, MF_STRING, IDM_LOGOFF, LoadStringDx(IDS_SM_LOGOFF)); + AppendFakeMenu(hMenu, MF_STRING, IDM_RESTART, LoadStringDx(IDS_SM_RESTART)); + AppendFakeMenu(hMenu, MF_STRING, IDM_SHUTDOWN, LoadStringDx(IDS_SM_SHUTDOWN)); + + UINT i = 0; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_PROGRAMS_FOLDER); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_PROGRAMS_FOLDER); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_MY_DOCUMENTS); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_MY_DOCUMENTS); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RECENT_DOCUMENTS); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RECENT_DOCUMENTS); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_FAVORITES); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_FAVORITES); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RUN); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RUN); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_SEARCH); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_SEARCH); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_HELP); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_HELP); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RUN2); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RUN2); + i++; + assert(i < hMenu->children.size()); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_LOGOFF); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_LOGOFF); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_RESTART_ICON); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_RESTART_ICON); + i++; + assert(i < hMenu->children.size()); + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_SHUTDOWN); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_SHUTDOWN); + + FakeMenu_OnCreate(m_hWnd, hMenu); + + m_hMenu = hMenu; + + TrackPopupFakeMenuNoLock(hMenu, + TPM_LEFTBUTTON, + ppt->x, ppt->y, + 0xDEADFACE, + GetTrayWnd(), + (LPRECT)prcExclude); + + return S_OK; } HRESULT STDMETHODCALLTYPE CStartMenu::SetSubMenu(IMenuPopup *pmp, BOOL fSet) @@ -112,6 +661,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 +671,186 @@ hr = pBandSiteObj->QueryInterface(IID_IBandSite, (VOID**)&m_pBandSite); if (FAILED(hr)) - return NULL; + return E_FAIL; return m_pBandSite->AddBand((IMenuBand*)this); } +static BOOL +IsSameOrDecendant(HWND hwnd, HWND hwndDecendant) +{ + if (hwnd == NULL || hwndDecendant == NULL) + return FALSE; + if (hwnd == hwndDecendant || IsChild(hwnd, hwndDecendant)) + return TRUE; + return IsSameOrDecendant(hwnd, GetParent(hwndDecendant)); +} + HRESULT STDMETHODCALLTYPE CStartMenu::IsMenuMessage(MSG *pmsg) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (pmsg->message == WM_CREATE) + { + TranslateMessage(pmsg); + DispatchMessage(pmsg); + return S_OK; + } + + if (IsWindow(m_hWnd) && m_hWnd == pmsg->hwnd) + { + HWND hwndActive; + HFAKEMENU h; + RECT rc; + POINT pt; + + hwndActive = GetActiveWindow(); + if (!IsSameOrDecendant(m_hWnd, hwndActive)) + { + SendMessage(m_hWnd, WM_CLOSE, 0, 0); + return E_FAIL; + } + + switch(pmsg->message) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + pt.x = (SHORT)LOWORD(pmsg->lParam); + pt.y = (SHORT)HIWORD(pmsg->lParam); + ClientToScreen(pmsg->hwnd, &pt); + h = m_hMenu; + do + { + GetClientRect(h->hwnd, &rc); + MapWindowPoints(h->hwnd, NULL, (LPPOINT)&rc, 2); + if (PtInRect(&rc, pt)) + { + pt.x = (SHORT)LOWORD(pmsg->lParam); + pt.y = (SHORT)HIWORD(pmsg->lParam); + MapWindowPoints(pmsg->hwnd, h->hwnd, &pt, 1); + pmsg->lParam = MAKELPARAM(pt.x, pt.y); + pmsg->hwnd = h->hwnd; + break; + } + h = h->popupped; + } while (h); + if (h == NULL) + { + pt.x = (SHORT)LOWORD(pmsg->lParam); + pt.y = (SHORT)HIWORD(pmsg->lParam); + MapWindowPoints(pmsg->hwnd, m_hWnd, &pt, 1); + pmsg->lParam = MAKELPARAM(pt.x, pt.y); + pmsg->hwnd = m_hWnd; + } + break; + + case WM_NCMOUSEMOVE: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCRBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_CONTEXTMENU: + pt.x = (SHORT)LOWORD(pmsg->lParam); + pt.y = (SHORT)HIWORD(pmsg->lParam); + h = m_hMenu; + do + { + GetClientRect(h->hwnd, &rc); + MapWindowPoints(h->hwnd, NULL, (LPPOINT)&rc, 2); + if (PtInRect(&rc, pt)) + { + pmsg->hwnd = h->hwnd; + break; + } + h = h->popupped; + } while (h); + if (h == NULL) + { + pmsg->hwnd = m_hWnd; + } + break; + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + pmsg->hwnd = hwndActive; + break; + } + + TranslateMessage(pmsg); + DispatchMessage(pmsg); + + hwndActive = GetActiveWindow(); + if (!IsSameOrDecendant(m_hWnd, hwndActive)) + { + SendMessage(m_hWnd, WM_CLOSE, 0, 0); + return S_OK; + } + + return S_OK; + } + + return E_FAIL; } HRESULT STDMETHODCALLTYPE CStartMenu::TranslateMenuMessage(MSG *pmsg, LRESULT *plRet) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (plRet == NULL) + return S_FALSE; + + *plRet = 0; + return CStartMenu::IsMenuMessage(pmsg); } +HRESULT STDMETHODCALLTYPE CStartMenu::SetIconSize(DWORD iIcon) +{ + m_iIcon = iIcon; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CStartMenu::GetIconSize(DWORD* piIcon) +{ + if (piIcon == NULL) + return E_POINTER; + *piIcon = m_iIcon; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CStartMenu::SetBitmap(HBITMAP hBitmap) +{ + BITMAP bm; + if (GetObject(hBitmap, sizeof(BITMAP), &bm)) + { + m_hBitmap = hBitmap; + 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), m_cObjects(0) Index: dll/win32/shell32/startmenu.h =================================================================== --- dll/win32/shell32/startmenu.h (revision 58821) +++ dll/win32/shell32/startmenu.h (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 @@ -21,19 +22,38 @@ #ifndef _STARTMENU_H_ #define _STARTMENU_H_ +#include "tiny.h" +#include "fakemenu.h" + +struct FILEITEMDATA +{ + CTinyString path; + CTinyString name; + bool is_folder; +}; + class CStartMenu : public CComCoClass, public CComObjectRootEx, public IMenuPopup, public IObjectWithSite, public IInitializeObject, + public IBanneredBar, public IMenuBand // FIXME { private: IBandSite *m_pBandSite; IUnknown *m_pUnkSite; + DWORD m_iIcon; + HBITMAP m_hBitmap; public: + HFAKEMENU m_hMenu; + HWND m_hWnd; + CTinyMap m_map; + CTinyVector m_list; + +public: CStartMenu(); ~CStartMenu(); @@ -58,6 +78,12 @@ // *** IInitializeObject methods *** virtual HRESULT STDMETHODCALLTYPE Initialize(); + // *** 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); + // *** IMenuBand methods *** FIXME virtual HRESULT STDMETHODCALLTYPE IsMenuMessage(MSG *pmsg); virtual HRESULT STDMETHODCALLTYPE TranslateMenuMessage(MSG *pmsg, LRESULT *plRet); @@ -74,6 +100,7 @@ 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() }; Index: dll/win32/shell32/undocshell.h =================================================================== --- dll/win32/shell32/undocshell.h (revision 58817) +++ dll/win32/shell32/undocshell.h (working copy) @@ -126,6 +126,10 @@ LPCWSTR lpstrRemoteName, DWORD dwType); +int WINAPI LogoffWindowsDialog(HWND hWndOwner); + +int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags); + /**************************************************************************** * Memory Routines */