Index: dll/win32/shell32/CMakeLists.txt =================================================================== --- dll/win32/shell32/CMakeLists.txt (revision 57956) +++ dll/win32/shell32/CMakeLists.txt (working copy) @@ -70,6 +70,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,996 @@ +/* + * 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, + LPCTSTR lpNewItem, + LPCTSTR path/* = NULL*/) +{ + HFAKEMENU hSubMenu; + if (uFlags & MF_POPUP) + { + hSubMenu = (HFAKEMENU)uIDNewItem; + hSubMenu->uID = 0xFFFFFFFF; + } + else + { + hSubMenu = CreatePopupFakeMenu(); + hSubMenu->uID = uIDNewItem; + } + hSubMenu->text = lpNewItem; + hSubMenu->uFlags = uFlags; + hSubMenu->nDepth = hMenu->nDepth + 1; + hSubMenu->parent = hMenu; + if (path) + hSubMenu->path = path; + else + hSubMenu->path.clear(); + 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]); + } + 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 + 16; + 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->popuped = 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, + L"BaseBar", + 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 (0xFFFFFFFF == 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; + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + 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) + { + 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)) + { + if (hMenu->uSelected == i) + { + 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 (hMenu->nIconSize == 32) + { + if (hChildMenu->hIcon == NULL && !hChildMenu->path.empty()) + { + SHFILEINFO sfi; + HIMAGELIST himl = (HIMAGELIST)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 = (HIMAGELIST)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) + { + POINT apt[3] = + { + {rcClient.right - 4, y + cy / 2}, + {rcClient.right - 8, y + cy / 2 - 4}, + {rcClient.right - 8, y + cy / 2 + 4}, + }; + SelectObject(hdc, GetStockObject(NULL_PEN)); + if (hMenu->uSelected == i) + SelectObject(hdc, GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); + else + SelectObject(hdc, GetStockObject(BLACK_BRUSH)); + Polygon(hdc, apt, 3); + } + SetBkMode(hdc, TRANSPARENT); + if (hChildMenu->uFlags & MF_GRAYED) + { + SetTextColor(hdc, RGB(255, 255, 255)); + } + 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)); + DrawText(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) + { + HFAKEMENU hSubMenu = hMenu->selected; + if (hSubMenu->uFlags & MF_SEPARATOR) + return; + + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), (LPARAM)hMenu); + + if (hMenu->popuped && IsWindow(hMenu->popuped->hwnd)) + { + DestroyWindow(hMenu->popuped->hwnd); + hMenu->popuped = NULL; + if (hMenu->popuped == hMenu->selected) + { + SetActiveWindow(hMenu->hwnd); + return; + } + } + + if (hSubMenu->uFlags & MF_POPUP) + { + RECT rc; + FakeMenu_GetItemRect(hMenu, hwnd, hMenu->uSelected, &rc); + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + hMenu->popuped = hSubMenu; + 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) +{ + 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->selected->uFlags & MF_POPUP) + { + HFAKEMENU hSubMenu = hMenu->selected; + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), (LPARAM)hMenu); + + RECT rc; + FakeMenu_GetItemRect(hMenu, hwnd, uSelected, &rc); + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + hMenu->popuped = hSubMenu; + 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->popuped && IsWindow(hMenu->popuped->hwnd)) + { + DestroyWindow(hMenu->popuped->hwnd); + hMenu->popuped = NULL; + SetActiveWindow(hMenu->hwnd); + } + + if (hMenu->selected->uFlags & MF_POPUP) + { + HFAKEMENU hSubMenu = hMenu->selected; + SendMessage(hMenu->hwnd, WM_MENUSELECT, + MAKEWPARAM(hMenu->uSelected, hSubMenu->uFlags), (LPARAM)hMenu); + + RECT rc; + FakeMenu_GetItemRect(hMenu, hwnd, uSelected, &rc); + MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2); + hMenu->popuped = hSubMenu; + 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, + (LPOLESTR)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, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, NULL, (LPVOID*)&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 = (HFAKEMENU)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->popuped = NULL; + 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 = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.lpszClassName = g_szFakeMenuClsName; + return RegisterClassEx(&wc) != 0; +} + +HFAKEMENU APIENTRY +CreatePopupFakeMenu(VOID) +{ + HFAKEMENU hMenu; + hMenu = (HFAKEMENU)HeapAlloc( + GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FAKEMENU)); + + ::new((void *)hMenu) FAKEMENU; + hMenu->uFlags = MF_ENABLED | MF_POPUP; + hMenu->nIconSize = 16; + hMenu->parent = NULL; + + 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,99 @@ +/* + * 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; + FAKEMENU *popuped; + tstring text; + CTinyVector children; + INT cyItem; + INT cySep; + INT nDepth; + HICON hIcon; + HICON hIconSm; + tstring 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, + LPCTSTR lpNewItem, + LPCTSTR 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 57956) +++ dll/win32/shell32/icon_res.rc (working copy) @@ -166,3 +166,5 @@ IDI_SHELL_DELETE3 ICON "res/icons/16717.ico" IDI_SHELL_DELETE4 ICON "res/icons/16718.ico" IDI_SHELL_DELETE5 ICON "res/icons/16721.ico" +IDI_RESTART ICON "res/icons/restart.ico" +IDI_SHUTDOWN ICON "res/icons/shutdown.ico" Index: dll/win32/shell32/lang/en-US.rc =================================================================== --- dll/win32/shell32/lang/en-US.rc (revision 57956) +++ dll/win32/shell32/lang/en-US.rc (working copy) @@ -805,4 +805,26 @@ 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)" END Index: dll/win32/shell32/res/icons/restart.ico =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: dll/win32/shell32/res/icons/restart.ico =================================================================== --- dll/win32/shell32/res/icons/restart.ico (revision 0) +++ dll/win32/shell32/res/icons/restart.ico (working copy) Property changes on: dll/win32/shell32/res/icons/restart.ico ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: dll/win32/shell32/res/icons/shutdown.ico =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: dll/win32/shell32/res/icons/shutdown.ico =================================================================== --- dll/win32/shell32/res/icons/shutdown.ico (revision 0) +++ dll/win32/shell32/res/icons/shutdown.ico (working copy) Property changes on: dll/win32/shell32/res/icons/shutdown.ico ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: dll/win32/shell32/shresdef.h =================================================================== --- dll/win32/shell32/shresdef.h (revision 57956) +++ dll/win32/shell32/shresdef.h (working copy) @@ -21,6 +21,8 @@ #define IDC_STATIC -1 +#define IDM_RESTART 1000 + /* * Bitmaps */ @@ -184,6 +186,29 @@ #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 + /* Note: those strings are referenced from the registry */ #define IDS_RECYCLEBIN_FOLDER_NAME 8964 #define IDS_ADMINISTRATIVETOOLS 22982 @@ -454,6 +479,9 @@ #define IDI_SHELL_SINGLE_CLICK_TO_OPEN 186 #define IDI_SHELL_DOUBLE_CLICK_TO_OPEN 187 +#define IDI_RESTART 1000 +#define IDI_SHUTDOWN 1001 + /* * AVI resources * Index: dll/win32/shell32/startmenu.cpp =================================================================== --- dll/win32/shell32/startmenu.cpp (revision 57956) +++ 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,453 @@ 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 LPTSTR +LoadStringDx(INT id) +{ + static TCHAR sz[512]; + LoadString(shell32_hInstance, id, sz, 512); + return sz; +} + +static HICON +LoadIconDx(INT id) +{ + return LoadIcon(shell32_hInstance, MAKEINTRESOURCE(id)); +} + +static HICON +LoadIconSmDx(INT id) +{ + return (HICON)LoadImage( + shell32_hInstance, MAKEINTRESOURCE(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) +{ + PostMessage(GetTrayWnd(), WM_COMMAND, MAKEWPARAM(nCommandID, 0), 0); +} + +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 +AddPathToFakeMenu(CStartMenu *pMenu, HFAKEMENU hMenu, LPCTSTR pszPath) +{ + INT nCount = 0; + size_t i = pMenu->m_list.size(); + + if (!GetFileList(pszPath, pMenu->m_list)) + { + AppendFakeMenu(hMenu, + MF_GRAYED | MF_STRING, + 0, + LoadStringDx(IDS_ACCESSDENIED)); + return; + } + + tstring 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 (lstrcmpi(name.c_str() + size - 4, TEXT(".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; + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_CONTROL_PANEL); + i++; + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_NETWORK); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_PRINTERS_FOLDER); + 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; + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_FIND_IN_FILE); + hMenu->children[i++]->hIconSm = LoadIconSmDx(IDI_SHELL_ENTIRE_NETWORK); + i++; + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_USERS); +} + +static VOID +CreateProgramsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + TCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPath(hMenu->hwnd, szPath, CSIDL_COMMON_PROGRAMS, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + SHGetSpecialFolderPath(hMenu->hwnd, szPath, CSIDL_PROGRAMS, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateMyDocumentsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + TCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPath(hMenu->hwnd, szPath, CSIDL_PERSONAL, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateRecentDocumentsMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + TCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPath(hMenu->hwnd, szPath, CSIDL_RECENT, FALSE); + AddPathToFakeMenu(pMenu, hMenu, szPath); + hMenu->path = szPath; +} + +static VOID +CreateFavoritesMenu(CStartMenu *pMenu, HFAKEMENU hMenu) +{ + TCHAR szPath[MAX_PATH]; + SHGetSpecialFolderPath(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((HFAKEMENU)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()); + } + } + } +} + +EXTERN_C int WINAPI LogoffWindowsDialog(HWND); +EXTERN_C int WINAPI RestartDialog(HWND, LPCWSTR, DWORD); + +static VOID +StartMenu_OnCommand(HWND hwnd, CStartMenu *pMenu, WPARAM wParam, LPARAM lParam) +{ + if (HIWORD(wParam) != 0xDEAD) + return; + + HFAKEMENU hMenu = (HFAKEMENU)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(), + L"You selected restart.", + 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 = (LPCREATESTRUCT)lParam; + CStartMenu *pMenu = (CStartMenu *)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->popuped && IsWindow(hMenu->popuped->hwnd)) + { + DestroyWindow(hMenu->popuped->hwnd); + hMenu->popuped = NULL; + } + + if (GetCapture() == hwnd) + ReleaseCapture(); + ShowWindow(hMenu->hwnd, SW_HIDE); + + if (hMenu->result) + { + SendMessage(hMenu->hwnd, WM_COMMAND, + MAKEWPARAM(hMenu->result->uID, 0xDEAD), (LPARAM)hMenu); + hMenu->result = NULL; + } +} + +static LRESULT CALLBACK +StartMenu_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CStartMenu *pMenu = (CStartMenu *)GetWindowLongPtr(hwnd, 0); + HFAKEMENU hMenu = (HFAKEMENU)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, L"Start Menu", + dwStyle, 0, 0, 0, 0, NULL, NULL, shell32_hInstance, pMenu); +} + CStartMenu::CStartMenu() { m_pBandSite = NULL; m_pUnkSite = NULL; + m_hWnd = NULL; + m_hBitmap = NULL; + m_iIcon = BMICON_LARGE; + m_hMenu = NULL; } CStartMenu::~CStartMenu() @@ -40,8 +484,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) @@ -71,10 +517,106 @@ return E_NOTIMPL; } -HRESULT STDMETHODCALLTYPE CStartMenu::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags) +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++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_MY_DOCUMENTS); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_MY_DOCUMENTS); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RECENT_DOCUMENTS); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RECENT_DOCUMENTS); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_FAVORITES); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_FAVORITES); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RUN); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RUN); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_SEARCH); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_SEARCH); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_HELP); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_HELP); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_RUN2); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_RUN2); + i++; + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHELL_LOGOFF); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_SHELL_LOGOFF); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_RESTART); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_RESTART); + i++; + hMenu->children[i]->hIcon = LoadIconDx(IDI_SHUTDOWN); + hMenu->children[i]->hIconSm = LoadIconSmDx(IDI_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 +654,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 +664,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->popuped; + } 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->popuped; + } 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; Index: dll/win32/shell32/startmenu.h =================================================================== --- dll/win32/shell32/startmenu.h (revision 57956) +++ 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,22 +22,43 @@ #ifndef _STARTMENU_H_ #define _STARTMENU_H_ +#include "tiny.h" +#include "fakemenu.h" + +struct FILEITEMDATA +{ + tstring path; + tstring name; + bool is_folder; +}; + +#define STARTMENUITEM_COUNT 11 + 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; + HBITMAP m_hBitmap; + CTinyMap m_map; + CTinyVector m_list; + DWORD m_iIcon; + HFAKEMENU m_hMenu; +public: + CStartMenu(); + ~CStartMenu(); + // *** IOleWindow methods *** virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); @@ -62,6 +84,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 +100,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() }; Index: dll/win32/shell32/tiny.h =================================================================== --- dll/win32/shell32/tiny.h (revision 0) +++ dll/win32/shell32/tiny.h (working copy) @@ -0,0 +1,193 @@ +/* + * Tiny objects + * + * 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 TINY_H +#define TINY_H + +#include +#include +#include + +#if 0 + #include + #include + #include + + #ifdef UNICODE + typedef std::wstring tstring; + #else + typedef std::string tstring; + #endif + + #define CTinyVector std::vector + #define CTinyMap std::map +#else + 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); } + void resize(size_t i) { m_psz[i] = TEXT('\0'); } + CTinyString substr(size_t i) { CTinyString str(&m_psz[i]); return str; } + bool empty() const { return m_psz[0] == TEXT('\0'); } + TCHAR operator[](size_t i) const { + assert(i <= _tcslen(m_psz)); + return m_psz[i]; + } + size_t find_last_of(TCHAR ch) { + LPTSTR pch = _tcsrchr(m_psz, ch); + if (pch) return pch - m_psz; + return (size_t)-1; + } + 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; } + }; + typedef CTinyString tstring; + + 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; + } +#endif + +#endif /* TINY_H */