Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1308 lines
33 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Eric W. Sink [email protected]
Jim Seidman [email protected]
Scott Piette [email protected]
*/
#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 <TITLE> 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