/* Enhanced NCSA Mosaic from Spyglass "Guitar" Copyright 1994 Spyglass, Inc. All Rights Reserved Author(s): Eric W. Sink eric@spyglass.com Jim Seidman jim@spyglass.com Scott Piette scott@spyglass.com */ #include "all.h" #include "history.h" // // Note: This constant must be kept in sync with the number of items in // rc_menu.mnu. // // Number of items in the File menu prior to adding adding history items #define MIN_FILE_ITEMS 15 // Note: The last digit of the item number for a history menu item is used as the // accelerator for that item. Because the max number of items is currently 9, this // insures a unique accelerator for each item. If the constant below is set // larger than 9, this assumption will no longer be true. Nothing will break, there // will simply be more than one item with the same accelerator. #define MAX_HISTORY_RECENT_ITEMS 7 #define HISTORY_MENUID_MIN HISTHOT_MENUITEM_FIRST #define HISTORY_MENUID_MAX (HISTORY_MENUID_MIN + MAX_HISTORY_RECENT_ITEMS) #define FILE_ITEMS_SKIP 3 #define MAX_HOTLIST_RECENT_ITEMS 10 #define MIN_HOTLIST_ITEMS_ROOT 3 //Add Current to hot, Explore Hotlist, Separator #define MAX_HOTLIST_ITEMS (MIN_HOTLIST_ITEMS_ROOT + MAX_HOTLIST_RECENT_ITEMS) #define HOTLIST_MENUID_MIN (HISTORY_MENUID_MAX + 1) #define HOTLIST_MENUID_MAX HISTHOT_MENUITEM_LAST #ifdef OLD #define CMenuitemMax() (MAX_HOTLIST_RECENT_ITEMS) #else #define CMenuitemMax() (cMenuitemMax) #endif /* 4th Menuitem in the menubar */ #define INDEX_MENU_FILE 0 #define INDEX_MENU_NAVIGATE 3 #ifdef FEATURE_OPTIONS_MENU #define INDEX_MENU_HOTLIST 5 #else #define INDEX_MENU_HOTLIST 4 #endif #define MAX_MENUITEM_LEN 80 #ifdef XX_DEBUG /* max no. of subdirs/menuitems to recurse through while * building/freeing list ofmenuitems */ #define MAX_DIRS 30 //arbitrary #define MAX_RECURSE (MAX_DIRS + MAX_HOTLIST_ITEMS) #endif /* Kiosk mode flag- true means that we have no menu */ #ifdef FEATURE_BRANDING extern BOOL bKioskMode; #endif typedef struct _IEMENUINFO { HMENU hMenu; char szURLDisplay[MAX_PATH]; char szURLFilePath[MAX_PATH]; BOOL fCheckMark; BOOL fSubMenu; FILETIME ftLastAccessTime; BOOL fMore; //Is this the More... menuitem struct _IEMENUINFO *pmiSub; struct _IEMENUINFO *pmiNext; } IEMENUINFO, MI, *PMI, *LPIEMENUINFO; typedef struct _THREADPARAMS { HWND hWnd; HMENU hMenuWnd; } MENUTHREADPARAMS, *LPMENUTHREADPARAMS; PRIVATE void BuildHistoryHotlistMenuDir( PCSTR pcszDir, PMI *ppmi, HMENU hMenuParent, BOOL fMoreMenu); DWORD WINAPI MenuUpdateProc(LPMENUTHREADPARAMS lpmtp); static BOOL FIgnoreFind(LPWIN32_FIND_DATA pwfd); PRIVATE BOOL FInsertMenuitemList( HMENU hMenuParent, LPWIN32_FIND_DATA pwfd, PCSTR pcszDir, PMI *ppmi, int *pcMenuitems); PRIVATE BOOL FInsertMoreMenuitemList( HMENU hMenuParent, PCSTR pcszDir, PMI *ppmi, int *pcMenuitems); PRIVATE BOOL FCreateMenuitem(PMI pmi, HMENU hMenuParent); static void GetMenuText(PSTR pszMenuText, PCSTR pszFn, PCSTR pcszDir, int iPrefix); PRIVATE int CompPmi(PMI pmi1, PMI pmi2); PRIVATE void FreePMI(PMI pmi); PRIVATE HBITMAP HbmpShrinkBitmap ( HWND hwnd, HBITMAP hbm); static void CC_HandleHistoryMenu(HWND hWnd, UINT wID); static BOOL FGetMiiFromWid(HMENU hMenu, UINT wId, LPMENUITEMINFO lpmii); static void DestroyHistoryHotlistMenus(HMENU hMenu, BOOL fToplevel); DebugCode(static void CleanupHistoryMenus(void)); static void SetMenuitemMax(void); PRIVATE void SetReloadCheckBitmaps(); static UINT wMenuitemCur=HOTLIST_MENUID_MIN; static HANDLE hChange=INVALID_HANDLE_VALUE; static HANDLE hMenuMutex=NULL; static HANDLE hMenuThread=NULL; static int cMenuitemMax=0; far struct hash_table *pFavUrlHash=NULL; static ULONG guShellFSRegisterID=0L; #define HMenuHotlistFromHwnd(hwnd) (GetSubMenu(GetMenu(hwnd), INDEX_MENU_HOTLIST)) #include "hthot2.c" BOOL HotList_Add(PCSTR title, PCSTR url) { PCSTR pTitle; BOOL bPrintable; int i = 1; HRESULT hr; if ( title && *title ) { /* Insure there are printable characters in the field */ /* If none, put URL into hotlist instead of <TITLE> */ bPrintable = FALSE; while ( title[i] && !bPrintable ) { bPrintable = ((title[0] != ' ') && (title[0] != '\n') && (title[0] != '\t') && (title[0] != '\r')); i++; } if (bPrintable) { pTitle = title; } else { pTitle = url; } } else { pTitle = url; } hr = CreateURLShortcut(url, title, NULL, FOLDER_FAVORITES, 0); /* Don't put up an error unless there was an error. Abort => user cancelled */ return (hr == S_OK || hr == E_ABORT); } static HANDLE MyFindFirstChangeNotification(PCSTR pszDir) { return FindFirstChangeNotification( pszDir, /*fSubTree=*/TRUE, ( FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_DIR_NAME)); } void BuildHistoryHotlistMenus(HWND hWnd) { static BOOL fMenusInited=FALSE; char szDir[MAX_PATH]; DWORD dwId; PMI pmiHot=NULL; HMENU hMenuHotlist; if (GetInternetScDir(szDir, ID_HOTLIST) != S_OK) return; if (fMenusInited) goto LInited; fMenusInited=TRUE; if (!(pFavUrlHash = Hash_Create())) { XX_Assert(FALSE, ("Couldn't allocate hash table for pFavUrlHash")); pFavUrlHash = NULL; return; } SetMenuitemMax(); if (!FExistsDir(szDir, TRUE, FALSE)) goto LSkipFileChangeNotif; hChange = MyFindFirstChangeNotification(szDir); if ( (hChange != INVALID_HANDLE_VALUE) && !(hMenuMutex = CreateMutex(NULL, FALSE, NULL))) goto LCloseNotif; if (hChange != INVALID_HANDLE_VALUE) { hMenuThread = CreateThread( NULL, //Default security 0x1000, //stack size MenuUpdateProc, NULL, CREATE_SUSPENDED, //wait till we've inited menus &dwId); if (!hMenuThread) { LCloseNotif: FindCloseChangeNotification(hChange); hChange = INVALID_HANDLE_VALUE; } } { LPITEMIDLIST pidlFavs; SHChangeNotifyEntry fsne; if (SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidlFavs) == S_OK) { fsne.pidl = pidlFavs; fsne.fRecursive = TRUE; #ifdef WINNT_SHELL32_DESIGN_NOT_FIXED guShellFSRegisterID = OldSHChangeNotifyRegister( wg.hWndHidden, SHCNRF_ShellLevel, SHCNE_RENAMEFOLDER, WM_FAVS_UPDATE_NOTIFY, 1, &fsne); #else guShellFSRegisterID = SHChangeNotifyRegister( wg.hWndHidden, SHCNRF_ShellLevel, SHCNE_RENAMEFOLDER, WM_FAVS_UPDATE_NOTIFY, 1, &fsne); #endif } } if (hChange) FindNextChangeNotification(hChange); LSkipFileChangeNotif: LInited: hMenuHotlist = HMenuHotlistFromHwnd(hWnd); XX_Assert(hMenuHotlist, ("Null hMenuHotlist!")); if (hMenuMutex) { WaitForSingleObject(hMenuMutex, INFINITE); #ifdef TEMP0 XX_DebugMessage("hMenuMutex taken by BuildHistoryHotlistMenus"); #endif } BuildHistoryHotlistMenuDir(szDir, &pmiHot, hMenuHotlist, /*fMoreMenu=*/FALSE); FreePMI(pmiHot); pmiHot = NULL; if (hMenuMutex) { ReleaseMutex(hMenuMutex); #ifdef TEMP0 XX_DebugMessage("hMenuMutex released by BuildHistoryHotlistMenus"); #endif } if (hMenuThread) ResumeThread(hMenuThread); UpdateHistoryMenus((struct Mwin *)GetPrivateData(hWnd)); /* DrawMenuBar(hWnd) is already called by caller */ } static void SetMenuitemMax(void) { HDC hDC; NONCLIENTMETRICS nclm; HFONT hFont, hFontSav; TEXTMETRIC tm; POINT pt; int cyEdge, cyMenuitem, cyScr; cMenuitemMax = MAX_HOTLIST_RECENT_ITEMS; //def val. if (hDC = GetDC(NULL)) { nclm.cbSize = sizeof(NONCLIENTMETRICS); if ( SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nclm, 0) && (hFont = CreateFontIndirect(&nclm.lfMenuFont))) { hFontSav = SelectObject(hDC, hFont); GetTextMetrics(hDC, &tm); pt.x = 0; pt.y = GetSystemMetrics(SM_CYEDGE); DPtoLP(hDC, &pt, 1); cyEdge = pt.y; pt.y = GetSystemMetrics(SM_CYSCREEN) - 2*GetSystemMetrics(SM_CYFIXEDFRAME); DPtoLP(hDC, &pt, 1); cyScr = pt.y; pt.y = GetSystemMetrics(SM_CYMENUSIZE)/2; DPtoLP(hDC, &pt, 1); // pt.y now contains max popup menu height. // // Height of each menuitem = height of font + external leading + // edge (which is height of underline + gap between underline // and menutext). #ifdef OLD cyMenuitem = max(tm.tmHeight + tm.tmExternalLeading + 2*cyEdge, pt.y); //cover upto 80% of screen height only cMenuitemMax = max((cyScr/cyMenuitem)*8/10, MAX_HOTLIST_RECENT_ITEMS); #else cyMenuitem = tm.tmHeight + tm.tmExternalLeading + 2*cyEdge; // Icons may determine menu item height if ( cyMenuitem < wg.cySmIcon + 2 * CYIMAGEGAP ) cyMenuitem = wg.cySmIcon + 2 * CYIMAGEGAP; // cover up to 80% of screen height only cMenuitemMax = max(((cyScr-pt.y/2)/cyMenuitem)*8/10, MAX_HOTLIST_RECENT_ITEMS); #endif SelectObject(hDC, hFontSav); DeleteObject(hFont); } ReleaseDC(NULL, hDC); } } DWORD WINAPI MenuUpdateProc(LPMENUTHREADPARAMS lpmtp) { DWORD dwWaitStatus; XX_Assert(hChange != INVALID_HANDLE_VALUE, ("File change notif. handle invalid!")); while (TRUE) { switch (dwWaitStatus = WaitForSingleObject(hChange, INFINITE)) { case WAIT_OBJECT_0: if (hChange == INVALID_HANDLE_VALUE) { hMenuThread = NULL; return(1); } UpdateHotlistMenus(ID_HOTLIST); if (!FindNextChangeNotification(hChange)) { XX_Assert(0,("FindNextChangeNotification failed. Error %d", GetLastError())); goto LError; } break; default: XX_Assert(FALSE, ("Error returned from WaitForSingleObject:%d", GetLastError())); LError: if (hChange != INVALID_HANDLE_VALUE) { FindCloseChangeNotification(hChange); hChange=INVALID_HANDLE_VALUE; } if (hMenuMutex) { while(ReleaseMutex(hMenuMutex)); CloseHandle(hMenuMutex); hMenuMutex = NULL; } /* DeRegister from Shell notifications */ if (guShellFSRegisterID) { #ifdef WINNT_SHELL32_DESIGN_NOT_FIXED OldSHChangeNotifyDeregister(guShellFSRegisterID); #else SHChangeNotifyDeregister(guShellFSRegisterID); #endif guShellFSRegisterID = 0L; } /* tell the world we are done with this thread */ hMenuThread = NULL; ExitThread(GetLastError()); return (DWORD)-1; } } return 0; } void CleanupHistoryHotlistMenus(void) { /* Called upon WM_DESTROY of parent window */ /* Wait for Mutex release */ if (hMenuMutex) { #ifdef TEMP0 XX_DebugMessage("hMenuMutex taken by CleanupHistoryHotlistMenus"); #endif WaitForSingleObject(hMenuMutex, INFINITE); } if (pFavUrlHash) { Hash_Destroy(pFavUrlHash); pFavUrlHash = NULL; } /* close filechangenotif. handle */ if (hChange != INVALID_HANDLE_VALUE) { HANDLE hTmp = hChange; hChange = INVALID_HANDLE_VALUE; FindCloseChangeNotification(hTmp); } /* DeRegister from Shell notifications */ if (guShellFSRegisterID) { #ifdef WINNT_SHELL32_DESIGN_NOT_FIXED OldSHChangeNotifyDeregister(guShellFSRegisterID); #else SHChangeNotifyDeregister(guShellFSRegisterID); #endif guShellFSRegisterID = 0L; } /* Close mutex handle */ if (hMenuMutex) { while(ReleaseMutex(hMenuMutex)); #ifdef TEMP0 XX_DebugMessage("hMenuMutex released by CleanupHistoryHotlistMenus"); #endif CloseHandle(hMenuMutex); hMenuMutex = NULL; } /* finally, kill thread */ // BUGBUG: evil API #if 0 /* This should not need to be called, as the close of hChange * should cause that thread top close out properly */ if(hMenuThread) { TRACE_OUT(("TerminateThread( 0x%lx, 0 ) in CleanupHistoryHotlistMenus\n", hMenuThread)); TerminateThread(hMenuThread, 0); hMenuThread = NULL; } #endif wMenuitemCur=HOTLIST_MENUID_MIN; } void WaitHotlistMenus(HWND hWnd, HMENU hMenu) { /* User clicked on menu, we could be updating Favs. */ if (hMenuMutex) { #ifdef TEMP0 XX_DebugMessage("Waiting for hMenuMutex in WaitHotlistMenus"); #endif WaitForSingleObject(hMenuMutex, INFINITE); ReleaseMutex(hMenuMutex); #ifdef TEMP0 XX_DebugMessage("hMenuMutex taken and released by WaitHotlistMenus"); #endif } } void UpdateHotlistMenus(UINT updId) { struct Mwin *tw; char szDir[MAX_PATH+1]; HMENU hMenuHot; PMI pmiHot = NULL; DWORD dwId; struct Mwin **twSeen = NULL; struct Mwin **twTemp; int cbSeen = 0; int i; #ifdef FEATURE_BRANDING if (bKioskMode) return; #endif if (hMenuMutex) { #ifdef TEMP0 XX_DebugMessage("hMenuMutex taken by UpdateHotlistMenus"); #endif WaitForSingleObject(hMenuMutex, INFINITE); } if (updId & ID_SYSCHANGE) { SetReloadCheckBitmaps(); SetMenuitemMax(); } if (GetInternetScDir(szDir, ID_HOTLIST) != S_OK) goto LRet; if ( (updId & ID_UPDATEDIR) && (hChange != INVALID_HANDLE_VALUE)) { /* Favs. folder has moved (shell tracking) Reset change notif. with filesys */ FindCloseChangeNotification(hChange); // BUGBUG: evil API TRACE_OUT(("TerminateThread( 0x%lx, 0 ) in UpdateHotlistMenus\n",hMenuThread)); TerminateThread(hMenuThread, 0); if ((hChange = MyFindFirstChangeNotification(szDir)) == INVALID_HANDLE_VALUE) goto LRet; hMenuThread = CreateThread( NULL, //Default security 0x1000, //stack size MenuUpdateProc, NULL, CREATE_SUSPENDED, //wait till we've inited menus &dwId); if (!hMenuThread) { FindCloseChangeNotification(hChange); hChange = INVALID_HANDLE_VALUE; goto LRet; } } // BUGBUG - this is not thread safe - the other thread could be // creating/deleting windows at the same time // This is a quick hack to lower vulnerability to GIF fake mwins // which come and go quickly during download while (1) { for (tw = Mlist; tw; tw = tw->next) { if (tw->wintype == GHTML) { for (i = 0; i < cbSeen; i++) { if (tw == twSeen[i]) goto continueFor; } break; } continueFor: /* NULL */; } if (!tw) break; twTemp = cbSeen ? GTR_REALLOC(twSeen,(cbSeen+1)*sizeof(tw)) : GTR_MALLOC((cbSeen+1)*sizeof(tw)); if (!twTemp) break; twSeen = twTemp; twSeen[cbSeen++] = tw; hMenuHot = HMenuHotlistFromHwnd(tw->hWndFrame); DestroyHistoryHotlistMenus(hMenuHot, /*fTopLevel=*/TRUE); wMenuitemCur=HOTLIST_MENUID_MIN; BuildHistoryHotlistMenuDir(szDir, &pmiHot, hMenuHot, /*fMoreMenu=*/FALSE); FreePMI(pmiHot); pmiHot = NULL; } if (twSeen) GTR_FREE(twSeen); LRet: if (hMenuMutex) { #ifdef TEMP0 XX_DebugMessage("hMenuMutex released by UpdateHotlistMenus"); #endif ReleaseMutex(hMenuMutex); } if ((updId & ID_UPDATEDIR) && hMenuThread) ResumeThread(hMenuThread); } static void DestroyHistoryHotlistMenus(HMENU hMenu, BOOL fTopLevel) { int nMenu, nMenuMin; MENUITEMINFO mii; /* Don't delete the Explore Hotlist.... and separator menuitems */ nMenuMin = (fTopLevel ? MIN_HOTLIST_ITEMS_ROOT : 0); for (nMenu = GetMenuItemCount(hMenu) - 1; nMenu >= nMenuMin; nMenu--) { /* REVIEW: We don't need to delete all the submenus individually. * DeleteMenu will delete nested popup menus. */ /* set init. vals to avoid random results */ mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_SUBMENU | MIIM_ID; mii.hSubMenu = NULL; if (!GetMenuItemInfo(hMenu, nMenu, /*fByPosition=*/TRUE, &mii)) { XX_Assert(0, ("Couldn't get menuitem info.!")); continue; } if (mii.hSubMenu) DestroyHistoryHotlistMenus(mii.hSubMenu, FALSE); DeleteMenu(hMenu, nMenu, MF_BYPOSITION); } } PRIVATE void BuildHistoryHotlistMenuDir( PCSTR pcszDir, PMI *ppmi, HMENU hMenuParent, BOOL fMoreMenu) { const char cszFileFilter[] = "\\*"; PMI pmi; int cMenuitems=0; WIN32_FIND_DATA wfd; HANDLE hFind; static char szFileFind[MAX_PATH + 1]; DebugCode(static int cRecurse=0); # define szFullDir szFileFind XX_Assert(ppmi, ("ppmi is NULL")); XX_Assert(cRecurse < MAX_RECURSE, ("Careful! Too much recursion")); XX_Assert(IsMenu(hMenuParent), ("hMenuParent invalid!")); #ifdef TEMP0 XX_DebugMessage("BuildHistoryHotlistMenuDir: Adding dir %s", pcszDir); #endif strcpy(szFileFind, pcszDir); strcat(szFileFind, cszFileFilter); if ((hFind = FindFirstFile(szFileFind, &wfd)) == INVALID_HANDLE_VALUE) goto LInsertNullItem; do { if (FIgnoreFind(&wfd)) continue; if (!FInsertMenuitemList(hMenuParent, &wfd, pcszDir, ppmi, &cMenuitems)) break; } while (FindNextFile(hFind, &wfd)); if (!cMenuitems) { LInsertNullItem: FCreateMenuitem(NULL, hMenuParent); goto LEnd; } if ( fMoreMenu && cMenuitems == (CMenuitemMax() - 1) && !FInsertMoreMenuitemList(hMenuParent, pcszDir, ppmi, &cMenuitems)) goto LEnd; for (pmi=*ppmi; pmi; pmi=pmi->pmiNext) { FCreateMenuitem(pmi, hMenuParent); if (!pmi->fSubMenu) continue; DebugCode(cRecurse++); BuildHistoryHotlistMenuDir(pmi->szURLFilePath, &pmi->pmiSub, pmi->hMenu, TRUE); DebugCode(cRecurse--); } LEnd: FindClose(hFind); } const char cszTrail[]="..."; #define cchTrail (sizeof(cszTrail) - 1) const char cszURLExt[]=".url"; #define cchURLExt (sizeof(cszURLExt) - 1) static BOOL FIgnoreFind(LPWIN32_FIND_DATA pwfd) { /* Use ShGetFileInfo */ # define NUM_IGNORE 2 const char cszDot[]="."; const char cszDotDot[]=".."; const char *rgszIgnore[NUM_IGNORE] = { cszDot, cszDotDot }; int i, cch; PSTR pszFn = pwfd->cFileName; for (i=0; i<NUM_IGNORE; i++) { if (!lstrcmpi(pszFn, rgszIgnore[i])) return TRUE; } if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return TRUE; if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return FALSE; return ( (cch = lstrlen(pszFn)) < cchURLExt || lstrcmpi(&pszFn[cch - cchURLExt], cszURLExt)); # undef NUM_IGNORE } static void GetMenuText(PSTR pszMenuText, PCSTR pszFn, PCSTR pcszDir, int iPrefix) { const char cszMenuTextFmt[] = "%s&%u %s"; int cch; #ifdef DISPLAYNAME SHFILEINFO shfi; #endif BOOL fShell=FALSE; char szMenuTextT[_MAX_PATH+1]; if (pszFn && pcszDir) { strcpy(pszMenuText, pcszDir); strcat(pszMenuText, "\\"); strcat(pszMenuText, pszFn); #ifdef DISPLAYNAME /* Calling SHGetFileInfo is too much of a perf. hit, esp. since it * manifests itself at boot time. It's ok to use filename because * our filenames are already friendly. What's more, the display names * are infact the same as the filenames (atleast for now). */ if (fShell = SHGetFileInfo(pszMenuText, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_DISPLAYNAME|SHGFI_USEFILEATTRIBUTES)) strcpy(pszMenuText, shfi.szDisplayName); else #endif strcpy(pszMenuText, pszFn); } if ((cch = lstrlen(pszMenuText)) < cchURLExt) goto LPrefix; /* Don't remove .url from display name returned by Shell */ if (!fShell && !lstrcmpi(&pszMenuText[cch - cchURLExt], cszURLExt)) pszMenuText[cch - cchURLExt] = '\0'; /* Chop off if too long */ if (lstrlen(pszMenuText) > MAX_MENUITEM_LEN - cchTrail) #ifdef FEATURE_INTL /* If 'pszMenuText[MAX_MENUITEM_LEN - cchTrail]' is DBCS secondary byte, */ /* we should cut string at DBCS primary byte to decrease offset. */ if (IsFECodePage(GetACP())) { BOOL fDBCS = FALSE; cch = 0; while(cch < MAX_MENUITEM_LEN - cchTrail){ fDBCS = IsDBCSLeadByte(pszMenuText[cch]); if(fDBCS && (cch == (MAX_MENUITEM_LEN - cchTrail - 1))) break; cch += (fDBCS) ? 2 : 1; } strcpy(&pszMenuText[cch], cszTrail); } else #endif strcpy(&pszMenuText[MAX_MENUITEM_LEN - cchTrail], cszTrail); LPrefix: if (iPrefix != -1) { char tempStr[12]; // will accomodate largest possible int int topDigits = (iPrefix+1) / 10; // All but last decimal digit EscapeForAcceleratorChar( szMenuTextT, sizeof(szMenuTextT), pszMenuText ); if ( topDigits ) sprintf( tempStr, "%u", topDigits ); else tempStr[0] = 0; // Note: This code makes the last digit of the item number the accelerator // for that item. Because the max number of items is currently 9, this // insures a unique accelerator for each item. wsprintf(pszMenuText, cszMenuTextFmt, tempStr, (iPrefix+1) % 10, szMenuTextT); } else { // This is a favorite menu item, so prepend the flag charcter that will // be used to determine if the item is a URL or folder item with submenu. strcpy(szMenuTextT, pszMenuText); pszMenuText[0] = MENU_TEXT_URL_FLAG_CHAR; strcpy(pszMenuText + 1, szMenuTextT ); } } PRIVATE BOOL FInsertMenuitemList( HMENU hMenuParent, LPWIN32_FIND_DATA pwfd, PCSTR pcszDir, PMI *ppmi, int *pcMenuitems) { PMI pmi, pmiT, pmiIns; pmi = (PMI)GTR_MALLOC(sizeof(MI)); if (!pmi) { XX_Assert(0, ("Cannot allocate mem for menuitem")); return FALSE; } pmi->hMenu = NULL; GetMenuText(pmi->szURLDisplay, pwfd->cFileName, pcszDir, -1); /* Save full path of url file */ strcpy(pmi->szURLFilePath, pcszDir); strcat(pmi->szURLFilePath, "\\"); strcat(pmi->szURLFilePath, pwfd->cFileName); pmi->fMore = FALSE; pmi->fCheckMark = FALSE; /* Not current page from history */ pmi->fSubMenu = (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); pmi->ftLastAccessTime = pwfd->ftLastAccessTime; pmi->pmiNext = pmi->pmiSub = NULL; // If this item has a sub menu, set the first character of the menu text to // the flag value that indicates submenu vs. URL if (pmi->fSubMenu) pmi->szURLDisplay[0] = MENU_TEXT_SUB_MENU_FLAG_CHAR; XX_Assert(ppmi, ("ppmi is NULL!")); pmiT= *ppmi; pmiIns = pmiT; if (!pmiT || CompPmi(pmi, pmiT) < 0) { pmi->pmiNext = pmiT; *ppmi = pmi; goto LInserted; } while (pmiIns->pmiNext) { if (CompPmi(pmi, pmiIns->pmiNext) < 0) { pmi->pmiNext = pmiIns->pmiNext; pmiIns->pmiNext = pmi; goto LInserted; } pmiIns = pmiIns->pmiNext; } pmiIns->pmiNext = pmi; LInserted: /* Leave room for the More... menuitem */ if (*pcMenuitems < CMenuitemMax() - 1) { *pcMenuitems += 1; return TRUE; } /* Need to remove last menuitem */ XX_Assert(pmiIns && pmiIns->pmiNext, ("Inserted menuitem or its parent is NULL!")); for (pmi=pmiIns; pmi->pmiNext && pmi->pmiNext->pmiNext; pmi=pmi->pmiNext); FreePMI(pmi->pmiNext); pmi->pmiNext = NULL; return TRUE; } PRIVATE BOOL FInsertMoreMenuitemList( HMENU hMenuParent, PCSTR pcszDir, PMI *ppmi, int *pcMenuitems) { PMI pmi, pmiT; XX_Assert(*pcMenuitems == (CMenuitemMax() - 1), ("More... menuitem inserted before max limit")); pmi = (PMI)GTR_MALLOC(sizeof(MI)); if (!pmi) { XX_Assert(0, ("Cannot allocate mem for menuitem")); return FALSE; } pmi->fMore = TRUE; /* This is the More... menuitem */ pmi->hMenu = NULL; LoadString(wg.hInstance, RES_MENU_STRING_MORE, pmi->szURLDisplay, MAX_PATH); strcpy(pmi->szURLFilePath, pcszDir); /* Save dir name */ pmi->fCheckMark = FALSE; /* Not current page from history */ pmi->fSubMenu = FALSE; pmi->pmiNext = pmi->pmiSub = NULL; XX_Assert(ppmi, ("ppmi is NULL!")); pmiT= *ppmi; for (pmiT = *ppmi; pmiT->pmiNext; pmiT = pmiT->pmiNext); pmiT->pmiNext = pmi; *pcMenuitems += 1; return TRUE; } static long dwOldCheckMark = 0; PRIVATE void SetReloadCheckBitmaps() { // Force Icon Reload dwOldCheckMark = 0; } PRIVATE BOOL FCreateMenuitem(PMI pmi, HMENU hMenuParent) { const char cszNull[] = ""; char szMenu[MAX_PATH]; HMENU hMenu; MENUITEMINFO mii; /* check for null pmi and add NULL menuitem */ if (!pmi) { if (!(hMenu = CreateMenu())) return FALSE; LoadString(wg.hInstance, RES_MENU_STRING_NULL, szMenu, MAX_PATH); if (AppendMenu(hMenuParent, MF_GRAYED|MF_STRING, (UINT)hMenu, (LPCSTR)szMenu)) return TRUE; else { /* Duh? Error */ DestroyMenu(hMenu); return FALSE; } } mii.cbSize = sizeof(MENUITEMINFO); if (pmi->fMore) { /* Insert a separator first */ mii.fMask = MIIM_TYPE; mii.fType = MFT_SEPARATOR; if (!InsertMenuItem(hMenuParent, (UINT)-1, TRUE, &mii)) return FALSE; mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_ID | MIIM_DATA; mii.fState = MFS_UNCHECKED; mii.fType = MFT_STRING; } else { mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_ID | MIIM_DATA; mii.fState = MFS_UNCHECKED; mii.fType = MFT_OWNERDRAW; } if (wMenuitemCur > HOTLIST_MENUID_MAX) { XX_Assert(FALSE, ("Wrapping wMenuitemCur around: no more menuitem id's")); wMenuitemCur = HOTLIST_MENUID_MIN; } XX_Assert(pFavUrlHash, ("")); mii.wID = wMenuitemCur++; if (pmi->fMore) { mii.dwTypeData = pmi->szURLDisplay; mii.cch = lstrlen(mii.dwTypeData); } else { mii.dwTypeData = 0; mii.cch = 0; } mii.dwItemData = Hash_FindOrAdd(pFavUrlHash, pmi->szURLFilePath, pmi->szURLDisplay, NULL); if (pmi->fSubMenu) { mii.fMask |= MIIM_SUBMENU; if (!(mii.hSubMenu = CreatePopupMenu())) return FALSE; } else { mii.hSubMenu = NULL; } if (!InsertMenuItem(hMenuParent, (UINT)-1, TRUE, &mii)) { if (pmi->fSubMenu) DestroyMenu(mii.hSubMenu); return FALSE; } if (!pmi->fSubMenu) return TRUE; if (!(pmi->hMenu = GetSubMenu(hMenuParent, GetMenuItemCount(hMenuParent) - 1))) { XX_Assert(FALSE, ("Could find hMenu for newly inserted menuitem!")); return FALSE; } return TRUE; } /* * Returns: if (pmi1 < pmi2) -1 * else 0 or 1 (depending on result of strcmpi) */ PRIVATE int CompPmi(PMI pmi1, PMI pmi2) { /* Rules for comparison: * Directories are "<" files. * szDir1 "<" szDir2 if szDir1 is alphabetically < szDir2 * szFile1 "<" szFile2 if szFile1 is alphabetically < szFile2 */ /* One's a dir and the other is not */ if (pmi1->fSubMenu ^ pmi2->fSubMenu) return (pmi2->fSubMenu - pmi1->fSubMenu); return lstrcmpi(pmi1->szURLDisplay, pmi2->szURLDisplay); } PRIVATE void FreePMI(PMI pmi) { PMI pmiCur; DebugCode(static int cRecurseFree=0); XX_Assert(cRecurseFree < MAX_RECURSE, ("Too much recursion")); while (pmiCur = pmi) { pmi = pmi->pmiNext; if (pmiCur->pmiSub) { DebugCode(cRecurseFree++); FreePMI(pmiCur->pmiSub); DebugCode(cRecurseFree--); } GTR_FREE(pmiCur); } } #define FHistoryID(wID) (wID >= HISTORY_MENUID_MIN && wID <= HISTORY_MENUID_MAX) void CC_Handle_HistoryHotlistMenu(HWND hWnd, UINT wID) { char szURL[MAX_URL_STRING+1]; MENUITEMINFO mii; struct Mwin *tw = GetPrivateData(hWnd); char szMsg[64]; PSTR pszURLFilePath; HMENU hMenuHotlist = HMenuHotlistFromHwnd(hWnd); XX_Assert((wID >= HISTHOT_MENUITEM_FIRST && wID <= HISTHOT_MENUITEM_LAST), ("Invalid menuitem id for history/hotlist!")); if (FHistoryID(wID)) { CC_HandleHistoryMenu(hWnd, wID); return; } if (!FGetMiiFromWid(hMenuHotlist, wID, &mii)) { XX_Assert(FALSE, ("Couldn't find wID in menuitems")); return; } /* mii.dwItemData has the index into gFavUrls that has full URL path */ if (mii.dwItemData == -1) return; XX_Assert(pFavUrlHash, ("")); Hash_GetIndexedEntry(pFavUrlHash, mii.dwItemData, &pszURLFilePath, NULL, NULL); if (FExistsFile((PCSTR)pszURLFilePath, FALSE, NULL)) { if (!FGetURLString((PCSTR)pszURLFilePath, szURL)) { XX_Assert(FALSE, ("Couldn't get URL from file: %s", (PSTR)pszURLFilePath)); } else { /* REVIEW (deepaka): /* Either load the document internally or launch explorer to handle * it the same way it would had the user clicked on a url shortcut * in the explorer. */ TW_LoadDocument(tw, szURL, TW_LD_FL_RECORD, NULL, NULL); } } else if (FExistsDir((PCSTR)pszURLFilePath, FALSE, FALSE)) { FExecExplorerAtShortcutsDir(ID_SUBDIR, pszURLFilePath); } else { GTR_formatmsg(RES_STRING_HTHOTLST1,szMsg,sizeof(szMsg)); MessageBox(hWnd, (PCSTR)pszURLFilePath, szMsg, MB_OK); } return; } static BOOL FGetMiiFromWid(HMENU hMenu, UINT wID, LPMENUITEMINFO lpmii) { int nMenu; lpmii->cbSize = sizeof(MENUITEMINFO); for (nMenu = GetMenuItemCount(hMenu) - 1; nMenu >= 0; nMenu--) { /* set init. vals to avoid random results */ lpmii->hSubMenu = NULL; lpmii->fMask = MIIM_STATE | MIIM_DATA | MIIM_ID | MIIM_SUBMENU; lpmii->dwItemData = 0; lpmii->fType = MFT_SEPARATOR; if (!GetMenuItemInfo(hMenu, nMenu, /*fByPosition=*/TRUE, lpmii)) continue; if (lpmii->wID == wID) return TRUE; if (lpmii->hSubMenu && FGetMiiFromWid(lpmii->hSubMenu, wID, lpmii)) return TRUE; } return FALSE; } void CC_OnItem_ExploreHistory(HWND hWnd) { FExecExplorerAtShortcutsDir(ID_HISTORY, NULL); } void CC_OnItem_ExploreHotlist(HWND hWnd) { FExecExplorerAtShortcutsDir(ID_HOTLIST, NULL); } void UpdateHistoryMenus(struct Mwin *tw) { HMENU hMenuFile; char szFFn[MAX_PATH+1]; MENUITEMINFO mii; int nMenu, nHist, nHistMin, nHistMax; UINT wId; XX_Assert(tw, ("Null tw in CC_HandleHistoryMenu")); #ifdef FEATURE_BRANDING if (bKioskMode) return; #endif hMenuFile = GetSubMenu(GetMenu(tw->hWndFrame), INDEX_MENU_FILE); mii.cbSize = sizeof(MENUITEMINFO); /* skip last menuitem (Explore history) */ for (nMenu = GetMenuItemCount(hMenuFile)-FILE_ITEMS_SKIP-1; nMenu >= 0; nMenu--) { /* set init. vals to avoid random results */ mii.hSubMenu = NULL; mii.fMask = MIIM_ID; mii.dwItemData = 0; if (!GetMenuItemInfo(hMenuFile, nMenu, /*fByPosition=*/TRUE, &mii)) break; /* if it is a history menuitem, delete it. The first non-history item * we hit is the sentinel for the completion of the deleting process. */ if (mii.wID >= HISTORY_MENUID_MIN && mii.wID <= HISTORY_MENUID_MAX) DeleteMenu(hMenuFile, nMenu, MF_BYPOSITION); else break; } if (!(nHist = HTList_count(tw->history))) { XX_Assert(GetMenuItemCount(hMenuFile) == MIN_FILE_ITEMS, ("tw->history items inconsistent with History Menuitems")); goto LRet; } mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_CHECKMARKS | MIIM_STATE | MIIM_TYPE | MIIM_ID | MIIM_DATA; mii.fType = MFT_STRING; mii.hbmpChecked = mii.hbmpUnchecked = NULL; mii.hSubMenu = NULL; /* nHist has count of recent history items */ nHistMin = max(0, min( tw->history_index - MAX_HISTORY_RECENT_ITEMS/2, nHist - MAX_HISTORY_RECENT_ITEMS)); nHistMax = nHistMin + min(MAX_HISTORY_RECENT_ITEMS, nHist); for (nHist=nHistMin, wId=HISTORY_MENUID_MIN; nHist < nHistMax; nHist++,wId++) { /* Get text to put into menuitem */ GetFriendlyFromURL((PSTR)HTList_objectAt(tw->history, nHist), szFFn, sizeof(szFFn), 0); GetMenuText(szFFn, NULL, NULL, nHist); mii.fState = (nHist == tw->history_index ? MFS_CHECKED : MFS_UNCHECKED); mii.wID = wId; mii.dwTypeData = szFFn; mii.cch = lstrlen(mii.dwTypeData); mii.dwItemData = nHist; //index into tw->history if (!InsertMenuItem(hMenuFile, RES_MENU_ITEM_EXPLORE_HISTORY, FALSE, &mii)) XX_Assert(0, ("Couldn't insert menuitem: %s \n just before Explore History menuitem", szFFn)); } LRet: DrawMenuBar(tw->hWndFrame); } static void CC_HandleHistoryMenu(HWND hWnd, UINT wId) { struct Mwin *tw = GetPrivateData(hWnd); PSTR pszURL; int indexTo; BOOL fNoCache=FALSE; HMENU hMenuFile; MENUITEMINFO mii; XX_Assert(tw, ("Null tw in CC_HandleHistoryMenu")); hMenuFile = GetSubMenu(GetMenu(tw->hWndFrame), INDEX_MENU_FILE); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_DATA | MIIM_ID; if (!GetMenuItemInfo(hMenuFile, wId, /*fByPosition=*/FALSE, &mii)) { XX_Assert(FALSE, ("")); return; } indexTo = mii.dwItemData; XX_Assert(indexTo < HTList_count(tw->history), ("Trying to load history item at invalid index!")); pszURL = (PSTR)HTList_objectAt(tw->history, indexTo); #ifdef FEATURE_INTL tw->iMimeCharSet = (int)HTList_objectAt(tw->MimeHistory, indexTo); #endif if (indexTo == tw->history_index) { if (tw->w3doc) pszURL = tw->w3doc->szActualURL; fNoCache = TRUE; } else { tw->history_index = indexTo; // UpdateHistoryMenuChecks(tw); } TW_LoadDocument( tw, pszURL, (fNoCache ? (TW_LD_FL_NO_DOC_CACHE | TW_LD_FL_NO_IMAGE_CACHE) : TW_LD_FL_AUTH_FAIL_CACHE_OK), NULL, tw->request->referer); /* Rely on TBar_Update_TBItems to call: * UpdateHistoryMenus(tw); */ } #if 0 /* * nMenuCheck should be (usually) tw->history_index */ void UpdateHistoryMenuChecks(struct Mwin *tw) { HMENU hMenuFile; int nMenu; int nMenuCheck=tw->history_index; hMenuFile = GetSubMenu(GetMenu(tw->hWndFrame), INDEX_MENU_FILE); nMenu=GetMenuItemCount(hMenuFile)-1-MIN_FILE_ITEMS; if (HTList_count(tw->history) <= MAX_HISTORY_RECENT_ITEMS) { /* Don't need to rebuild the menuitems from scratch. Just * update the checkmarks */ for (; nMenu >= 0; nMenu--) { CheckMenuItem( hMenuFile, nMenu+HISTORY_MENUID_MIN, MF_BYCOMMAND | (nMenu == nMenuCheck ? MF_CHECKED : MF_UNCHECKED)); } DrawMenuBar(tw->hWndFrame); } else { /* Might have to rebuild the history menuitems */ UpdateHistoryMenus(tw); } } #endif #ifdef XX_DEBUG static void CleanupHistoryMenus(void) { /* Nothing to cleanup: Windows destroys all the menus for us */ } #endif