//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platform/menus/popupMenu.h" #include "platformWin32/platformWin32.h" #include "platformWin32/winWindow.h" #include "memory/safeDelete.h" ////////////////////////////////////////////////////////////////////////// // Platform Menu Data ////////////////////////////////////////////////////////////////////////// struct PlatformPopupMenuData { static U32 mLastPopupMenuID; static const U32 PopupMenuIDRange; HMENU mMenu; U32 mMenuID; U32 mLastID; PlatformPopupMenuData() { mMenu = NULL; mMenuID = mLastPopupMenuID++; mLastID = 0; } ~PlatformPopupMenuData() { if(mMenu) DestroyMenu(mMenu); } }; U32 PlatformPopupMenuData::mLastPopupMenuID = 0; const U32 PlatformPopupMenuData::PopupMenuIDRange = 100; void PopupMenu::createPlatformPopupMenuData() { mData = new PlatformPopupMenuData; } void PopupMenu::deletePlatformPopupMenuData() { SAFE_DELETE(mData); } void PopupMenu::createPlatformMenu() { mData->mMenu = mIsPopup ? CreatePopupMenu() : CreateMenu(); } ////////////////////////////////////////////////////////////////////////// // Public Methods ////////////////////////////////////////////////////////////////////////// S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator) { MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID|MIIM_TYPE; mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; mData->mLastID++; if(title && *title) mi.fType = MFT_STRING; else mi.fType = MFT_SEPARATOR; char buf[1024]; dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator); mi.dwTypeData = (LPSTR)buf; if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) { DrawMenuBar(winState.appWindow); return mi.wID; } return -1; } S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu) { for(S32 i = 0;i < mSubmenus->size();i++) { if(submenu == (*mSubmenus)[i]) { Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice"); return -1; } } MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA; mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; if(title && *title) mi.fType = MFT_STRING; else mi.fType = MFT_SEPARATOR; mi.dwTypeData = (LPSTR)title; mi.hSubMenu = submenu->mData->mMenu; mi.dwItemData = (ULONG_PTR)submenu; if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) { mSubmenus->addObject(submenu); DrawMenuBar(winState.appWindow); return mi.wID; } return -1; } void PopupMenu::removeItem(S32 itemPos) { MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_DATA; if(GetMenuItemInfoA(mData->mMenu, itemPos, TRUE, &mi)) { if(mi.fMask & MIIM_DATA) { PopupMenu *mnu = (PopupMenu *)mi.dwItemData; if (mnu) mSubmenus->removeObject(mnu); } } RemoveMenu(mData->mMenu, itemPos, MF_BYPOSITION); DrawMenuBar(winState.appWindow); } ////////////////////////////////////////////////////////////////////////// void PopupMenu::enableItem(S32 pos, bool enable) { U32 flags = enable ? MF_ENABLED : MF_GRAYED; EnableMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags); } void PopupMenu::checkItem(S32 pos, bool checked) { // U32 flags = checked ? MF_CHECKED : MF_UNCHECKED; // CheckMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags); MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_STATE; mi.fState = checked ? MFS_CHECKED : MFS_UNCHECKED; SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi); } void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos) { CheckMenuRadioItem(mData->mMenu, firstPos, lastPos, checkPos, MF_BYPOSITION); } bool PopupMenu::isItemChecked(S32 pos) { MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_STATE; if(GetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi) && (mi.fState & MFS_CHECKED)) return true; return false; } ////////////////////////////////////////////////////////////////////////// bool PopupMenu::canHandleID(U32 iD) { for(S32 i = 0;i < mSubmenus->size();i++) { PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); if(subM == NULL) continue; if(subM->canHandleID(iD)) return true; } if(iD >= mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange && iD < (mData->mMenuID+1) * PlatformPopupMenuData::PopupMenuIDRange) { return true; } return false; } bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */) { // [tom, 8/20/2006] Pass off to a sub menu if it's for them for(S32 i = 0;i < mSubmenus->size();i++) { PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); if(subM == NULL) continue; if(subM->canHandleID(command)) { return subM->handleSelect(command, text); } } // [tom, 8/21/2006] Cheesey hack to find the position based on ID char buf[512]; MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.dwTypeData = NULL; S32 numItems = GetMenuItemCount(mData->mMenu); S32 pos = -1; for(S32 i = 0;i < numItems;i++) { mi.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE; if(GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi)) { if(mi.wID == command) { if(text == NULL) { mi.dwTypeData = buf; mi.cch++; GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi); // [tom, 5/11/2007] Don't do anything if the menu item is disabled if(mi.fState & MFS_DISABLED) return false; text = StringTable->insert(mi.dwTypeData); } pos = i; break; } } } if(pos == -1) { Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command); return false; } // [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script return dAtob(Con::executef(this, 4, "onSelectItem", Con::getIntArg(pos), text ? text : "")); } ////////////////////////////////////////////////////////////////////////// void PopupMenu::showPopup(S32 x /* = -1 */, S32 y /* = -1 */) { POINT p; if(x == -1 && y == -1) GetCursorPos(&p); else { p.x = x; p.y = y; ClientToScreen(winState.appWindow, &p); } winState.renderThreadBlocked = true; U32 opt = (int)TrackPopupMenu(mData->mMenu, TPM_NONOTIFY|TPM_RETURNCMD, p.x, p.y, 0, winState.appWindow, NULL); if(opt > 0) handleSelect(opt, NULL); winState.renderThreadBlocked = false; } ////////////////////////////////////////////////////////////////////////// void PopupMenu::attachToMenuBar(S32 pos, const char *title) { if(winState.appMenu == NULL) { CreateWin32MenuBar(); } MENUITEMINFOA mii; mii.cbSize = sizeof(MENUITEMINFOA); mii.fMask = MIIM_STRING|MIIM_DATA; mii.dwTypeData = (LPSTR)title; mii.fMask |= MIIM_ID; mii.wID = mData->mMenuID; mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = mData->mMenu; mii.dwItemData = (ULONG_PTR)this; InsertMenuItemA(winState.appMenu, pos, TRUE, &mii); DrawMenuBar(winState.appWindow); } void PopupMenu::removeFromMenuBar() { // [tom, 8/21/2006] Find us on the menu bar (lame) S32 numItems = GetMenuItemCount(winState.appMenu); S32 pos = -1; for(S32 i = 0;i < numItems;i++) { MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_DATA; if(GetMenuItemInfoA(winState.appMenu, i, TRUE, &mi)) { if(mi.fMask & MIIM_DATA) { PopupMenu *mnu = (PopupMenu *)mi.dwItemData; if(mnu == this) { pos = i; break; } } } } if(pos == -1) return; RemoveMenu(winState.appMenu, pos, MF_BYPOSITION); DrawMenuBar(winState.appWindow); }