/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2010 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "..\..\core\commonheaders.h" #include "genmenu.h" static bool bIsGenMenuInited; bool bIconsDisabled; static CRITICAL_SECTION csMenuHook; static int NextObjectId = 0x100, NextObjectMenuItemId = CLISTMENUIDMIN; #if defined(_DEBUG) static void DumpMenuItem(TMO_IntMenuItem* pParent, int level = 0) { char temp[ 30 ]; memset(temp, '\t', level); temp[ level ] = 0; for (PMO_IntMenuItem pimi = pParent; pimi != NULL; pimi = pimi->next) { Netlib_Logf(NULL, "%sMenu item %08p [%08p]: %S", temp, pimi, pimi->mi.root, pimi->mi.ptszName); PMO_IntMenuItem submenu = pimi->submenu.first; if (submenu) DumpMenuItem(submenu, level+1); } } #endif static int CompareMenus(const TIntMenuObject* p1, const TIntMenuObject* p2) { return lstrcmpA(p1->Name, p2->Name); } LIST g_menus(10, CompareMenus); void FreeAndNil(void **p) { if (p == NULL) return; if (*p != NULL) { mir_free(*p); *p = NULL; } } int GetMenuObjbyId(const int id) { for (int i=0; i < g_menus.getCount(); i++) if (g_menus[i]->id == id) return i; return -1; } LPTSTR GetMenuItemText(PMO_IntMenuItem pimi) { if (pimi->mi.flags & CMIF_KEEPUNTRANSLATED) return pimi->mi.ptszName; return LangPackTranslateStringT(pimi->mi.hLangpack, pimi->mi.ptszName); } PMO_IntMenuItem MO_RecursiveWalkMenu(PMO_IntMenuItem parent, pfnWalkFunc func, void* param) { if (parent == NULL) return FALSE; PMO_IntMenuItem pnext; for (PMO_IntMenuItem pimi = parent; pimi != NULL; pimi = pnext) { PMO_IntMenuItem submenu = pimi->submenu.first; pnext = pimi->next; if (func(pimi, param)) // it can destroy the menu item return pimi; if (submenu) { PMO_IntMenuItem res = MO_RecursiveWalkMenu(submenu, func, param); if (res) return res; } } return FALSE; } //wparam=0 //lparam=LPMEASUREITEMSTRUCT int MO_MeasureMenuItem(LPMEASUREITEMSTRUCT mis) { // prevent win9x from ugly menus displaying when there is no icon mis->itemWidth = 0; mis->itemHeight = 0; if ( !bIsGenMenuInited) return -1; if (mis == NULL) return FALSE; PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)mis->itemData); if (pimi == NULL) return FALSE; if (pimi->iconId == -1) return FALSE; mis->itemWidth = max(0, GetSystemMetrics(SM_CXSMICON)-GetSystemMetrics(SM_CXMENUCHECK)+4); mis->itemHeight = GetSystemMetrics(SM_CYSMICON)+2; return TRUE; } //wparam=0 //lparam=LPDRAWITEMSTRUCT int MO_DrawMenuItem(LPDRAWITEMSTRUCT dis) { if ( !bIsGenMenuInited) return -1; if (dis == NULL) return FALSE; EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)dis->itemData); if (pimi == NULL || pimi->iconId == -1) { LeaveCriticalSection(&csMenuHook); return FALSE; } int y = (dis->rcItem.bottom - dis->rcItem.top - GetSystemMetrics(SM_CYSMICON))/2+1; if (dis->itemState & ODS_SELECTED) { if (dis->itemState & ODS_CHECKED) { RECT rc; rc.left = 2; rc.right = GetSystemMetrics(SM_CXSMICON)+2; rc.top = y; rc.bottom = rc.top+GetSystemMetrics(SM_CYSMICON)+2; FillRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHT)); ImageList_DrawEx(pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_SELECTED); } else ImageList_DrawEx(pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_FOCUS); } else { if (dis->itemState & ODS_CHECKED) { RECT rc; rc.left = 0; rc.right = GetSystemMetrics(SM_CXSMICON)+4; rc.top = y-2; rc.bottom = rc.top + GetSystemMetrics(SM_CYSMICON)+4; DrawEdge(dis->hDC, &rc, BDR_SUNKENOUTER, BF_RECT); InflateRect(&rc, -1, -1); COLORREF menuCol = GetSysColor(COLOR_MENU); COLORREF hiliteCol = GetSysColor(COLOR_3DHIGHLIGHT); HBRUSH hBrush = CreateSolidBrush(RGB((GetRValue(menuCol)+GetRValue(hiliteCol))/2, (GetGValue(menuCol)+GetGValue(hiliteCol))/2, (GetBValue(menuCol)+GetBValue(hiliteCol))/2)); FillRect(dis->hDC, &rc, GetSysColorBrush(COLOR_MENU)); DeleteObject(hBrush); ImageList_DrawEx(pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, GetSysColor(COLOR_MENU), ILD_BLEND50); } else ImageList_DrawEx(pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); } LeaveCriticalSection(&csMenuHook); return TRUE; } int MO_RemoveAllObjects() { int i; for (i=0; i < g_menus.getCount(); i++) delete g_menus[i]; g_menus.destroy(); return 0; } //wparam=MenuObjectHandle INT_PTR MO_RemoveMenuObject(WPARAM wParam, LPARAM) { int objidx; if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); objidx = GetMenuObjbyId((int)wParam); if (objidx == -1) { LeaveCriticalSection(&csMenuHook); return -1; } delete g_menus[ objidx ]; g_menus.remove(objidx); LeaveCriticalSection(&csMenuHook); return 0; } //wparam=MenuObjectHandle //lparam=vKey INT_PTR MO_ProcessHotKeys(HANDLE menuHandle, INT_PTR vKey) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); int objidx = GetMenuObjbyId((int)menuHandle); if (objidx == -1) { LeaveCriticalSection(&csMenuHook); return FALSE; } for (PMO_IntMenuItem pimi = g_menus[objidx]->m_items.first; pimi != NULL; pimi = pimi->next) { if (pimi->mi.hotKey == 0) continue; if (HIWORD(pimi->mi.hotKey) != vKey) continue; if ( !(LOWORD(pimi->mi.hotKey) & MOD_ALT) != !(GetKeyState(VK_MENU) & 0x8000)) continue; if ( !(LOWORD(pimi->mi.hotKey) & MOD_CONTROL) != !(GetKeyState(VK_CONTROL) & 0x8000)) continue; if ( !(LOWORD(pimi->mi.hotKey) & MOD_SHIFT) != !(GetKeyState(VK_SHIFT) & 0x8000)) continue; MO_ProcessCommand(pimi, 0); LeaveCriticalSection(&csMenuHook); return TRUE; } LeaveCriticalSection(&csMenuHook); return FALSE; } INT_PTR MO_GetProtoRootMenu(WPARAM wParam, LPARAM lParam) { char* szProto = (char*)wParam; if (szProto == NULL) return 0; if (DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", TRUE)) return (INT_PTR)cli.pfnGetProtocolMenu(szProto); int objidx = GetMenuObjbyId((int)hMainMenuObject); if (objidx == -1) return NULL; EnterCriticalSection(&csMenuHook); TIntMenuObject* pmo = g_menus[objidx]; PMO_IntMenuItem p; for (p = pmo->m_items.first; p != NULL; p = p->next) if ( !lstrcmpA(p->UniqName, szProto)) break; LeaveCriticalSection(&csMenuHook); return (INT_PTR)p; } //wparam=MenuItemHandle //lparam=PMO_MenuItem INT_PTR MO_GetMenuItem(WPARAM wParam, LPARAM lParam) { PMO_MenuItem mi = (PMO_MenuItem)lParam; if ( !bIsGenMenuInited || mi == NULL) return -1; PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)wParam); EnterCriticalSection(&csMenuHook); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } *mi = pimi->mi; LeaveCriticalSection(&csMenuHook); return 0; } static int FindDefaultItem(PMO_IntMenuItem pimi, void*) { if (pimi->mi.flags & (CMIF_GRAYED | CMIF_HIDDEN)) return FALSE; return (pimi->mi.flags & CMIF_DEFAULT) ? TRUE : FALSE; } INT_PTR MO_GetDefaultMenuItem(WPARAM wParam, LPARAM) { if ( !bIsGenMenuInited) return -1; PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)wParam); EnterCriticalSection(&csMenuHook); if (pimi) pimi = MO_RecursiveWalkMenu(pimi, FindDefaultItem, NULL); LeaveCriticalSection(&csMenuHook); return (INT_PTR)pimi; } //wparam MenuItemHandle //lparam PMO_MenuItem int MO_ModifyMenuItem(PMO_IntMenuItem menuHandle, PMO_MenuItem pmi) { int oldflags; if ( !bIsGenMenuInited || pmi == NULL || pmi->cbSize != sizeof(TMO_MenuItem)) return -1; EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)menuHandle); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } if (pmi->flags & CMIM_NAME) { FreeAndNil((void**)&pimi->mi.pszName); if (pmi->flags & CMIF_UNICODE) pimi->mi.ptszName = mir_tstrdup(pmi->ptszName); else pimi->mi.ptszName = mir_a2t(pmi->pszName); } if (pmi->flags & CMIM_FLAGS) { oldflags = pimi->mi.flags & (CMIF_ROOTHANDLE | CMIF_ICONFROMICOLIB); pimi->mi.flags = (pmi->flags & ~CMIM_ALL) | oldflags; } if ((pmi->flags & CMIM_ICON) && !bIconsDisabled) { if (pimi->mi.flags & CMIF_ICONFROMICOLIB) { HICON hIcon = IcoLib_GetIconByHandle(pmi->hIcolibItem, false); if (hIcon != NULL) { pimi->hIcolibItem = pmi->hIcolibItem; pimi->iconId = ImageList_ReplaceIcon(pimi->parent->m_hMenuIcons, pimi->iconId, hIcon); IconLib_ReleaseIcon(hIcon, 0); } else pimi->iconId = -1, pimi->hIcolibItem = NULL; } else { pimi->mi.hIcon = pmi->hIcon; if (pmi->hIcon != NULL) pimi->iconId = ImageList_ReplaceIcon(pimi->parent->m_hMenuIcons, pimi->iconId, pmi->hIcon); else pimi->iconId = -1; //fixme, should remove old icon & shuffle all iconIds } if (pimi->hBmp) { DeleteObject(pimi->hBmp); pimi->hBmp = NULL; } } if (pmi->flags & CMIM_HOTKEY) pimi->mi.hotKey = pmi->hotKey; LeaveCriticalSection(&csMenuHook); return 0; } //wparam MenuItemHandle //return ownerdata useful to free ownerdata before delete menu item, //NULL on error. INT_PTR MO_MenuItemGetOwnerData(WPARAM wParam, LPARAM) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)wParam); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } INT_PTR res = (INT_PTR)pimi->mi.ownerdata; LeaveCriticalSection(&csMenuHook); return res; } PMO_IntMenuItem MO_GetIntMenuItem(HGENMENU wParam) { PMO_IntMenuItem result = (PMO_IntMenuItem)wParam; if (result == NULL || wParam == (HGENMENU)0xffff1234 || wParam == HGENMENU_ROOT) return NULL; __try { if (result->signature != MENUITEM_SIGNATURE) result = NULL; } __except(EXCEPTION_EXECUTE_HANDLER) { result = NULL; } return result; } //LOWORD(wparam) menuident static int FindMenuByCommand(PMO_IntMenuItem pimi, void* pCommand) { return (pimi->iCommand == (int)pCommand); } int MO_ProcessCommandBySubMenuIdent(int menuID, int command, LPARAM lParam) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); int objidx = GetMenuObjbyId(menuID); if (objidx == -1) { LeaveCriticalSection(&csMenuHook); return -1; } PMO_IntMenuItem pimi = MO_RecursiveWalkMenu(g_menus[objidx]->m_items.first, FindMenuByCommand, (void*)command); if (pimi) { LeaveCriticalSection(&csMenuHook); return MO_ProcessCommand(pimi, lParam); } LeaveCriticalSection(&csMenuHook); return -1; } INT_PTR MO_ProcessCommandByMenuIdent(WPARAM wParam, LPARAM lParam) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); for (int i=0; i < g_menus.getCount(); i++) { PMO_IntMenuItem pimi = MO_RecursiveWalkMenu(g_menus[i]->m_items.first, FindMenuByCommand, (void*)wParam); if (pimi) { LeaveCriticalSection(&csMenuHook); return MO_ProcessCommand(pimi, lParam); } } LeaveCriticalSection(&csMenuHook); return FALSE; } int MO_ProcessCommand(PMO_IntMenuItem aHandle, LPARAM lParam) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem(aHandle); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } char *srvname = pimi->parent->ExecService; void *ownerdata = pimi->mi.ownerdata; LeaveCriticalSection(&csMenuHook); CallService(srvname, (WPARAM)ownerdata, lParam); return 1; } int MO_SetOptionsMenuItem(PMO_IntMenuItem aHandle, int setting, INT_PTR value) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem(aHandle); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } int res = -1; __try { res = 1; if (setting == OPT_MENUITEMSETUNIQNAME) { mir_free(pimi->UniqName); pimi->UniqName = mir_strdup((char*)value); } } __except(EXCEPTION_EXECUTE_HANDLER) {} LeaveCriticalSection(&csMenuHook); return res; } int MO_SetOptionsMenuObject(HANDLE handle, int setting, INT_PTR value) { int pimoidx; int res = 0; if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); __try { pimoidx = GetMenuObjbyId((int)handle); res = pimoidx != -1; if (res) { TIntMenuObject* pmo = g_menus[pimoidx]; switch (setting) { case OPT_MENUOBJECT_SET_ONADD_SERVICE: FreeAndNil((void**)&pmo->onAddService); pmo->onAddService = mir_strdup((char*)value); break; case OPT_MENUOBJECT_SET_FREE_SERVICE: FreeAndNil((void**)&pmo->FreeService); pmo->FreeService = mir_strdup((char*)value); break; case OPT_MENUOBJECT_SET_CHECK_SERVICE: FreeAndNil((void**)&pmo->CheckService); pmo->CheckService = mir_strdup((char*)value); break; case OPT_USERDEFINEDITEMS: pmo->m_bUseUserDefinedItems = (BOOL)value; break; } } } __except(EXCEPTION_EXECUTE_HANDLER) {} LeaveCriticalSection(&csMenuHook); return res; } //wparam=0; //lparam=PMenuParam; //result=MenuObjectHandle INT_PTR MO_CreateNewMenuObject(WPARAM, LPARAM lParam) { PMenuParam pmp = (PMenuParam)lParam; if ( !bIsGenMenuInited || pmp == NULL) return -1; EnterCriticalSection(&csMenuHook); TIntMenuObject* p = new TIntMenuObject(); p->id = NextObjectId++; p->Name = mir_strdup(pmp->name); p->CheckService = mir_strdup(pmp->CheckService); p->ExecService = mir_strdup(pmp->ExecService); p->m_hMenuIcons = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 15, 100); g_menus.insert(p); LeaveCriticalSection(&csMenuHook); return p->id; } //wparam=MenuItemHandle //lparam=0 static int FreeMenuItem(TMO_IntMenuItem* pimi, void*) { pimi->parent->freeItem(pimi); return FALSE; } static int FindParent(TMO_IntMenuItem* pimi, void* p) { return pimi->next == p; } INT_PTR MO_RemoveMenuItem(WPARAM wParam, LPARAM) { EnterCriticalSection(&csMenuHook); PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)wParam); if ( !pimi) { LeaveCriticalSection(&csMenuHook); return -1; } if (pimi->submenu.first) { MO_RecursiveWalkMenu(pimi->submenu.first, FreeMenuItem, NULL); pimi->submenu.first = NULL; } PMO_IntMenuItem prev = MO_RecursiveWalkMenu(pimi->owner->first, FindParent, pimi); if (prev) prev->next = pimi->next; if (pimi->owner->first == pimi) pimi->owner->first = pimi->next; if (pimi->owner->last == pimi) pimi->owner->last = prev; pimi->signature = 0; // invalidate all future calls to that object pimi->parent->freeItem(pimi); LeaveCriticalSection(&csMenuHook); return 0; } /////////////////////////////////////////////////////////////////////////////// // we presume that this function is being called inside csMenuHook only static int PackMenuItems(PMO_IntMenuItem pimi, void*) { pimi->iCommand = NextObjectMenuItemId++; return FALSE; } static int GetNextObjectMenuItemId() { // if menu commands are exausted, pack the menu array if (NextObjectMenuItemId >= CLISTMENUIDMAX) { NextObjectMenuItemId = CLISTMENUIDMIN; for (int i=0; i < g_menus.getCount(); i++) MO_RecursiveWalkMenu(g_menus[i]->m_items.first, PackMenuItems, NULL); } return NextObjectMenuItemId++; } //wparam=MenuObjectHandle //lparam=PMO_MenuItem //return MenuItemHandle PMO_IntMenuItem MO_AddNewMenuItem(HANDLE menuobjecthandle, PMO_MenuItem pmi) { if ( !bIsGenMenuInited || pmi == NULL || pmi->cbSize != sizeof(TMO_MenuItem)) return NULL; //old mode if ( !(pmi->flags & CMIF_ROOTHANDLE)) return MO_AddOldNewMenuItem(menuobjecthandle, pmi); EnterCriticalSection(&csMenuHook); int objidx = GetMenuObjbyId((int)menuobjecthandle); if (objidx == -1) { LeaveCriticalSection(&csMenuHook); return NULL; } TIntMenuObject* pmo = g_menus[objidx]; TMO_IntMenuItem* p = (TMO_IntMenuItem*)mir_calloc(sizeof(TMO_IntMenuItem)); p->parent = pmo; p->signature = MENUITEM_SIGNATURE; p->iCommand = GetNextObjectMenuItemId(); p->mi = *pmi; p->iconId = -1; p->OverrideShow = TRUE; p->originalPosition = pmi->position; if (pmi->flags & CMIF_UNICODE) p->mi.ptszName = mir_tstrdup(pmi->ptszName); else p->mi.ptszName = mir_a2u(pmi->pszName); if (pmi->hIcon != NULL && !bIconsDisabled) { if (pmi->flags & CMIF_ICONFROMICOLIB) { HICON hIcon = IcoLib_GetIconByHandle(pmi->hIcolibItem, false); p->iconId = ImageList_AddIcon(pmo->m_hMenuIcons, hIcon); p->hIcolibItem = pmi->hIcolibItem; IconLib_ReleaseIcon(hIcon, 0); } else { HANDLE hIcolibItem = IcoLib_IsManaged(pmi->hIcon); if (hIcolibItem) { p->iconId = ImageList_AddIcon(pmo->m_hMenuIcons, pmi->hIcon); p->hIcolibItem = hIcolibItem; } else p->iconId = ImageList_AddIcon(pmo->m_hMenuIcons, pmi->hIcon); } } if (p->mi.root == HGENMENU_ROOT) p->mi.root = NULL; PMO_IntMenuItem pRoot = (p->mi.root != NULL) ? MO_GetIntMenuItem(p->mi.root) : NULL; if (pRoot) p->owner = &pRoot->submenu; else p->owner = &pmo->m_items; if ( !p->owner->first) p->owner->first = p; if (p->owner->last) p->owner->last->next = p; p->owner->last = p; LeaveCriticalSection(&csMenuHook); return p; } //wparam=MenuObjectHandle //lparam=PMO_MenuItem int FindRoot(PMO_IntMenuItem pimi, void* param) { if (pimi->mi.pszName != NULL) if (pimi->submenu.first && !_tcscmp(pimi->mi.ptszName, (TCHAR*)param)) return TRUE; return FALSE; } PMO_IntMenuItem MO_AddOldNewMenuItem(HANDLE menuobjecthandle, PMO_MenuItem pmi) { if ( !bIsGenMenuInited || pmi == NULL) return NULL; int objidx = GetMenuObjbyId((int)menuobjecthandle); if (objidx == -1) return NULL; if (pmi->cbSize != sizeof(TMO_MenuItem)) return NULL; if (pmi->flags & CMIF_ROOTHANDLE) return NULL; //is item with popup or not if (pmi->root == 0) { //yes, this without popup pmi->root = NULL; //first level } else { // no, search for needed root and create it if need TCHAR* tszRoot; if (pmi->flags & CMIF_UNICODE) tszRoot = mir_tstrdup((TCHAR*)pmi->root); else tszRoot = mir_a2t((char*)pmi->root); PMO_IntMenuItem oldroot = MO_RecursiveWalkMenu(g_menus[objidx]->m_items.first, FindRoot, tszRoot); mir_free(tszRoot); if (oldroot == NULL) { //not found, creating root TMO_MenuItem tmi = { 0 }; tmi = *pmi; tmi.flags |= CMIF_ROOTHANDLE; tmi.ownerdata = 0; tmi.root = NULL; //copy pszPopupName tmi.ptszName = (TCHAR*)pmi->root; if ((oldroot = MO_AddNewMenuItem(menuobjecthandle, &tmi)) != NULL) MO_SetOptionsMenuItem(oldroot, OPT_MENUITEMSETUNIQNAME, (INT_PTR)pmi->root); } pmi->root = oldroot; //popup will be created in next commands } pmi->flags |= CMIF_ROOTHANDLE; //add popup(root allready exists) return MO_AddNewMenuItem(menuobjecthandle, pmi); } static int WhereToPlace(HMENU hMenu, PMO_MenuItem mi) { MENUITEMINFO mii = { 0 }; mii.cbSize = MENUITEMINFO_V4_SIZE; mii.fMask = MIIM_SUBMENU | MIIM_DATA; for (int i=GetMenuItemCount(hMenu)-1; i >= 0; i--) { GetMenuItemInfo(hMenu, i, TRUE, &mii); if (mii.fType != MFT_SEPARATOR) { PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)mii.dwItemData); if (pimi != NULL) if (pimi->mi.position <= mi->position) return i+1; } } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// static DWORD GetMenuItemType(HMENU hMenu, int uItem) { MENUITEMINFO mii = { 0 }; mii.cbSize = MENUITEMINFO_V4_SIZE; mii.fMask = MIIM_TYPE; GetMenuItemInfo(hMenu, uItem, TRUE, &mii); return mii.fType; } static UINT GetMenuItemTypeData(HMENU hMenu, int uItem, PMO_IntMenuItem& p) { MENUITEMINFO mii = { 0 }; mii.cbSize = MENUITEMINFO_V4_SIZE; mii.fMask = MIIM_DATA | MIIM_TYPE; GetMenuItemInfo(hMenu, uItem, TRUE, &mii); p = MO_GetIntMenuItem((HGENMENU)mii.dwItemData); return mii.fType; } static void InsertSeparator(HMENU hMenu, int uItem) { MENUITEMINFO mii; mii.cbSize = MENUITEMINFO_V4_SIZE; mii.fMask = MIIM_TYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(hMenu, uItem, TRUE, &mii); } static void InsertMenuItemWithSeparators(HMENU hMenu, int uItem, MENUITEMINFO *lpmii) { PMO_IntMenuItem pimi = MO_GetIntMenuItem((HGENMENU)lpmii->dwItemData), p; if (pimi == NULL) return; //check for separator before if (uItem) { UINT fType = GetMenuItemTypeData(hMenu, uItem-1, p); if (p != NULL && fType != MFT_SEPARATOR) { if ((p->mi.position / SEPARATORPOSITIONINTERVAL) != (pimi->mi.position / SEPARATORPOSITIONINTERVAL)) { //but might be supposed to be after the next one instead if (uItem < GetMenuItemCount(hMenu) && GetMenuItemType(hMenu, uItem) != MFT_SEPARATOR) InsertSeparator(hMenu, uItem); uItem++; } } } //check for separator after if (uItem < GetMenuItemCount(hMenu)) { UINT fType = GetMenuItemTypeData(hMenu, uItem, p); if (p != NULL && fType != MFT_SEPARATOR) if ((p->mi.position / SEPARATORPOSITIONINTERVAL) != (pimi->mi.position / SEPARATORPOSITIONINTERVAL)) InsertSeparator(hMenu, uItem); } // create local copy *lpmii so we can change some flags MENUITEMINFO mii = *lpmii; int count = GetMenuItemCount(hMenu); if (count != 0 && (count % 33) == 0 && pimi->mi.root != NULL) { if ( !(mii.fMask & MIIM_FTYPE)) mii.fType = 0; mii.fMask |= MIIM_FTYPE; mii.fType |= MFT_MENUBARBREAK; } mii.dwTypeData = GetMenuItemText(pimi); InsertMenuItem(hMenu, uItem, TRUE, &mii); } ///////////////////////////////////////////////////////////////////////////////////////// // wparam started hMenu // lparam ListParam* // result hMenu INT_PTR MO_BuildMenu(WPARAM wParam, LPARAM lParam) { if ( !bIsGenMenuInited) return -1; EnterCriticalSection(&csMenuHook); ListParam *lp = (ListParam*)lParam; int pimoidx = GetMenuObjbyId((int)lp->MenuObjectHandle); if (pimoidx == -1) { LeaveCriticalSection(&csMenuHook); return 0; } #if defined(_DEBUG) // DumpMenuItem(g_menus[pimoidx]->m_items.first); #endif INT_PTR res = (INT_PTR)BuildRecursiveMenu((HMENU)wParam, g_menus[pimoidx]->m_items.first, (ListParam*)lParam); LeaveCriticalSection(&csMenuHook); return res; } #ifdef _DEBUG #define PUTPOSITIONSONMENU #endif void GetMenuItemName(PMO_IntMenuItem pMenuItem, char* pszDest, size_t cbDestSize) { if (pMenuItem->UniqName) mir_snprintf(pszDest, cbDestSize, "{%s}", pMenuItem->UniqName); else if (pMenuItem->mi.flags & CMIF_UNICODE) { char* name = mir_t2a(pMenuItem->mi.ptszName); mir_snprintf(pszDest, cbDestSize, "{%s}", name); mir_free(name); } else mir_snprintf(pszDest, cbDestSize, "{%s}", pMenuItem->mi.pszName); } HMENU BuildRecursiveMenu(HMENU hMenu, PMO_IntMenuItem pRootMenu, ListParam *param) { if (param == NULL || pRootMenu == NULL) return NULL; TIntMenuObject* pmo = pRootMenu->parent; int rootlevel = (param->rootlevel == -1) ? 0 : param->rootlevel; ListParam localparam = *param; while (rootlevel == 0 && GetMenuItemCount(hMenu) > 0) DeleteMenu(hMenu, 0, MF_BYPOSITION); for (PMO_IntMenuItem pmi = pRootMenu; pmi != NULL; pmi = pmi->next) { PMO_MenuItem mi = &pmi->mi; if (mi->cbSize != sizeof(TMO_MenuItem)) continue; if (mi->flags & CMIF_HIDDEN) continue; if (pmo->CheckService != NULL) { TCheckProcParam CheckParam; CheckParam.lParam = param->lParam; CheckParam.wParam = param->wParam; CheckParam.MenuItemOwnerData = mi->ownerdata; CheckParam.MenuItemHandle = pmi; if (CallService(pmo->CheckService, (WPARAM)&CheckParam, 0) == FALSE) continue; } /**************************************/ if (rootlevel == 0 && mi->root == NULL && pmo->m_bUseUserDefinedItems) { char DBString[256]; DBVARIANT dbv = { 0 }; int pos; char MenuNameItems[256]; mir_snprintf(MenuNameItems, SIZEOF(MenuNameItems), "%s_Items", pmo->Name); char menuItemName[256]; GetMenuItemName(pmi, menuItemName, sizeof(menuItemName)); // check if it visible mir_snprintf(DBString, SIZEOF(DBString), "%s_visible", menuItemName); if (DBGetContactSettingByte(NULL, MenuNameItems, DBString, -1) == -1) DBWriteContactSettingByte(NULL, MenuNameItems, DBString, 1); pmi->OverrideShow = TRUE; if ( !DBGetContactSettingByte(NULL, MenuNameItems, DBString, 1)) { pmi->OverrideShow = FALSE; continue; // find out what value to return if not getting added } // mi.pszName mir_snprintf(DBString, SIZEOF(DBString), "%s_name", menuItemName); if ( !DBGetContactSettingTString(NULL, MenuNameItems, DBString, &dbv)) { if (_tcslen(dbv.ptszVal) > 0) { if (pmi->CustomName) mir_free(pmi->CustomName); pmi->CustomName = mir_tstrdup(dbv.ptszVal); } DBFreeVariant(&dbv); } mir_snprintf(DBString, SIZEOF(DBString), "%s_pos", menuItemName); if ((pos = DBGetContactSettingDword(NULL, MenuNameItems, DBString, -1)) == -1) { DBWriteContactSettingDword(NULL, MenuNameItems, DBString, mi->position); if (pmi->submenu.first) mi->position = 0; } else mi->position = pos; } /**************************************/ if (rootlevel != (int)pmi->mi.root) continue; MENUITEMINFO mii = { 0 }; mii.dwItemData = (LPARAM)pmi; int i = WhereToPlace(hMenu, mi); if ( !IsWinVer98Plus()) { mii.cbSize = MENUITEMINFO_V4_SIZE; mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID; mii.fType = MFT_STRING; } else { mii.cbSize = sizeof(mii); mii.fMask = MIIM_DATA | MIIM_ID | MIIM_STRING; if (pmi->iconId != -1) { mii.fMask |= MIIM_BITMAP; if (IsWinVerVistaPlus() && isThemeActive()) { if (pmi->hBmp == NULL) pmi->hBmp = ConvertIconToBitmap(NULL, pmi->parent->m_hMenuIcons, pmi->iconId); mii.hbmpItem = pmi->hBmp; } else mii.hbmpItem = HBMMENU_CALLBACK; } } mii.fMask |= MIIM_STATE; mii.fState = ((pmi->mi.flags & CMIF_GRAYED) ? MFS_GRAYED : MFS_ENABLED); mii.fState |= ((pmi->mi.flags & CMIF_CHECKED) ? MFS_CHECKED : MFS_UNCHECKED); if (pmi->mi.flags & CMIF_DEFAULT) mii.fState |= MFS_DEFAULT; mii.dwTypeData = (pmi->CustomName) ? pmi->CustomName : mi->ptszName; // it's a submenu if (pmi->submenu.first) { mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = CreatePopupMenu(); #ifdef PUTPOSITIONSONMENU if (GetKeyState(VK_CONTROL) & 0x8000) { TCHAR str[256]; mir_sntprintf(str, SIZEOF(str), _T("%s (%d, id %x)"), mi->pszName, mi->position, mii.dwItemData); mii.dwTypeData = str; } #endif InsertMenuItemWithSeparators(hMenu, i, &mii); localparam.rootlevel = LPARAM(pmi); BuildRecursiveMenu(mii.hSubMenu, pmi->submenu.first, &localparam); } else { mii.wID = pmi->iCommand; #ifdef PUTPOSITIONSONMENU if (GetKeyState(VK_CONTROL) & 0x8000) { TCHAR str[256]; mir_sntprintf(str, SIZEOF(str), _T("%s (%d, id %x)"), mi->pszName, mi->position, mii.dwItemData); mii.dwTypeData = str; } #endif if (pmo->onAddService != NULL) if (CallService(pmo->onAddService, (WPARAM)&mii, (LPARAM)pmi) == FALSE) continue; InsertMenuItemWithSeparators(hMenu, i, &mii); } } return hMenu; } ///////////////////////////////////////////////////////////////////////////////////////// // iconlib in menu static int MO_ReloadIcon(PMO_IntMenuItem pmi, void*) { if (pmi->hIcolibItem) { HICON newIcon = IcoLib_GetIconByHandle(pmi->hIcolibItem, false); if (newIcon) ImageList_ReplaceIcon(pmi->parent->m_hMenuIcons, pmi->iconId, newIcon); IconLib_ReleaseIcon(newIcon, 0); } return FALSE; } int OnIconLibChanges(WPARAM, LPARAM) { EnterCriticalSection(&csMenuHook); for (int mo=0; mo < g_menus.getCount(); mo++) if ((int)hStatusMenuObject != g_menus[mo]->id) //skip status menu MO_RecursiveWalkMenu(g_menus[mo]->m_items.first, MO_ReloadIcon, 0); LeaveCriticalSection(&csMenuHook); cli.pfnReloadProtoMenus(); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // static int MO_RegisterIcon(PMO_IntMenuItem pmi, void*) { char *uname, *descr; uname = pmi->UniqName; if (uname == NULL) uname = mir_u2a(pmi->CustomName); descr = mir_u2a(pmi->mi.ptszName); if ( !uname && !descr) return FALSE; if ( !pmi->hIcolibItem) { HICON hIcon = ImageList_GetIcon(pmi->parent->m_hMenuIcons, pmi->iconId, 0); char* buf = NEWSTR_ALLOCA(descr); char sectionName[256], iconame[256]; mir_snprintf(sectionName, sizeof(sectionName), "Menu Icons/%s", pmi->parent->Name); // remove '&' char* start = buf; while (start) { if ((start = strchr(start, '&')) == NULL) break; memmove(start, start+1, strlen(start+1)+1); if (*start != '\0') start++; else break; } mir_snprintf(iconame, sizeof(iconame), "genmenu_%s_%s", pmi->parent->Name, uname && *uname ? uname : descr); SKINICONDESC sid={0}; sid.cbSize = sizeof(sid); sid.cx = 16; sid.cy = 16; sid.pszSection = sectionName; sid.pszName = iconame; sid.pszDefaultFile = NULL; sid.pszDescription = buf; sid.hDefaultIcon = hIcon; pmi->hIcolibItem = (HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); Safe_DestroyIcon(hIcon); if (hIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)iconame)) { ImageList_ReplaceIcon(pmi->parent->m_hMenuIcons, pmi->iconId, hIcon); IconLib_ReleaseIcon(hIcon, 0); } } if ( !pmi->UniqName) mir_free(uname); mir_free(descr); return FALSE; } int RegisterAllIconsInIconLib() { //register all icons for (int mo=0; mo < g_menus.getCount(); mo++) { if ((int)hStatusMenuObject == g_menus[mo]->id) //skip status menu continue; MO_RecursiveWalkMenu(g_menus[mo]->m_items.first, MO_RegisterIcon, 0); } return 0; } int TryProcessDoubleClick(HANDLE hContact) { int iMenuID = GetMenuObjbyId((int)hContactMenuObject); if (iMenuID != -1) { NotifyEventHooks(hPreBuildContactMenuEvent, (WPARAM)hContact, 0); PMO_IntMenuItem pimi = (PMO_IntMenuItem)MO_GetDefaultMenuItem((WPARAM)g_menus[ iMenuID ]->m_items.first, 0); if (pimi != NULL) { MO_ProcessCommand(pimi, (LPARAM)hContact); return 0; } } return 1; } ///////////////////////////////////////////////////////////////////////////////////////// // Static services int posttimerid; static VOID CALLBACK PostRegisterIcons(HWND, UINT, UINT_PTR, DWORD) { KillTimer(0, posttimerid); RegisterAllIconsInIconLib(); } static int OnModulesLoaded(WPARAM, LPARAM) { posttimerid = SetTimer((HWND)NULL, 0, 5, (TIMERPROC)PostRegisterIcons); HookEvent(ME_SKIN2_ICONSCHANGED, OnIconLibChanges); return 0; } static INT_PTR SRVMO_SetOptionsMenuObject(WPARAM, LPARAM lParam) { lpOptParam lpop = (lpOptParam)lParam; if (lpop == NULL) return 0; return MO_SetOptionsMenuObject(lpop->Handle, lpop->Setting, lpop->Value); } static INT_PTR SRVMO_SetOptionsMenuItem(WPARAM, LPARAM lParam) { lpOptParam lpop = (lpOptParam)lParam; if (lpop == NULL) return 0; return MO_SetOptionsMenuItem((PMO_IntMenuItem)lpop->Handle, lpop->Setting, lpop->Value); } int InitGenMenu() { InitializeCriticalSection(&csMenuHook); CreateServiceFunction(MO_BUILDMENU, MO_BuildMenu); CreateServiceFunction(MO_PROCESSCOMMAND, (MIRANDASERVICE)MO_ProcessCommand); CreateServiceFunction(MO_CREATENEWMENUOBJECT, MO_CreateNewMenuObject); CreateServiceFunction(MO_REMOVEMENUITEM, MO_RemoveMenuItem); CreateServiceFunction(MO_ADDNEWMENUITEM, (MIRANDASERVICE)MO_AddNewMenuItem); CreateServiceFunction(MO_MENUITEMGETOWNERDATA, MO_MenuItemGetOwnerData); CreateServiceFunction(MO_MODIFYMENUITEM, (MIRANDASERVICE)MO_ModifyMenuItem); CreateServiceFunction(MO_GETMENUITEM, MO_GetMenuItem); CreateServiceFunction(MO_GETDEFAULTMENUITEM, MO_GetDefaultMenuItem); CreateServiceFunction(MO_PROCESSCOMMANDBYMENUIDENT, MO_ProcessCommandByMenuIdent); CreateServiceFunction(MO_PROCESSHOTKEYS, (MIRANDASERVICE)MO_ProcessHotKeys); CreateServiceFunction(MO_REMOVEMENUOBJECT, MO_RemoveMenuObject); CreateServiceFunction(MO_GETPROTOROOTMENU, MO_GetProtoRootMenu); CreateServiceFunction(MO_SETOPTIONSMENUOBJECT, SRVMO_SetOptionsMenuObject); CreateServiceFunction(MO_SETOPTIONSMENUITEM, SRVMO_SetOptionsMenuItem); bIconsDisabled = DBGetContactSettingByte(NULL, "CList", "DisableMenuIcons", 0) != 0; EnterCriticalSection(&csMenuHook); bIsGenMenuInited = true; LeaveCriticalSection(&csMenuHook); HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); HookEvent(ME_OPT_INITIALISE, GenMenuOptInit); return 0; } int UnitGenMenu() { if (bIsGenMenuInited) { EnterCriticalSection(&csMenuHook); MO_RemoveAllObjects(); bIsGenMenuInited=false; LeaveCriticalSection(&csMenuHook); DeleteCriticalSection(&csMenuHook); } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// TIntMenuObject::TIntMenuObject() { } TIntMenuObject::~TIntMenuObject() { MO_RecursiveWalkMenu(m_items.first, FreeMenuItem, NULL); FreeAndNil((void**)&FreeService); FreeAndNil((void**)&onAddService); FreeAndNil((void**)&CheckService); FreeAndNil((void**)&ExecService); FreeAndNil((void**)&Name); ImageList_Destroy(m_hMenuIcons); } void TIntMenuObject::freeItem(TMO_IntMenuItem* p) { if (FreeService) CallService(FreeService, (WPARAM)p, (LPARAM)p->mi.ownerdata); FreeAndNil((void**)&p->mi.pszName); FreeAndNil((void**)&p->UniqName); FreeAndNil((void**)&p->CustomName); if (p->hBmp) DeleteObject(p->hBmp); mir_free(p); }