2020-09-30 17:12:32 +02:00

9758 lines
291 KiB
C

#include "cabinet.h"
#include "cabwnd.h"
#include "rcids.h"
#include <shellapi.h>
#include <shlapip.h>
#include "trayclok.h"
#include <fsmenu.h>
#include <shguidp.h>
#include "traynot.h"
#include <help.h>// help ids
#include <desktray.h>
#if defined(FE_IME)
#include <immp.h>
#endif
#include <trayp.h>
#include <regstr.h>
#ifdef WINNT
// NT APM support
#include <ntddapmt.h>
#else
// Win95 APM support
#include <vmm.h>
#include <bios.h>
#define NOPOWERSTATUSDEFINES
#include <pwrioctl.h>
#include <wshioctl.h>
#include <dbt.h>
#include <pbt.h>
#define Not_VxD
#define No_CM_Calls
#include <configmg.h>
#endif
#include "bandsite.h"
#include "mmhelper.h" // Multimonitor helper functions
#include <shdguid.h>
#include "startmnu.h"
#include "apithk.h"
#include "uemapp.h"
#include "deskconf.h"
#define DM_FOCUS 0 // focus
#define DM_SHUTDOWN TF_TRAY // shutdown
#define DM_UEMTRACE TF_TRAY // timer service, other UEM stuff
#define DM_MISC 0 // miscellany
#define POLLINTERVAL (15*1000) // 15 seconds
#define MINPOLLINTERVAL (3*1000) // 3 seconds
#define ERRTIMEOUT (5*1000) // 5 seconds
#define SECOND 1000
#define RUNWAITSECS 5
#define DOCKSTATE_DOCKED 0
#define DOCKSTATE_UNDOCKED 1
#define DOCKSTATE_UNKNOWN 2
#ifdef WINNT
#define MAXPRINTERBUFFER (MAX_PATH+18+1)
#define JOB_STATUS_ERROR_BITS (JOB_STATUS_USER_INTERVENTION|JOB_STATUS_ERROR)
#else
#define MAXPRINTERBUFFER 32
#define JOB_STATUS_ERROR_BITS JOB_STATUS_USER_INTERVENTION
#endif
#define RDM_ACTIVATE (WM_USER + 0x101)
// import the WIN31 Compatibility HACKs from the shell32.dll
WINSHELLAPI void WINAPI CheckWinIniForAssocs(void);
HWND v_hwndDesktop = NULL;
#ifdef WINNT
// event to tell the services on NT5 that we are done with boot and they can do their stuff
HANDLE g_hShellReadyEvent = NULL;
#endif
UINT g_uStartButtonAllowPopup = WM_NULL;
UINT g_uStartButtonBalloonTip = WM_NULL;
// Users and Passwords must send this message to get the "real" logged on user to log off.
// This is required since sometimes U&P runs in the context of a different user and logging this
// other user off does no good. See ext\netplwiz for the other half of this...-dsheldon.
UINT g_uLogoffUser = WM_NULL;
// DSA haPrinterNames structure:
typedef struct _PRINTERNAME {
TCHAR szPrinterName[MAXPRINTERBUFFER]; // we know max printer name length is 32
LPITEMIDLIST pidlPrinter; // relative pidl to printer
BOOL fInErrorState;
} PRINTERNAME, * LPPRINTERNAME;
void RaiseDesktop();
void PrintNotify_Init(HWND hwnd);
void PrintNotify_AddToQueue(LPHANDLE phaPrinterNames, LPSHELLFOLDER *ppsf, LPCITEMIDLIST pidlPrinter);
BOOL PrintNotify_StartThread(HWND hwnd);
void PrintNotify_HandleFSNotify(HWND hwnd, LPCITEMIDLIST *ppidl, LONG lEvent);
void PrintNotify_Exit(void);
void PrintNotify_IconNotify(HWND hwnd, LPARAM uMsg);
void RevertMenu(HMENU hmenuSub, UINT idMenu);
void _PropagateMessage(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
void Tray_HandleShellServiceObject(WPARAM wParam, LPARAM lParam);
STDMETHODIMP CShellTray_AddViewPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam);
HMONITOR Tray_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn, UINT uFlags);
HMONITOR Tray_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags);
void Tray_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size, UINT uStick);
void Tray_ScreenSizeChange(HWND hwnd);
void Tray_SizeWindows();
void Tray_ContextMenu(DWORD dwPos, BOOL fSetTime);
void Tray_DesktopMenu(DWORD dwPos);
void StuckAppChange(HWND hwnd, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray);
void Tray_StuckTrayChange();
void SetWindowStyleBit(HWND hwnd, DWORD dwBit, DWORD dwValue);
void CStartDropTarget_Register();
void CStartDropTarget_Revoke();
void DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions);
void Tray_ResetZorder();
void Cabinet_InitGlobalMetrics(WPARAM, LPTSTR);
void StartMenuFolder_ContextMenu(DWORD dwPos);
void Tray_HandleSize();
void Tray_HandleSizing(WPARAM code, LPRECT lprc, UINT uStuckPlace);
void MinimizeAll(HWND hwndView);
void Tray_RegisterGlobalHotkeys();
void Tray_UnregisterGlobalHotkeys();
void Tray_HandleGlobalHotkey(WPARAM wParam);
void Tray_Unhide();
void Tray_SetAutoHideTimer();
void Tray_ComputeHiddenRect(LPRECT prc, UINT uStuck);
UINT Tray_GetDockedRect(LPRECT prc, BOOL fMoving);
void Tray_CalcClipCoords(RECT *prcClip, const RECT *prcMonitor, const RECT *prcNew);
void Tray_ClipInternal(const RECT *prcClip);
void Tray_ClipWindow(BOOL fEnableClipping);
UINT Tray_CalcDragPlace(POINT pt);
UINT Tray_RecalcStuckPos(LPRECT prc);
void Tray_AutoHideCollision();
void StartMenu_Build();
int HotkeyList_Restore(HWND hwnd);
LRESULT Tray_RegisterHotkey(HWND hwnd, int i);
void RecordStartButtonSize(HWND hwndStart);
LRESULT Tray_HandleMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmi);
void Tray_OnDesktopState(LPARAM lParam);
#ifdef DESKBTN
void Tray_CreateDesktopButton();
HWND g_hwndDesktopTB = NULL;
#else
#define Tray_CreateDesktopButton() 0
#define KillConfigDesktopDlg() 0
#define g_hwndDesktopTB NULL
#endif
void ToggleDesktop();
void LowerDesktop();
void DoTrayProperties(INT nStartPage);
void RunStartupApps();
void ClearRecentDocumentsAndMRUStuff(BOOL fBroadcastChange);
void WriteCleanShutdown(DWORD dwValue);
void RefreshStartMenu();
// Review chrisny: this can be moved into an object easily to handle generic droptarget, dropcursor
// , autoscrool, etc. . .
void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject);
void _DragMove(HWND hwndTarget, const POINTL ptStart);
BOOL SetWindowZorder(HWND hwnd, HWND hwndInsertAfter);
// Settings UI entry point types.
typedef VOID (WINAPI *PTRAYPROPSHEETCALLBACK)(DWORD nStartPage);
typedef VOID (WINAPI *PSETTINGSUIENTRY)(PTRAYPROPSHEETCALLBACK);
DWORD SettingsUI_ThreadProc(void *pv);
VOID WINAPI SettingsUI_TrayPropSheetCallback(DWORD nStartPage);
extern void Cabinet_RefreshAll(void);
#if defined(DBCS) || defined(FE_IME)
// b#11258-win95d
#if !defined(WINNT)
DWORD WINAPI ImmGetAppIMECompatFlags(DWORD dwThreadID);
#endif
#endif
// Shell perf automation
extern DWORD g_dwShellStartTime;
extern DWORD g_dwShellStopTime;
// appbar stuff
BOOL IAppBarSetAutoHideBar(HWND hwnd, BOOL fAutoHide, UINT uEdge);
void IAppBarActivationChange(HWND hwnd, UINT uEdge);
HWND AppBarGetAutoHideBar(UINT uEdge);
// BOGUS: nuke this (multiple monitors...)
HWND g_hwndAutoHide[ABE_MAX] = { NULL, NULL, NULL, NULL };
extern BOOL g_fUseMerge;
BOOL IsChildOrHWND(HWND hwnd, HWND hwndChild)
{
return (hwnd == hwndChild || IsChild(hwnd, hwndChild));
}
void ClockCtl_HandleTrayHide(BOOL fHiding)
{
g_ts.fLastHandleTrayHide = fHiding ? TRUE : FALSE;
SendMessage(g_ts.hwndNotify, TNM_TRAYHIDE, 0, fHiding);
}
// dyna-res change for multi-config hot/warm-doc
void HandleDisplayChange(int x, int y, BOOL fCritical);
#ifdef WINNT
#define IsDisplayChangeSafe() TRUE
#else
BOOL IsDisplayChangeSafe(void);
#endif
DWORD GetMinDisplayRes(void);
// appbar stuff
void AppBarNotifyAll(HMONITOR hmon, UINT uMsg, HWND hwndExclude, LPARAM lParam);
// Button subclass.
WNDPROC g_ButtonProc = NULL;
// timer IDs
#define IDT_AUTOHIDE 2
#define IDT_AUTOUNHIDE 3
#ifdef DELAYWININICHANGE
#define IDT_DELAYWININICHANGE 5
#endif
#define IDT_DESKTOP 6
#define IDT_PROGRAMS IDM_PROGRAMS
#define IDT_RECENT IDM_RECENT
#define IDT_REBUILDMENU 7
#define IDT_HANDLEDELAYBOOTSTUFF 8
#define IDT_REVERTPROGRAMS 9
#define IDT_REVERTRECENT 10
#define IDT_REVERTFAVORITES 11
#define IDT_STARTMENU 12
#define IDT_ENDUNHIDEONTRAYNOTIFY 13
#define IDT_SERVICE0 14
#define IDT_SERVICE1 15
#define IDT_SERVICELAST IDT_SERVICE1
#define IDT_SAVESETTINGS 17
#define IDT_ENABLEUNDO 18
#define RECTWIDTH(rc) ((rc).right-(rc).left)
#define RECTHEIGHT(rc) ((rc).bottom-(rc).top)
#define SZ_RECENTKEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs\\Menu")
#define SZ_STARTMENUKEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Start Menu\\Menu")
#define REGSTR_EXPLORER_ADVANCED REGSTR_PATH_EXPLORER TEXT("\\Advanced")
int g_cyTrayBorders = -1; // the amount of Y difference between the window and client height;
// amount of time to show/hide the tray to turn sliding off set these to 0
int g_dtSlideHide;
int g_dtSlideShow;
typedef enum
{
// Sequence commands
SMCT_NONE = 0x0,
SMCT_PRIMARY = 0x1,
SMCT_WININIASSOCS = 0x2,
SMCT_DESKTOPHOTKEYS = 0x3,
SMCT_INITPROGRAMS = 0x4,
SMCT_INITRECENT = 0x5,
SMCT_INITFAVORITES = 0x6,
SMCT_PARTFILLPROGRAMS = 0x7,
SMCT_FILLRECENT = 0x8,
SMCT_FILLPROGRAMS = 0x9,
SMCT_FILLFASTITEMS = 0xa,
SMCT_FILLFAVORITES = 0xb,
SMCT_BUILDLISTOFPATHS = 0xc, // Must be last item in list before SMCT_DONE
SMCT_DONE = 0xd,
// Single execution commands
SMCT_FILLRECENTONLY = 0xe,
SMCT_FILLPROGRAMSONLY = 0xf,
SMCT_DESKTOPHOTKEYSONLY = 0x10,
SMCT_FILLFAVORITESONLY = 0x11,
SMCT_STOP = 0x12,
SMCT_RESTART = 0x13,
SMCT_STOPNOWAITBLOP = 0x14, // Stop, but don't wait for BuildListOfPaths to complete
} STARTMENUCONTROLTHREAD;
// INSTRUMENTATION WARNING: If you change anything here, make sure to update instrument.c
// we need to start at 500 because we're now sharing the hotkey handler
// with shortcuts.. they use an index array so they need to be 0 based
// NOTE, this constant is also in desktop.cpp, so that we can forward hotkeys from the desktop for
// NOTE, app compatibility.
#define GHID_FIRST 500
enum
{
GHID_RUN = GHID_FIRST,
GHID_MINIMIZEALL,
GHID_UNMINIMIZEALL,
GHID_HELP,
GHID_EXPLORER,
GHID_FINDFILES,
GHID_FINDCOMPUTER,
GHID_TASKTAB,
GHID_TASKSHIFTTAB,
GHID_SYSPROPERTIES,
GHID_DESKTOP,
GHID_MAX
};
const DWORD GlobalKeylist[] =
{
MAKELONG(TEXT('R'), MOD_WIN),
MAKELONG(TEXT('M'), MOD_WIN),
MAKELONG(TEXT('M'), MOD_SHIFT | MOD_WIN),
MAKELONG(VK_F1, MOD_WIN),
MAKELONG(TEXT('E'), MOD_WIN),
MAKELONG(TEXT('F'), MOD_WIN),
MAKELONG(TEXT('F'), MOD_CONTROL | MOD_WIN),
MAKELONG(VK_TAB, MOD_WIN),
MAKELONG(VK_TAB, MOD_WIN | MOD_SHIFT),
MAKELONG(VK_PAUSE, MOD_WIN),
MAKELONG(TEXT('D'), MOD_WIN),
};
void DoExitWindows(HWND hwnd);
void Tray_Command(UINT idCmd);
LONG Tray_SetAutoHideState(BOOL fAutoHide);
LRESULT CALLBACK Tray_WndProc(HWND, UINT, WPARAM, LPARAM);
// Global to this file only.
// NB None of the tray stuff needs thread serialising because
// we only ever have one tray.
TRAYSTUFF g_ts = {0};
// g_fDockingFlags bits
#define DOCKFLAG_WARMEJECTABLENOW (1<<0)
// TVSD Flags.
#define TVSD_NULL 0x0000
#define TVSD_AUTOHIDE 0x0001
#define TVSD_TOPMOST 0x0002
#define TVSD_SMSMALLICONS 0x0004
#define TVSD_HIDECLOCK 0x0008
#define TVSD_SHOWDESKBTN 0x0010
// old Win95 TVSD struct
typedef struct _TVSD95
{
DWORD dwSize;
LONG cxScreen;
LONG cyScreen;
LONG dxLeft;
LONG dxRight;
LONG dyTop;
LONG dyBottom;
DWORD uAutoHide;
RECTL rcAutoHide;
DWORD uStuckPlace;
DWORD dwFlags;
} TVSD95;
// Nashville tray save data
typedef struct _TVSD
{
DWORD dwSize;
LONG lSignature; // signature (must be negative)
DWORD dwFlags; // TVSD_ flags
DWORD uStuckPlace; // current stuck edge
SIZE sStuckWidths;// widths of stuck rects (BUGBUG: in tbd units)
RECT rcLastStuck; // last stuck position in pixels
} TVSD;
// convenient union for reading either
typedef union _TVSDCOMPAT
{
TVSD; // new format
TVSD95 w95; // old format
} TVSDCOMPAT;
#define TVSDSIG_CURRENT (-1L)
#define IS_CURRENT_TVSD(t) ((t.dwSize >= sizeof(TVSD)) && (t.lSignature < 0))
#define MAYBE_WIN95_TVSD(t) (t.dwSize == sizeof(TVSD95))
BOOL ShouldWeShowTheStartButtonBalloon()
{
DWORD dwType;
DWORD dwData = 0;
DWORD cbSize = sizeof(DWORD);
SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartButtonBalloonTip"), &dwType, (BYTE*) &dwData, &cbSize);
return (dwData == 0); // if StartButtonBalloonTip == 1, don't show
}
void DontShowTheStartButtonBalloonAnyMore()
{
DWORD dwData = 1;
SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartButtonBalloonTip"), REG_DWORD, (BYTE*) &dwData, sizeof(DWORD));
}
void DestroyStartButtonBalloon()
{
if (g_ts.hwndStartBalloon)
{
DestroyWindow(g_ts.hwndStartBalloon);
g_ts.hwndStartBalloon = NULL;
}
}
void ShowStartButtonToolTip()
{
if (!ShouldWeShowTheStartButtonBalloon())
return;
if (!g_ts.hwndStartBalloon)
{
g_ts.hwndStartBalloon = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstCabinet, NULL);
if (g_ts.hwndStartBalloon)
{
// set the version so we can have non buggy mouse event forwarding
SendMessage(g_ts.hwndStartBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
SendMessage(g_ts.hwndStartBalloon, TTM_SETMAXTIPWIDTH, 0, (LPARAM)300);
}
}
if (g_ts.hwndStartBalloon)
{
TCHAR szTip[MAX_PATH];
szTip[0] = TEXT('\0');
LoadString(hinstCabinet, IDS_STARTMENUBALLOON_TIP, szTip, ARRAYSIZE(szTip));
if (szTip[0])
{
RECT rc;
TOOLINFO ti = {0};
ti.cbSize = SIZEOF(ti);
ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
ti.hwnd = v_hwndTray;
ti.uId = (UINT_PTR)g_ts.hwndStart;
//ti.lpszText = NULL;
SendMessage(g_ts.hwndStartBalloon, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
SendMessage(g_ts.hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
ti.lpszText = szTip;
SendMessage(g_ts.hwndStartBalloon, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
LoadString(hinstCabinet, IDS_STARTMENUBALLOON_TITLE, szTip, ARRAYSIZE(szTip));
if (szTip[0])
{
SendMessage(g_ts.hwndStartBalloon, TTM_SETTITLE, TTI_INFO, (LPARAM)szTip);
}
GetWindowRect(g_ts.hwndStart, &rc);
SendMessage(g_ts.hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
SetWindowZorder(g_ts.hwndStartBalloon, HWND_TOPMOST);
SendMessage(g_ts.hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
}
}
}
// Mirror a bitmap in a DC (mainly a text object in a DC)
// [samera]
void MirrorBitmapInDC( HDC hdc , HBITMAP hbmOrig )
{
HDC hdcMem;
HBITMAP hbm;
BITMAP bm;
if (!GetObject(hbmOrig, sizeof(BITMAP), &bm))
return;
hdcMem = CreateCompatibleDC(hdc);
if (!hdcMem)
return;
hbm = CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight);
if (!hbm)
{
DeleteDC(hdcMem);
return;
}
// Flip the bitmap
SelectObject(hdcMem, hbm);
SET_DC_RTL_MIRRORED(hdcMem);
BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdc, 0, 0, SRCCOPY);
SET_DC_LAYOUT(hdcMem, 0);
// BUGBUG : The offset by 1 in hdcMem is to solve the off-by-one problem. Removed.
// [samera]
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(hbm);
}
BOOL Tray_CreateClockWindow()
{
g_ts.hwndNotify = TrayNotifyCreate(v_hwndTray, IDC_CLOCK, hinstCabinet);
return BOOLFROMPTR(g_ts.hwndNotify);
}
BOOL InitTrayClass(HINSTANCE hInstance)
{
WNDCLASS wc;
ZeroMemory(&wc, SIZEOF(WNDCLASS));
wc.lpszClassName = TEXT(WNDCLASS_TRAYNOTIFY);
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = Tray_WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
return RegisterClass(&wc);
}
HMENU LoadMenuPopup(LPCTSTR id)
{
HMENU hMenuSub = NULL;
HMENU hMenu = LoadMenu(hinstCabinet, id);
if (hMenu) {
hMenuSub = GetSubMenu(hMenu, 0);
if (hMenuSub) {
RemoveMenu(hMenu, 0, MF_BYPOSITION);
}
DestroyMenu(hMenu);
}
return hMenuSub;
}
#define CXGAP 4
// Compose the Start bitmap out of a flag and some text.
// REVIEW UNDONE - Put the up/down arrow back in.
HBITMAP CreateStartBitmap(HWND hwndTray)
{
HBITMAP hbmpStart = NULL;
HBITMAP hbmpStartOld = NULL;
HICON hiconFlag = NULL;
int cx, cy, cySmIcon;
TCHAR szStart[256];
SIZE size;
HDC hdcStart;
HDC hdcScreen;
HFONT hfontStart = NULL;
HFONT hfontStartOld = NULL;
NONCLIENTMETRICS ncm;
RECT rcStart;
WORD wLang;
// DebugMsg(DM_TRACE, "c.csb: Creating start bitmap.");
hdcScreen = GetDC(NULL);
hdcStart = CreateCompatibleDC(hdcScreen);
if (hdcStart)
{
ncm.cbSize = SIZEOF(ncm);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE))
{
wLang = GetUserDefaultLangID();
// Select normal weight font for chinese language.
if ( PRIMARYLANGID(wLang) == LANG_CHINESE && ( (SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) || (SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED) ))
ncm.lfCaptionFont.lfWeight = FW_NORMAL;
else
ncm.lfCaptionFont.lfWeight = FW_BOLD;
hfontStart = CreateFontIndirect(&ncm.lfCaptionFont);
}
else
{
DebugMsg(DM_ERROR, TEXT("c.csb: Can't create font."));
}
// Get an idea about how big we need everyhting to be.
LoadString(hinstCabinet, IDS_START, szStart, ARRAYSIZE(szStart));
hfontStartOld = SelectObject(hdcScreen, hfontStart);
GetTextExtentPoint(hdcScreen, szStart, lstrlen(szStart), &size);
SelectObject(hdcScreen, hfontStartOld);
// Mimick the tray and ignore the font size for determining the height.
#if 0
cy = max(g_cySize, size.cy);
#else
cy = g_cySize;
#endif
cySmIcon = GetSystemMetrics(SM_CYSMICON);
cx = (cy + size.cx) + CXGAP;
hbmpStart = CreateCompatibleBitmap(hdcScreen, cx, cy);
hbmpStartOld = SelectObject(hdcStart, hbmpStart);
hfontStartOld = SelectObject(hdcStart , hfontStart);
// Let's mirror the DC, so that text won't get mirrored
// if the window ir mirrored
if (IS_WINDOW_RTL_MIRRORED(hwndTray))
{
// Mirror the DC, so that drawing goes from the visual right
// edge. [samera]
SET_DC_RTL_MIRRORED(hdcStart);
}
rcStart.left = -g_cxEdge; // subtract this off because drawcaptiontemp adds it on
rcStart.top = 0;
rcStart.right = cx;
rcStart.bottom = cy;
hiconFlag = (HICON)LoadImage(NULL, MAKEINTRESOURCE(OIC_WINLOGO_DEFAULT ), IMAGE_ICON, cySmIcon, cySmIcon, 0);
// Get User to draw everything for us.
DrawCaptionTemp(hwndTray, hdcStart, &rcStart, hfontStart, hiconFlag, szStart, DC_INBUTTON | DC_TEXT | DC_ICON | DC_NOSENDMSG);
// Now we have the image ready to maintained by USER, let's mirror
// it so that when it is bitblt'ed to the hwndTray DC it will be mirrored and the bitmap image is maintained. [samera]
if (IS_WINDOW_RTL_MIRRORED(hwndTray))
{
MirrorBitmapInDC(hdcStart, hbmpStart);
}
// Clean up Start stuff.
SelectObject(hdcStart, hbmpStartOld);
if (hfontStart)
{
SelectObject(hdcStart, hfontStartOld);
DeleteObject(hfontStart);
}
DeleteDC(hdcStart);
DestroyIcon(hiconFlag);
}
else
{
DebugMsg(DM_ERROR, TEXT("c.csb: Can't create Start bitmap."));
}
ReleaseDC(NULL, hdcScreen);
return hbmpStart;
}
// Set the stuck monitor for the tray window
void Tray_SetStuckMonitor()
{
// use STICK_LEFT because most of the multi-monitors systems are set up
// side by side. use DEFAULTTONULL because we don't want to get the wrong one
// use the center point to call again in case we failed the first time.
g_ts.hmonStuck = MonitorFromRect(&g_ts.arStuckRects[STICK_LEFT], MONITOR_DEFAULTTONULL);
if (!g_ts.hmonStuck)
{
POINT pt;
pt.x = (g_ts.arStuckRects[STICK_LEFT].left + g_ts.arStuckRects[STICK_LEFT].right)/2;
pt.y = (g_ts.arStuckRects[STICK_LEFT].top + g_ts.arStuckRects[STICK_LEFT].bottom)/2;
g_ts.hmonStuck = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
}
g_ts.hmonOld = g_ts.hmonStuck;
}
VOID Tray_GetSaveStateAndInitRects()
{
TVSDCOMPAT tvsd;
DWORD cbData1, cbData2;
RECT rcDisplay;
DWORD dwTrayFlags;
UINT uStick;
SIZE size;
// first fill in the defaults
SetRect(&rcDisplay, 0, 0, g_cxPrimaryDisplay, g_cyPrimaryDisplay);
// size gets defaults
size.cx = g_ts.sizeStart.cx + 2 * (g_cxDlgFrame + g_cxBorder);
size.cy = g_ts.sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
// sStuckWidths gets minimum
g_ts.sStuckWidths.cx = 2 * (g_cxDlgFrame + g_cxBorder);
g_ts.sStuckWidths.cy = g_ts.sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
g_ts.uStuckPlace = STICK_BOTTOM;
dwTrayFlags = TVSD_TOPMOST;
#ifdef WINNT
// if we are on a remote hydra session
// and if there is no previous saved value,
// do not display the clock.
if (IsRemoteSession())
{
g_ts.fHideClock = TRUE;
dwTrayFlags |= TVSD_HIDECLOCK;
}
#endif
g_ts.uAutoHide = 0;
// now try to load saved vaules
// BUG : 231077
// Since Tasbar properties don't roam from NT5 to NT4, (NT4 -> NT5 yes)
// Allow roaming from NT4 to NT5 only for the first time the User logs
// on to NT5, so that future changes to NT5 are not lost when the user logs on to NT4 after customizing the taskbar properties on NT5.
cbData1 = SIZEOF(tvsd);
cbData2 = SIZEOF(tvsd);
if (Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), &tvsd, &cbData1) || Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects"), TEXT("Settings"), &tvsd, &cbData2))
{
if (IS_CURRENT_TVSD(tvsd) && IsValidSTUCKPLACE(tvsd.uStuckPlace))
{
Tray_GetDisplayRectFromRect(&rcDisplay, &tvsd.rcLastStuck, MONITOR_DEFAULTTONEAREST);
// BUGBUG: scale sStuckWidths.cy here?
size = tvsd.sStuckWidths;
g_ts.uStuckPlace = tvsd.uStuckPlace;
dwTrayFlags = tvsd.dwFlags;
}
else if (MAYBE_WIN95_TVSD(tvsd) && IsValidSTUCKPLACE(tvsd.w95.uStuckPlace))
{
g_ts.uStuckPlace = tvsd.w95.uStuckPlace;
dwTrayFlags = tvsd.w95.dwFlags;
if (tvsd.w95.uAutoHide & AH_ON)
dwTrayFlags |= TVSD_AUTOHIDE;
switch (g_ts.uStuckPlace)
{
case STICK_LEFT:
size.cx = tvsd.w95.dxLeft;
break;
case STICK_RIGHT:
size.cx = tvsd.w95.dxRight;
break;
case STICK_BOTTOM:
size.cy = tvsd.w95.dyBottom;
break;
case STICK_TOP:
size.cy = tvsd.w95.dyTop;
break;
}
}
}
ASSERT(IsValidSTUCKPLACE(g_ts.uStuckPlace));
// use the size only if it is not bogus
if (g_ts.sStuckWidths.cx < size.cx)
g_ts.sStuckWidths.cx = size.cx;
if (g_ts.sStuckWidths.cy < size.cy)
g_ts.sStuckWidths.cy = size.cy;
// set the tray flags
g_ts.fAlwaysOnTop = dwTrayFlags & TVSD_TOPMOST ? 1 : 0;
g_ts.fSMSmallIcons = dwTrayFlags & TVSD_SMSMALLICONS ? 1 : 0;
g_ts.fHideClock = dwTrayFlags & TVSD_HIDECLOCK ? 1 : 0;
g_ts.uAutoHide = dwTrayFlags & TVSD_AUTOHIDE ? (AH_ON | AH_HIDING) : 0;
g_ts.fShowDeskBtn = dwTrayFlags & TVSD_SHOWDESKBTN ? 1 : 0;
// initialize stuck rects
for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
Tray_MakeStuckRect(&g_ts.arStuckRects[uStick], &rcDisplay, g_ts.sStuckWidths, uStick);
Tray_SetStuckMonitor();// Determine which monitor the tray is on using its stuck rectangles
}
extern HRESULT Tray_SaveView();
void _SaveTrayStuff(void)
{
TVSD tvsd;
tvsd.dwSize = SIZEOF(tvsd);
tvsd.lSignature = TVSDSIG_CURRENT;
// position
CopyRect(&tvsd.rcLastStuck, &g_ts.arStuckRects[g_ts.uStuckPlace]);
tvsd.sStuckWidths = g_ts.sStuckWidths;
tvsd.uStuckPlace = g_ts.uStuckPlace;
tvsd.dwFlags = 0;
if (g_ts.fAlwaysOnTop) tvsd.dwFlags |= TVSD_TOPMOST;
if (g_ts.fSMSmallIcons) tvsd.dwFlags |= TVSD_SMSMALLICONS;
if (g_ts.fHideClock) tvsd.dwFlags |= TVSD_HIDECLOCK;
if (g_ts.uAutoHide & AH_ON) tvsd.dwFlags |= TVSD_AUTOHIDE;
if (g_ts.fShowDeskBtn) tvsd.dwFlags |= TVSD_SHOWDESKBTN;
// Save for now in Stuck rects.
// BUGBUG: really want to save rcLastStuck per user/maching/config
Reg_SetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), &tvsd, SIZEOF(tvsd));
Tray_SaveView(g_ts.ptbs);
}
/*
** align toolbar so that buttons are flush with client area and make toolbar's buttons to be MENU style
*/
void Tray_AlignStartButton()
{
HWND hwndStart = g_ts.hwndStart;
if (hwndStart)
{
RECT rcClient;
if (g_ts.sizeStart.cy == 0)
{
BITMAP bm;
HBITMAP hbm = (HBITMAP)SendMessage(hwndStart, BM_GETIMAGE, IMAGE_BITMAP, 0);
if (hbm)
{
GetObject(hbm, SIZEOF(bm), &bm);
g_ts.sizeStart.cx = bm.bmWidth + 2 * g_cxEdge;
g_ts.sizeStart.cy = bm.bmHeight + 2 * g_cyEdge;
if (g_ts.sizeStart.cy < g_cySize + 2*g_cyEdge)
g_ts.sizeStart.cy = g_cySize + 2*g_cyEdge;
}
else
{
// BUGBUG: New user may have caused this to fail...
// Setup some size for it that wont be too bad...
g_ts.sizeStart.cx = g_cxMinimized;
g_ts.sizeStart.cy = g_cySize + 2*g_cyEdge;
}
}
GetClientRect(g_ts.hwndMain, &rcClient);
SetWindowPos(hwndStart, NULL, 0, 0, (rcClient.right < g_ts.sizeStart.cx) ? rcClient.right : g_ts.sizeStart.cx, g_ts.sizeStart. cy, SWP_NOZORDER | SWP_NOACTIVATE);
}
}
// Allow us to do stuff on a "button-down".
HWND g_hwndPrevFocus = NULL;
LRESULT CALLBACK StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRet;
static UINT uDown = 0;
static BOOL fAllowUp = FALSE; // Is the start button allowed to be in the up position?
ASSERT(g_ButtonProc)
// Is the button going down?
if (uMsg == BM_SETSTATE)
{
// Is it going Down?
if (wParam) {
// DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
// Yes, Is it already down?
if (!uDown)
{
// Nope.
INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_DOWN);
uDown = 1;
fAllowUp = FALSE;// If we are going down, then we do not want to popup again until the Start Menu is collapsed
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
lRet = CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );// Show the button down.
SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)LOWORD(GetDlgCtrlID(hwnd)), (LPARAM)hwnd);// Notify the parent.
return lRet;
}
else
{
// Yep. Do nothing.
// fDown = FALSE;
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
else
{
// DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
// Nope, buttons coming up.
// Is it supposed to be down? Is it not allowed to be up?
if (uDown == 1 || !fAllowUp)
{
INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_UP);
// Yep, do nothing.
uDown = 2;
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
else
{
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
// Nope, Forward it on.
uDown = 0;
return CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );
}
}
}
else
{
switch (uMsg) {
case WM_LBUTTONDOWN:
// The button was clicked on, then we don't need no stink'n focus rect.
SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), 0);
goto ProcessCapture;
break;
case WM_KEYDOWN:
// The user pressed enter or return or some other bogus key combination when
// the start button had keyboard focus, so show the rect....
SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
if (wParam == VK_RETURN)
PostMessage(g_ts.hwndMain, WM_COMMAND, IDC_KBSTART, 0);
// We do not need the capture, because we do all of our button processing
// on the button down. In fact taking capture for no good reason screws with
// drag and drop into the menus. We're overriding user.
ProcessCapture:
lRet = CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );
SetCapture(NULL);
return lRet;
break;
case WM_MOUSEMOVE:
{
MSG msg;
msg.lParam = lParam;
msg.wParam = wParam;
msg.message = uMsg;
msg.hwnd = hwnd;
SendMessage(g_ts.hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)& msg);
break;
}
case WM_NULL:
break;
default:
if (uMsg == g_uStartButtonAllowPopup)
{
fAllowUp = TRUE;
}
break;
}
return CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam);
}
}
const TCHAR c_szButton[] = TEXT("button");
/*
** create the toolbar with the three buttons and align windows
*/
HWND Tray_CreateStartButton()
{
HWND hwnd;
DWORD dwStyle = BS_BITMAP;
g_uStartButtonBalloonTip = RegisterWindowMessage(TEXT("Welcome Finished"));
g_uLogoffUser = RegisterWindowMessage(TEXT("Logoff User"));
// BUGBUG: BS_CENTER | VS_VCENTER required, user bug?
hwnd = CreateWindowEx(0, c_szButton, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | BS_PUSHBUTTON | BS_LEFT | BS_VCENTER | dwStyle, 0, 0, 0, 0, v_hwndTray, (HMENU)IDC_START, hinstCabinet, NULL);
if (hwnd)
{
// Subclass it.
g_ts.hwndStart = hwnd;
g_ButtonProc = SubclassWindow(hwnd, StartButtonSubclassWndProc);
{
HBITMAP hbm = CreateStartBitmap(v_hwndTray);
if (hbm)
{
SendMessage(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm);
Tray_AlignStartButton();
return hwnd;
}
}
DestroyWindow(hwnd);
}
return NULL;
}
void Tray_GetWindowSizes(PRECT prcClient, PRECT prcView, PRECT prcClock, PRECT prcDesktop)
{
int xFSView, yFSView, cxFSView, cyFSView;
int xClock, yClock, cxClock, cyClock;
DWORD_PTR dwClockMinSize;
int cxDesktop;
int cyDesktop;
BOOL fDesktopBottom = FALSE;
RECT rcDesktop;
if (!g_hwndDesktopTB || !g_ts.fShowDeskBtn || SHRestricted(REST_CLASSICSHELL))
{
cxDesktop = cyDesktop = 0;
}
else
{
SendMessage(g_hwndDesktopTB, TB_GETITEMRECT, 0, (LPARAM)&rcDesktop);
cxDesktop = RECTWIDTH(rcDesktop) + g_cxEdge;
cyDesktop = RECTHEIGHT(rcDesktop);
}
// size to fill either horizontally or vertically
if ((prcClient->right - g_ts.sizeStart.cx) > (prcClient->bottom - g_ts.sizeStart.cy))
{
// Horizontal - Two cases. One where we have room below the
// toolbar to display the clock. so display it there, else display it on the right hand side...
yFSView = 0;
cyFSView = prcClient->bottom - yFSView;
// Recalc the min size for horizontal arrangement
dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE, 0x7fff, prcClient->bottom);
cxClock = LOWORD(dwClockMinSize);
cyClock = HIWORD(dwClockMinSize);
// xClock = prcClient->right - cxClock - g_cxFrame; // A little gap...
xClock = prcClient->right - cxClock - cxDesktop;
#ifdef VCENTER_CLOCK
yClock = (prcClient->bottom-cyClock)/2; // Center it vertically.
#else
yClock = 0;
#endif
xFSView = g_ts.sizeStart.cx + g_cxFrame + 1; // NT5 VFREEZE: one more pixel here
cxFSView = xClock - xFSView - g_cxFrame/2; // NT5 VFREEZE: we removed the right etch, so reclaim the space
}
else
{
// Vertical - Again two cases. One where we have room to the right to display the clock.
// Note: we want some gap here between the clock and toolbar.
// If it does not fit, then we will center it at the bottom...
xFSView = 0;
cxFSView = prcClient->right - xFSView;
dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE, 0x7fff, g_ts.sizeStart.cy);
cxClock = LOWORD(dwClockMinSize);
cyClock = HIWORD(dwClockMinSize);
if ((g_ts.sizeStart.cx + cxClock + 4 * g_cyTabSpace + cxDesktop) < prcClient->right)
{
int cyMax = g_ts.sizeStart.cy;;
// Can fit on the same row!
xClock = prcClient->right - cxClock - (2 * g_cxEdge) - cxDesktop; // A little gap...
yClock = 0;
if (cyClock > cyMax)
cyMax = cyClock;
yFSView = cyMax + g_cyTabSpace;
cyFSView = prcClient->bottom - yFSView;
}
else
{
// Nope put at bottom
// Recalc the min size for vertical arrangement
dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE, cxFSView, 0x7fff);
if (LOWORD(dwClockMinSize) + cyDesktop >= prcClient->right)
fDesktopBottom = TRUE;
if (fDesktopBottom) {
cxClock = min(LOWORD(dwClockMinSize), prcClient->right);
} else {
cxClock = LOWORD(dwClockMinSize);
}
cyClock = HIWORD(dwClockMinSize);
xClock = (prcClient->right - cxClock) / 2;
yClock = prcClient->bottom - cyClock - g_cyTabSpace;
// if we'ren ot slowing the clock, we still need to make
// room for hte desktop button
if (!cyClock)
yClock -= (cyDesktop - g_cyEdge);
if (fDesktopBottom) {
yClock -= cyDesktop;
} else {
xClock -= (cxDesktop/2);
}
yFSView = g_ts.sizeStart.cy + g_cyTabSpace;
cyFSView = yClock - yFSView - g_cyTabSpace;
}
}
prcView->left = xFSView;
prcView->top = yFSView;
prcView->right = xFSView + cxFSView;
prcView->bottom = yFSView + cyFSView;
prcClock->left = xClock;
prcClock->top = yClock;
prcClock->right = xClock + cxClock;
prcClock->bottom = yClock + cyClock;
if (fDesktopBottom) {
prcDesktop->top = prcClock->bottom + g_cyEdge;
prcDesktop->left = (prcClient->right - cxDesktop)/2;
} else {
*prcDesktop = *prcClock;
prcDesktop->left = prcClock->right + g_cxEdge;
}
prcDesktop->bottom = prcDesktop->top + cyDesktop;
prcDesktop->right = prcDesktop->left + cxDesktop + g_cxEdge;
}
void Tray_RestoreWindowPos()
{
WINDOWPLACEMENT wp;
//first restore the stuck postitions
Tray_GetSaveStateAndInitRects();
wp.length = SIZEOF(wp);
wp.showCmd = SW_HIDE;
g_ts.uMoveStuckPlace = (UINT)-1;
Tray_GetDockedRect(&wp.rcNormalPosition, FALSE);
ClockCtl_HandleTrayHide(g_ts.fHideClock);
SetWindowPlacement(v_hwndTray, &wp);
}
// Sort of a registry equivalent of the profile API's.
BOOL Reg_GetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, void *pData, DWORD *pcbData)
{
BOOL fRet = FALSE;
if (!g_fCleanBoot)
{
fRet = ERROR_SUCCESS == SHGetValue(hkey, pszSubKey, pszValue, NULL, pData, pcbData);
}
return fRet;
}
// Sort of a registry equivalent of the profile API's.
BOOL Reg_SetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, void *lpData, DWORD cbData)
{
HKEY hkeyNew = hkey;
BOOL fRet = FALSE;
if (pszSubKey)
{
if (RegCreateKey(hkey, pszSubKey, &hkeyNew) != ERROR_SUCCESS)
{
return fRet;
}
}
if (RegSetValueEx(hkeyNew, pszValue, 0, REG_BINARY, lpData, cbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
if (pszSubKey)
RegCloseKey(hkeyNew);
return fRet;
}
// Get the display (monitor) rectangle from the given arbitrary point
HMONITOR Tray_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags)
{
HMONITOR hmon;
RECT rcEmpty = {0};
hmon = MonitorFromPoint(pt, uFlags);
if (hmon && prcDisplay)
GetMonitorRect(hmon, prcDisplay);
else if (prcDisplay)
*prcDisplay = rcEmpty;
return hmon;
}
// Get the display (monitor) rectangle from the given arbitrary rectangle
HMONITOR Tray_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn, UINT uFlags)
{
HMONITOR hmon;
RECT rcEmpty = {0};
hmon = MonitorFromRect(prcIn, uFlags);
if (hmon && prcDisplay)
GetMonitorRect(hmon, prcDisplay);
else if (prcDisplay)
*prcDisplay = rcEmpty;
return hmon;
}
// Get the display (monitor) rectangle where the taskbar is currently on, if that monitor is invalid, get the nearest one.
void Tray_GetStuckDisplayRect(UINT uStuckPlace, LPRECT prcDisplay)
{
BOOL fValid;
ASSERT(prcDisplay);
fValid = GetMonitorRect(g_ts.hmonStuck, prcDisplay);
if (!fValid)
Tray_GetDisplayRectFromRect(prcDisplay, &g_ts.arStuckRects[uStuckPlace], MONITOR_DEFAULTTONEAREST);
}
// Snap a StuckRect to the edge of a containing rectangle
// fClip determines whether to clip the rectangle if it's off the display or move it onto the screen
void Tray_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size, UINT uStick)
{
CopyRect(prcStick, prcBound);
InflateRect(prcStick, g_cxEdge, g_cyEdge);
if (size.cx < 0) size.cx *= -1;
if (size.cy < 0) size.cy *= -1;
switch (uStick)
{
case STICK_LEFT:
prcStick->right = (prcStick->left + size.cx);
break;
case STICK_TOP:
prcStick->bottom = (prcStick->top + size.cy);
break;
case STICK_RIGHT:
prcStick->left = (prcStick->right - size.cx);
break;
case STICK_BOTTOM:
prcStick->top = (prcStick->bottom - size.cy);
break;
}
}
/*
** the screen size has changed, so the docked rectangles need to be adjusted to the new screen.
*/
void ResizeStuckRects(RECT *arStuckRects)
{
UINT uStick;
RECT rcDisplay;
Tray_GetStuckDisplayRect(g_ts.uStuckPlace, &rcDisplay);
for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
Tray_MakeStuckRect(&arStuckRects[uStick], &rcDisplay, g_ts.sStuckWidths, uStick);
}
void Tray_UpdateDockingFlags()
{
#ifndef WINNT
BOOL fIoSuccess;
BIOSPARAMS bp;
DWORD cbOut;
#endif
static BOOL fFirstTime=TRUE;
if ((!fFirstTime) && (g_ts.hBIOS == INVALID_HANDLE_VALUE)) {
return;
}
#ifdef WINNT
g_ts.hBIOS = INVALID_HANDLE_VALUE;
fFirstTime = FALSE;
return;
#else
g_ts.hBIOS = CreateFile(TEXT("\\\\.\\BIOS"), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (g_ts.hBIOS == INVALID_HANDLE_VALUE) {
fFirstTime = FALSE;
return;
}
bp.bp_ret=0;
fIoSuccess=DeviceIoControl(g_ts.hBIOS, PNPBIOS_SERVICE_GETDOCKCAPABILITIES,&bp,SIZEOF(bp),&bp,SIZEOF(bp),&cbOut,NULL);
if (fFirstTime) {
// first time through, if a warm dock change is not possible, don't bother with the menu item.
fFirstTime=FALSE;
if (!fIoSuccess) {
// problem getting the dock capabilities:
// if problem wasn't "undocked now" or "can't identify dock"
// then warm ejecting isn't possible.
if ((bp.bp_ret!=PNPBIOS_ERR_SYSTEM_NOT_DOCKED) && (bp.bp_ret!=PNPBIOS_ERR_CANT_DETERMINE_DOCKING)) {
CloseHandle(g_ts.hBIOS);
g_ts.hBIOS=INVALID_HANDLE_VALUE;
return;
}
} else {
// success getting the dock capabilities:
// if the dock isn't capable of warm or hot docking
// then warm ejecting isn't possible
if (!(bp.bp_ret&PNPBIOS_DOCK_CAPABILITY_TEMPERATURE)) {
CloseHandle(g_ts.hBIOS);
g_ts.hBIOS=INVALID_HANDLE_VALUE;
return;
}
}
}
// on each call we update WARMEJECTABLENOW
// depending on whether the dock is capable of warm ejecting right now
if ((fIoSuccess) && (bp.bp_ret&PNPBIOS_DOCK_CAPABILITY_TEMPERATURE)) {
g_ts.fDockingFlags|=DOCKFLAG_WARMEJECTABLENOW;
} else {
g_ts.fDockingFlags&=~DOCKFLAG_WARMEJECTABLENOW;
}
#endif
}
/* Check if system is currently in a docking station.
* Returns: TRUE if docked, FALSE if undocked or can't tell.
*/
// BUGBUG (lamadio): This should be moved into it's own file with the other
// API and ACPI routines.
BOOL GetDockedState(void)
{
#ifndef WINNT // BUGBUG - Fix this when NT gets docking capability
struct _ghwpi { // Get_Hardware_Profile_Info parameter blk
CMAPI cmApi;
ULONG ulIndex;
PFARHWPROFILEINFO pHWProfileInfo;
ULONG ulFlags;
HWPROFILEINFO HWProfileInfo;
} *pghwpi;
HANDLE hCMHeap;
UINT Result = DOCKSTATE_UNKNOWN;
DWORD dwRecipients = BSM_VXDS;
#define HEAP_SHARED 0x04000000 /* put heap in shared memory--undoc'd */
// Create a shared heap for CONFIGMG parameters
if ((hCMHeap = HeapCreate(HEAP_SHARED, 1, 4096)) == NULL)
return DOCKSTATE_UNKNOWN;
#undef HEAP_SHARED
// Allocate parameter block in shared memory
pghwpi = (struct _ghwpi *)HeapAlloc(hCMHeap, HEAP_ZERO_MEMORY, SIZEOF(*pghwpi));
if (pghwpi == NULL)
{
HeapDestroy(hCMHeap);
return DOCKSTATE_UNKNOWN;
}
pghwpi->cmApi.dwCMAPIRet = 0;
pghwpi->cmApi.dwCMAPIService = GetVxDServiceOrdinal(_CONFIGMG_Get_Hardware_Profile_Info);
pghwpi->cmApi.pCMAPIStack = (DWORD)(((LPBYTE)pghwpi) + SIZEOF(pghwpi->cmApi));
pghwpi->ulIndex = 0xFFFFFFFF;
pghwpi->pHWProfileInfo = &pghwpi->HWProfileInfo;
pghwpi->ulFlags = 0;
// "Call" _CONFIGMG_Get_Hardware_Profile_Info service
BroadcastSystemMessage(0, &dwRecipients, WM_DEVICECHANGE, DBT_CONFIGMGAPI32, (LPARAM)pghwpi);
if (pghwpi->cmApi.dwCMAPIRet == CR_SUCCESS) {
switch (pghwpi->HWProfileInfo.HWPI_dwFlags) {
case CM_HWPI_DOCKED:
Result = DOCKSTATE_DOCKED;
break;
case CM_HWPI_UNDOCKED:
Result = DOCKSTATE_UNDOCKED;
break;
default:
Result = DOCKSTATE_UNKNOWN;
break;
}
}
HeapDestroy(hCMHeap);
return Result;
#else
return(DOCKSTATE_DOCKED);
#endif
}
int g_cHided;
#ifdef DEBUG
BOOL g_dbNoShow;
#endif
void Tray_Hide();
//** TrayShowWindow -- temporary 'invisible' un-autohide
// DESCRIPTION
// various tray resize routines need the tray to be un-autohide'd for
// stuff to be calculated correctly. so we un-autohide it (invisibly...)
// here. note the WM_SETREDRAW to prevent flicker (nt5:182340).
// note that this is kind of a hack -- ideally the tray code would do
// stuff correctly even if hidden.
// NOTES
// separate routine (vs. inline) to aid debugging. turn this func off
// to 'see' resize stuff happening
// BUGBUG what happens if rehide kicks off before we're done?
void TrayShowWindow(int nCmdShow)
{
if (nCmdShow == SW_HIDE) {
if (g_cHided++ == 0) {
#ifdef DEBUG
if (!g_dbNoShow)
#endif
SendMessage(v_hwndTray, WM_SETREDRAW, FALSE, 0);
ShowWindow(v_hwndTray, nCmdShow);
Tray_Unhide();
}
}
else if (nCmdShow == SW_SHOWNA) {
ASSERT(g_cHided > 0); // must be push/pop
if (--g_cHided == 0) {
Tray_Hide();
#ifdef DEBUG
if (!g_dbNoShow)
#endif
ShowWindow(v_hwndTray, nCmdShow);
SendMessage(v_hwndTray, WM_SETREDRAW, TRUE, 0);
}
}
else {
ASSERT(0);
}
}
void Tray_VerifySize(BOOL fWinIni)
{
RECT rc;
BOOL fHiding;
fHiding = (g_ts.uAutoHide & AH_HIDING);
if (fHiding) {
// force it visible so various calculations will happen relative
// to unhidden size/position.
// fixes (e.g.) ie5:154536, where dropping a large-icon ISFBand
// onto hidden tray didn't do size negotiation.
// BUGBUG what happens if rehide kicks off before we're done?.
TrayShowWindow(SW_HIDE);
}
rc = g_ts.arStuckRects[g_ts.uStuckPlace];
Tray_HandleSizing(0, NULL, g_ts.uStuckPlace);
// if the old view had a height, and now it won't...
// push it up to at least one height
// do this only on win ini if we're on the top or bottom
if (fWinIni && STUCK_HORIZONTAL(g_ts.uStuckPlace)) {
RECT rcView;
// stash the old view size;
GetClientRect(g_ts.hwndView, &rcView);
if (RECTHEIGHT(rcView) && (RECTHEIGHT(rc) == g_cyTrayBorders)) {
int cyOneRow = g_cySize + 2 * g_cyEdge;
if (g_ts.uStuckPlace == STICK_TOP) {
rc.bottom = rc.top + cyOneRow;
} else {
rc.top = rc.bottom - cyOneRow;
}
Tray_HandleSizing(0, NULL, g_ts.uStuckPlace);// now snap this size.
}
}
if (!EqualRect(&rc, &g_ts.arStuckRects[g_ts.uStuckPlace]))
{
if (fWinIni) {
// if we're changing size or position, we need to be unhidden
Tray_Unhide();
Tray_SizeWindows();
}
rc = g_ts.arStuckRects[g_ts.uStuckPlace];
if ((g_ts.uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING))
{
g_ts.fSelfSizing = TRUE;
SetWindowPos(g_ts.hwndMain, NULL, rc.left, rc.top, RECTWIDTH(rc),RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOACTIVATE);
g_ts.fSelfSizing = FALSE;
}
else
ASSERT(0); // tmp unhide above
Tray_StuckTrayChange();
}
if (fWinIni)
Tray_SizeWindows();
if (fHiding) {
TrayShowWindow(SW_SHOWNA);
}
}
TCHAR const c_szCheckAssociations[] = TEXT("CheckAssociations");
// Returns true if GrpConv says we should check extensions again (and then
// clears the flag).
// The assumption here is that runonce gets run before we call this (so
// GrpConv -s can set this).
BOOL Tray_CheckAssociations(void)
{
DWORD dw = 0;
DWORD cb = SIZEOF(dw);
if (Reg_GetStruct(g_hkeyExplorer, NULL, c_szCheckAssociations, &dw, &cb) && dw)
{
dw = 0;
Reg_SetStruct(g_hkeyExplorer, NULL, c_szCheckAssociations, &dw, SIZEOF(dw));
return TRUE;
}
return FALSE;
}
ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl, BOOL fRecursive );
void Tray_RegisterDesktopNotify()
{
LPITEMIDLIST pidl;
TraceMsg(TF_TRAY, "c.rdn: Notify for desktop.");
if (!g_ts.uDesktopNotify)
{
pidl = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
if (pidl)
{
g_ts.uDesktopNotify = _RegisterNotify(g_ts.hwndMain, WMTRAY_DESKTOPCHANGE, pidl, FALSE);
ILFree(pidl);
}
}
if (!SHRestricted(REST_NOCOMMONGROUPS) && !g_ts.uCommonDesktopNotify)
{
pidl = SHCloneSpecialIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
if (pidl)
{
g_ts.uCommonDesktopNotify = _RegisterNotify(g_ts.hwndMain, WMTRAY_DESKTOPCHANGE, pidl, FALSE);
ILFree(pidl);
}
}
}
HWND Tray_GetClockWindow(void)
{
return (HWND)SendMessage(g_ts.hwndNotify, TNM_GETCLOCK, 0, 0L);
}
UINT Tray_GetStartIDB()
{
UINT id;
#ifdef WINNT
if (IsOS(OS_TERMINALCLIENT))
{
id = IDB_TERMINALSERVICESBKG;
}
else if (IsOS(OS_WIN2000DATACENTER))
{
id = IDB_DCSERVERSTARTBKG;
}
else if (IsOS(OS_SERVERAPPLIANCE))
{
id = IDB_SRVAPPSTARTBKG;
}
else if (IsOS(OS_WIN2000ADVSERVER))
{
id = IDB_ADVSERVERSTARTBKG;
}
else if (IsOS(OS_WIN2000SERVER))
{
id = IDB_SERVERSTARTBKG;
}
else if (IsOS(OS_WIN2000EMBED))
{
id = IDB_EMBEDDED;
}
else
{
id = IDB_STARTBKG;
}
#else
if (IsOS(OS_MEMPHIS_GOLD))
{
id = IDB_STARTBKG;
}
else
{
id = IDB_START95BK;
}
#endif
return id;
}
void Tray_CreateTrayTips()
{
g_ts.hwndTrayTips = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstCabinet, NULL);
SetWindowZorder(g_ts.hwndTrayTips, HWND_TOPMOST);
if (g_ts.hwndTrayTips)
{
HWND hwndClock;
TOOLINFO ti;
ti.cbSize = SIZEOF(ti);
ti.uFlags = TTF_IDISHWND;
ti.hwnd = v_hwndTray;
ti.uId = (UINT_PTR)g_ts.hwndStart;
ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_STARTBUTTONTIP);
ti.hinst = hinstCabinet;
SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
if (NULL != (hwndClock = Tray_GetClockWindow()))
{
ti.uFlags = 0;
ti.uId = (UINT_PTR)hwndClock;
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
}
}
}
IUnknown* Tray_CreateView();
LRESULT Tray_CreateWindows()
{
if (Tray_CreateStartButton())
{
if (Tray_CreateClockWindow())
{
// We need to set the tray position, before creating
// the view window, because it will call back our
// GetWindowRect member functions.
Tray_RestoreWindowPos();
Tray_CreateTrayTips();
g_ts.ptbs = Tray_CreateView();
SendMessage(g_ts.hwndNotify, TNM_HIDECLOCK, 0, g_ts.fHideClock);
if (g_ts.ptbs)
{
Tray_CreateDesktopButton();
Tray_VerifySize(FALSE);
Tray_SizeWindows(); // size after all windows created
return 1;
}
}
}
return -1;
}
LRESULT Tray_InitStartButtonEtc()
{
// NOTE: This bitmap is used as a flag in CTaskBar::OnPosRectChangeDB to
// tell when we are done initializing, so we don't resize prematurely
g_ts.hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(Tray_GetStartIDB()));
if (g_ts.hbmpStartBkg)
{
UpdateWindow(v_hwndTray);
StartMenu_Build();
CStartDropTarget_Register();
PrintNotify_Init(v_hwndTray);
if (Tray_CheckAssociations())
CheckWinIniForAssocs();
Tray_RegisterDesktopNotify();
SendNotifyMessage(HWND_BROADCAST, RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
return 1;
}
return -1;
}
void Tray_AdjustMinimizedMetrics()
{
MINIMIZEDMETRICS mm;
mm.cbSize = SIZEOF(mm);
SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
mm.iArrange |= ARW_HIDE;
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
}
LRESULT Tray_OnCreateAsync()
{
LRESULT lres;
if (g_dwProfileCAP & 0x00000004)
{
StartCAP();
}
lres = Tray_InitStartButtonEtc();
if (g_dwProfileCAP & 0x00000004)
{
StopCAP();
}
g_ts.hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_TRAY));
Tray_RegisterGlobalHotkeys();
HotkeyList_Restore(v_hwndTray);
// we run the tray thread that handles Ctrl-Esc with a high priority
// class so that it can respond even on a stressed system.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
return lres;
}
LRESULT Tray_OnCreate(HWND hwnd)
{
LRESULT lres;
v_hwndTray = hwnd;
SendMessage(v_hwndTray, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
Tray_AdjustMinimizedMetrics();
Tray_UpdateDockingFlags();
lres = Tray_CreateWindows();
return lres;
}
void Tray_DestroyShellView()
{
DestroyWindow(g_ts.hwndView);
g_ts.hwndView = NULL;
}
BOOL CALLBACK Tray_FullScreenEnumCallback(HMONITOR hmon, HDC hdc, LPRECT prc, LPARAM dwData)
{
BOOL fFullScreen; // Is there a rude app on this monitor?
LPRECT prcRude = (LPRECT)dwData;
if (prcRude)
{
RECT rc, rcMon;
GetMonitorRect(hmon, &rcMon);
IntersectRect(&rc, &rcMon, prcRude);
fFullScreen = EqualRect(&rc, &rcMon);
}
else
{
fFullScreen = FALSE;
}
if (hmon == g_ts.hmonStuck)
{
g_ts.fStuckRudeApp = fFullScreen;
}
// Tell all the appbars on the same display to get out of the way too
AppBarNotifyAll(hmon, ABN_FULLSCREENAPP, NULL, fFullScreen);
return TRUE;
}
void Tray_HandleFullScreenApp(HWND hwnd)
{
// First check to see if something has actually changed
if (g_ts.hwndRude != hwnd)
{
g_ts.hwndRude = hwnd;
// Enumerate all the monitors, see if the app is rude on each, adjust
// app bars and g_ts.fStuckRudeApp as necessary. (Some rude apps, such as the NT Logon Screen Saver, span multiple monitors.)
{
LPRECT prc;
RECT rc;
if (hwnd && GetWindowRect(hwnd, &rc))
{
prc = &rc;
}
else
{
prc = NULL;
}
EnumDisplayMonitors(NULL, NULL, Tray_FullScreenEnumCallback, (LPARAM)prc);
}
Tray_ResetZorder();// Now that we've set g_ts.fStuckRudeApp, update the tray's z-order position
ClockCtl_HandleTrayHide(g_ts.fStuckRudeApp);// stop the clock so we don't eat cycles and keep tons of code paged in
SendMessage(g_ts.hwndNotify, TNM_RUDEAPP, g_ts.fStuckRudeApp, 0);// Finally, let traynot know about whether the tray is hiding
}
}
BOOL Tray_IsTopmost()
{
return BOOLIFY(GetWindowLong(v_hwndTray, GWL_EXSTYLE) & WS_EX_TOPMOST);
}
BOOL Tray_IsStartMenuVisible()
{
HWND hwnd;
if (SUCCEEDED(IUnknown_GetWindow((IUnknown*)g_ts._pmpStartMenu, &hwnd)))
{
return IsWindowVisible(hwnd);
}
return FALSE;
}
BOOL Tray_IsActive()
{
// We say the tray is "active" iff:
// (a) the foreground window is the tray or a window owned by the tray, or
// (b) the start menu is showing
BOOL fActive = FALSE;
HWND hwnd = GetForegroundWindow();
if (hwnd != NULL && (hwnd == v_hwndTray || (GetWindowOwner(hwnd) == v_hwndTray)))
{
fActive = TRUE;
}
else if (Tray_IsStartMenuVisible())
{
fActive = TRUE;
}
return fActive;
}
void Tray_ResetZorder()
{
HWND hwndZorder, hwndZorderCurrent;
if (g_fDesktopRaised || (g_ts.fAlwaysOnTop && !g_ts.fStuckRudeApp))
{
hwndZorder = HWND_TOPMOST;
}
else if (Tray_IsActive())
{
hwndZorder = HWND_TOP;
}
else if (g_ts.fStuckRudeApp)
{
hwndZorder = HWND_BOTTOM;
}
else
{
hwndZorder = HWND_NOTOPMOST;
}
// We don't have to worry about the HWND_BOTTOM current case -- it's ok
// to keep moving ourselves down to the bottom when there's a rude app.
// Nor do we have to worry about the HWND_TOP current case -- it's ok
// to keep moving ourselves up to the top when we're active.
hwndZorderCurrent = Tray_IsTopmost() ? HWND_TOPMOST : HWND_NOTOPMOST;
if (hwndZorder != hwndZorderCurrent)
{
// only do this if somehting has changed.
// this keeps us from popping up over menus as desktop async
// notifies us of it's state
SHForceWindowZorder(v_hwndTray, hwndZorder);
}
}
DWORD CALLBACK Tray_SyncThreadProc(void *hInst);
DWORD CALLBACK Tray_MainThreadProc(void *hInst);
void Tray_MessageLoop()
{
for (;;)
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
if (v_hwndTray && IsWindow(v_hwndTray))
{
// Tell the tray to save everything off if we got here
// without it being destroyed.
SendMessage(v_hwndTray, WM_ENDSESSION, 1, 0);
}
return; // break all the way out of the main loop
}
if (g_ts._pmbStartMenu && g_ts._pmbStartMenu->lpVtbl->IsMenuMessage(g_ts._pmbStartMenu, &msg) == S_OK)
{
continue;
}
if (g_ts.hMainAccel && TranslateAccelerator(g_ts.hwndMain, g_ts.hMainAccel, &msg))
{
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
WaitMessage();
}
}
}
BOOL InitTray(HINSTANCE hInst )
{
// put the tray on a separate thread
return SHCreateThread(Tray_MainThreadProc, hInst, 0, Tray_SyncThreadProc);
}
void Tray_InitBandsite()
{
ASSERT(v_hwndTray);
// we initilize the contents after all the infrastructure is created and sized properly
// need to notify which side we're on.
// nt5:211881: set mode *before* load, o.w. Update->RBAutoSize screwed up
BandSite_SetMode(g_ts.ptbs, STUCK_HORIZONTAL(g_ts.uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
BandSite_Load();
// now that the mode is set, we need to force an update because we
// explicitly avoided the update during BandSite_Load
BandSite_Update(g_ts.ptbs);
BandSite_UIActivateDBC(g_ts.ptbs, DBC_SHOW);
}
void Tray_KickStartAutohide()
{
if (g_ts.uAutoHide & AH_ON)
{
g_ts.uAutoHide = AH_ON | AH_HIDING;// tray always starts out hidden on autohide
Tray_Unhide();// we and many apps rely upon us having calculated the size correctly
// register it
if (!IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace))
{
// don't bother putting up UI in this case
// if someone is there just silently convert to normal
// (the shell is booting who would be there anyway?)
Tray_SetAutoHideState(FALSE);
}
}
}
void Tray_InitNonzeroGlobals()
{
// initalize globals that need to be non-zero
g_ts.wThreadCmd = SMCT_DONE;
g_ts.hBIOS = INVALID_HANDLE_VALUE;
if (GetSystemMetrics(SM_SLOWMACHINE))
{
g_dtSlideHide = 0;// dont slide the tray out
g_dtSlideShow = 0;
}
else
{
//BUGBUG: we should read from registry.
g_dtSlideHide = 400;
g_dtSlideShow = 200;
}
}
void Tray_CreateTrayWindow(HINSTANCE hinst)
{
DWORD dwExStyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW;
dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
v_hwndTray = CreateWindowEx(dwExStyle, TEXT(WNDCLASS_TRAYNOTIFY), NULL, WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_THICKFRAME, 0, 0, 0, 0, NULL, NULL, hinst, NULL);
}
DWORD CALLBACK Tray_SyncThreadProc(void *pv)
{
MSG msg;
if (g_dwProfileCAP & 0x00000002)
{
StartCAP();
}
// make sure the message queue has been created...
PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE );
OleInitialize( NULL );
Tray_InitNonzeroGlobals();
Tray_CreateTrayWindow((HINSTANCE)pv);
if (!v_hwndTray || !g_ts.ptbs)
{
OleUninitialize();
return FALSE;
}
// obey the "always on top" flag
Tray_ResetZorder();
Tray_KickStartAutohide();
Tray_InitBandsite();
// make sure we clip the taskbar to the current monitor before showing it
Tray_ClipWindow(TRUE);
// it looks really dorky for the tray to pop up and rehide at logon
// if we are autohide don't activate the tray when we show it
// if we aren't autohide do what Win95 did (tray is active by default)
ShowWindow(v_hwndTray, ((g_ts.uAutoHide & AH_HIDING)? SW_SHOWNA : SW_SHOW));
UpdateWindow(v_hwndTray);
Tray_StuckTrayChange();
SetTimer(v_hwndTray, IDT_HANDLEDELAYBOOTSTUFF, 10 * 1000, NULL);
if (g_dwProfileCAP & 0x00020000)
{
StopCAP();
}
return FALSE;
}
// the rest of the thread proc that includes the message loop
DWORD CALLBACK Tray_MainThreadProc(void *pv)
{
if (!v_hwndTray)
return FALSE;
Tray_OnCreateAsync();
Tray_MessageLoop();
OleUninitialize();
return FALSE;
}
#define DM_IANELHK 0
ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl, BOOL fRecursive)
{
SHChangeNotifyEntry fsne;
ULONG lReturn;
fsne.fRecursive = fRecursive;
fsne.pidl = pidl;
// Don't watch for attribute changes since we just want the name and icon.
// For example, if a printer is paused, we don't want to re-enumerate everything.
lReturn = SHChangeNotifyRegister(hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel, ((SHCNE_DISKEVENTS | SHCNE_UPDATEIMAGE) & ~SHCNE_ATTRIBUTES), nMsg, 1, &fsne);
return lReturn;
}
ULONG RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl)
{
return _RegisterNotify(hwnd, nMsg, pidl, TRUE);
}
void UnregisterNotify(ULONG nNotify)
{
if (nNotify)
SHChangeNotifyDeregister(nNotify);
}
#define HKIF_NULL 0
#define HKIF_CACHED 1
#define HKIF_FREEPIDLS 2
typedef struct
{
LPITEMIDLIST pidlFolder;
LPITEMIDLIST pidlItem;
WORD wGHotkey;
// BOOL fCached;
WORD wFlags;
} HOTKEYITEM, *PHOTKEYITEM;
const TCHAR c_szSlashCLSID[] = TEXT("\\CLSID");
// like OLE GetClassFile(), but it only works on ProgID\CLSID type registration
// not real doc files or pattern matched files
HRESULT _CLSIDFromExtension(LPCTSTR pszExt, CLSID *pclsid)
{
TCHAR szProgID[80];
ULONG cb = SIZEOF(szProgID);
if (RegQueryValue(HKEY_CLASSES_ROOT, pszExt, szProgID, &cb) == ERROR_SUCCESS)
{
TCHAR szCLSID[80];
lstrcat(szProgID, c_szSlashCLSID);
cb = SIZEOF(szCLSID);
if (RegQueryValue(HKEY_CLASSES_ROOT, szProgID, szCLSID, &cb) == ERROR_SUCCESS)
return SHCLSIDFromString(szCLSID, pclsid);
}
return E_FAIL;
}
// this gets hotkeys for files given a folder and a pidls it is much faster
// than _GetHotkeyFromPidls since it does not need to bind to an IShellFolder
// to interrogate it. if you have access to the item's IShellFolder, call this one, especially in a loop.
WORD _GetHotkeyFromFolderItem(LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
{
// TCHAR szPath[MAX_PATH];
WORD wHotkey = 0;
DWORD dwAttrs = SFGAO_LINK;
// Make sure it is an SFGAO_LINK so we don't load a big handler dll just to get back E_NOINTERFACE...
if (SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &dwAttrs)) && (dwAttrs & SFGAO_LINK))
{
IShellLink * pLink;
UINT rgfInOut = 0;
if ( SUCCEEDED( psf->lpVtbl->GetUIObjectOf(psf, NULL, 1, &pidl, &IID_IShellLink, &rgfInOut, &pLink )))
{
pLink->lpVtbl->GetHotkey(pLink, &wHotkey);
pLink->lpVtbl->Release(pLink);
}
}
return wHotkey;
}
UINT HotkeyList_GetFreeItemIndex(void)
{
int i, cItems;
PHOTKEYITEM phki;
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
if (!phki->wGHotkey)
{
ASSERT(!phki->pidlFolder);
ASSERT(!phki->pidlItem);
break;
}
}
return i;
}
// Weird, Global hotkeys use different flags for modifiers than window hotkeys
// (and hotkeys returned by the hotkey control)
WORD MapHotkeyToGlobalHotkey(WORD wHotkey)
{
UINT nVirtKey;
UINT nMod = 0;
// Map the modifiers.
if (HIBYTE(wHotkey) & HOTKEYF_SHIFT)
nMod |= MOD_SHIFT;
if (HIBYTE(wHotkey) & HOTKEYF_CONTROL)
nMod |= MOD_CONTROL;
if (HIBYTE(wHotkey) & HOTKEYF_ALT)
nMod |= MOD_ALT;
nVirtKey = LOBYTE(wHotkey);
return (WORD)((nMod*256) + nVirtKey);
}
// NB This takes a regular window hotkey not a global hotkey (it does
// the convertion for you).
int HotkeyList_Add(WORD wHotkey, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fClone)
{
LPCITEMIDLIST pidl1, pidl2;
if (wHotkey)
{
HOTKEYITEM hki;
int i = HotkeyList_GetFreeItemIndex();
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
// DebugMsg(DM_IANELHK, "c.hl_a: Hotkey %x with id %d.", wHotkey, i);
if (fClone)
{
pidl1 = ILClone(pidlFolder);
pidl2 = ILClone(pidlItem);
hki.wFlags = HKIF_FREEPIDLS;
}
else
{
pidl1 = pidlFolder;
pidl2 = pidlItem;
hki.wFlags = HKIF_NULL;
}
hki.pidlFolder = (LPITEMIDLIST)pidl1;
hki.pidlItem = (LPITEMIDLIST)pidl2;
hki.wGHotkey = MapHotkeyToGlobalHotkey(wHotkey);
DSA_SetItem(g_ts.hdsaHKI, i, &hki);
return i;
}
return -1;
}
// NB Cached hotkeys have their own pidls that need to be free but
// regular hotkeys just keep a pointer to pidls used by the startmenu and so don't.
int HotkeyList_AddCached(WORD wGHotkey, LPITEMIDLIST pidl)
{
int i = -1;
if (wGHotkey)
{
LPITEMIDLIST pidlItem = ILClone(ILFindLastID(pidl));
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
if (pidlItem)
{
if (ILRemoveLastID(pidl))
{
HOTKEYITEM hki;
i = HotkeyList_GetFreeItemIndex();
// DebugMsg(DM_IANELHK, "c.hl_ac: Hotkey %x with id %d.", wGHotkey, i);
hki.pidlFolder = pidl;
hki.pidlItem = pidlItem;
hki.wGHotkey = wGHotkey;
hki.wFlags = HKIF_CACHED | HKIF_FREEPIDLS;
DSA_SetItem(g_ts.hdsaHKI, i, &hki);
}
}
}
return i;
}
const TCHAR c_szHotkeys[] = TEXT("Hotkeys");
// NB Must do this before destroying the startmenu since the hotkey list
// uses it's pidls in a lot of cases.
int HotkeyList_Save(void)
{
int cCached = 0;
TraceMsg(TF_TRAY, "HotkeyList_Save: Saving global hotkeys.");
if (EVAL(g_ts.hdsaHKI))
{
int i, cItems;
PHOTKEYITEM phki;
LPITEMIDLIST pidl;
int cbData = 0;
TCHAR szValue[32];
LPBYTE pData;
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
// Non cached item?
if (phki->wGHotkey && !(phki->wFlags & HKIF_CACHED))
{
// Yep, save it.
pidl = ILCombine(phki->pidlFolder, phki->pidlItem);
if (pidl)
{
cbData = SIZEOF(WORD) + ILGetSize(pidl);
pData = LocalAlloc(GPTR, cbData);
if (pData)
{
// DebugMsg(DM_TRACE, "c.hl_s: Saving %x.", phki->wGHotkey );
*((LPWORD)pData) = phki->wGHotkey;
memcpy(pData+SIZEOF(WORD), pidl, cbData-SIZEOF(DWORD));
wsprintf(szValue, TEXT("%d"), cCached);
Reg_SetStruct(g_hkeyExplorer, c_szHotkeys, szValue, pData , cbData);
cCached++;
LocalFree(pData);
}
ILFree(pidl);
}
}
}
}
return cCached;
}
HKEY Reg_EnumValueCreate(HKEY hkey, LPCTSTR pszSubkey)
{
HKEY hkeyOut;
if (RegOpenKeyEx(hkey, pszSubkey, 0L, KEY_ALL_ACCESS, &hkeyOut) == ERROR_SUCCESS)
{
return hkeyOut;
}
return(NULL);
}
int Reg_EnumValue(HKEY hkey, int i, LPBYTE pData, int cbData)
{
TCHAR szValue[MAX_PATH];
DWORD cchValue;
DWORD dw;
DWORD dwType;
cchValue = ARRAYSIZE(szValue);
if (RegEnumValue(hkey, i, szValue, &cchValue, &dw, &dwType, pData,
&cbData) == ERROR_SUCCESS)
{
return cbData;
}
return 0;
}
BOOL Reg_EnumValueDestroy(HKEY hkey)
{
return RegCloseKey(hkey) == ERROR_SUCCESS;
}
int HotkeyList_Restore(HWND hwnd)
{
int i = 0;
HKEY hkey;
LPBYTE pData;
int cbData;
WORD wGHotkey;
int id;
LPITEMIDLIST pidl;
// If shdocvw fails to load for some reason, we will GP-fault if we
// don't validate hdsaHKI.
if (EVAL(g_ts.hdsaHKI))
{
TraceMsg(TF_TRAY, "HotkeyList_Restore: Restoring global hotkeys...");
hkey = Reg_EnumValueCreate(g_hkeyExplorer, c_szHotkeys);
if (hkey)
{
cbData = Reg_EnumValue(hkey, i, NULL, 0);
while (cbData)
{
pData = LocalAlloc(GPTR, cbData);
if (pData)
{
Reg_EnumValue(hkey, i, pData, cbData);
// Get the hotkey and the pidl components.
wGHotkey = *((LPWORD)pData);
pidl = ILClone((LPITEMIDLIST)(pData+SIZEOF(WORD)));
// DebugMsg(DM_TRACE, "c.hl_r: Restoring %x", wGHotkey);
id = HotkeyList_AddCached(wGHotkey, pidl);
if (id != -1)
Tray_RegisterHotkey(hwnd, id);
LocalFree(pData);
}
i++;
cbData = Reg_EnumValue(hkey, i, NULL, 0);
}
Reg_EnumValueDestroy(hkey);
// Nuke the cached stuff.
RegDeleteKey(g_hkeyExplorer, c_szHotkeys);
}
}
return i;
}
// NB Again, this takes window hotkey not a Global one.
// NB This doesn't delete cached hotkeys.
int HotkeyList_Remove(WORD wHotkey)
{
if (EVAL(g_ts.hdsaHKI))
{
int i, cItems;
PHOTKEYITEM phki;
WORD wGHotkey;
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
// DebugMsg(DM_IANELHK, "c.hl_r: Remove hotkey for %x" , wHotkey);
// Unmap the modifiers.
wGHotkey = MapHotkeyToGlobalHotkey(wHotkey);
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
if (phki && !(phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
{
// DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
if (phki->wFlags & HKIF_FREEPIDLS)
{
if (phki->pidlFolder)
ILFree(phki->pidlFolder);
if (phki->pidlItem)
ILFree(phki->pidlItem);
}
phki->wGHotkey = 0;
phki->pidlFolder = NULL;
phki->pidlItem = NULL;
phki->wFlags &= ~HKIF_FREEPIDLS;
return i;
}
}
}
return -1;
}
// NB This takes a global hotkey.
int HotkeyList_RemoveCached(WORD wGHotkey)
{
int i, cItems;
PHOTKEYITEM phki;
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
// DebugMsg(DM_IANELHK, "c.hl_rc: Remove hotkey for %x" , wGHotkey);
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
if (phki && (phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
{
// DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
if (phki->wFlags & HKIF_FREEPIDLS)
{
if (phki->pidlFolder)
ILFree(phki->pidlFolder);
if (phki->pidlItem)
ILFree(phki->pidlItem);
}
phki->pidlFolder = NULL;
phki->pidlItem = NULL;
phki->wGHotkey = 0;
phki->wFlags &= ~(HKIF_CACHED | HKIF_FREEPIDLS);
return i;
}
}
return -1;
}
// NB Some (the ones not marked HKIF_FREEPIDLS) of the items in the list of hotkeys
// have pointers to idlists used by the filemenu so they are only valid for the lifetime of the filemenu.
BOOL HotkeyList_Create(void)
{
if (!g_ts.hdsaHKI)
{
// DebugMsg(DM_TRACE, "c.hkl_c: Creating global hotkey list.");
g_ts.hdsaHKI = DSA_Create(SIZEOF(HOTKEYITEM), 0);
}
if (g_ts.hdsaHKI)
return TRUE;
return FALSE;
}
void StartMenu_AddTask(IShellTaskScheduler* pSystemScheduler, int nFolder, int iPriority)
{
LPITEMIDLIST pidlFolder;
IShellHotKey * pshk;
if (FAILED(CHotKey_Create(&pshk)))
return;
SHGetSpecialFolderLocation(NULL, nFolder, &pidlFolder);
if (pidlFolder)
{
LPITEMIDLIST pidlParent = ILClone(pidlFolder);
if (pidlParent)
{
IShellFolder* psf;
ILRemoveLastID(pidlParent);
psf = BindToFolder(pidlParent);
if (psf)
{
HRESULT hres;
IStartMenuTask * psmt;
BOOL bDesktop = (CSIDL_DESKTOPDIRECTORY == nFolder || CSIDL_COMMON_DESKTOPDIRECTORY == nFolder);
hres = CoCreateInstance(bDesktop ? &CLSID_DesktopTask : &CLSID_StartMenuTask, NULL, CLSCTX_INPROC, &IID_IStartMenuTask, (void **) &psmt);
if (SUCCEEDED(hres))
{
DWORD dwFlags = bDesktop ? 0 : ITSFT_RECURSE;
LONG nMaxRecursion = bDesktop ? 32 : 2 ;
// Initialize the task. Set task priority according to max
// folder depth. We'll assume 32 is a reasonable number.
hres = psmt->lpVtbl->InitTaskSFT(psmt, psf, pidlFolder, nMaxRecursion, dwFlags, 32);
if (SUCCEEDED(hres))
{
IRunnableTask * ptask;
psmt->lpVtbl->InitTaskSMT(psmt, pshk, iPriority);
hres = psmt->lpVtbl->QueryInterface(psmt, &IID_IRunnableTask, (void **)&ptask);
if (SUCCEEDED(hres))
{
// Add it to the scheduler
hres = pSystemScheduler->lpVtbl->AddTask(pSystemScheduler, ptask, &CLSID_StartMenuTask, 0, 32);
ptask->lpVtbl->Release(ptask);
}
}
psmt->lpVtbl->Release(psmt);
}
if (FAILED(hres))
TraceMsg(TF_ERROR, "StartMenu_AddTask: failed to create start menu task");
psf->lpVtbl->Release(psf);
}
ILFree(pidlParent);
}
ILFree(pidlFolder);
}
pshk->lpVtbl->Release(pshk);
}
void StartMenu_Build()
{
HRESULT hres;
ATOMICRELEASET(g_ts._pmpStartMenu, IMenuPopup);
ATOMICRELEASET(g_ts._pmbStartMenu, IMenuBand);
g_uStartButtonAllowPopup = RegisterWindowMessage(TEXT("StartButtonAllowPopup"));
hres = StartMenuHost_Create(&g_ts._pmpStartMenu, &g_ts._pmbStartMenu);
if (SUCCEEDED(hres))
{
IBanneredBar* pbb;
hres = g_ts._pmpStartMenu->lpVtbl->QueryInterface(g_ts._pmpStartMenu, &IID_IBanneredBar, (void**)&pbb);
if (SUCCEEDED(hres))
{
pbb->lpVtbl->SetBitmap(pbb, g_ts.hbmpStartBkg);
if (g_ts.fSMSmallIcons)
pbb->lpVtbl->SetIconSize(pbb, BMICON_SMALL);
else
pbb->lpVtbl->SetIconSize(pbb, BMICON_LARGE);
pbb->lpVtbl->Release(pbb);
}
HotkeyList_Create();
}
else
TraceMsg(TF_ERROR, "Could not create StartMenu");
}
void StartMenu_Destroy()
{
IUnknown_SetSite((IUnknown*)g_ts._pmpStartMenu, NULL);
ATOMICRELEASET(g_ts._pmpStartMenu, IMenuPopup);
ATOMICRELEASET(g_ts._pmbStartMenu, IMenuBand);
}
void _ForceStartButtonUp()
{
MSG msg;
// don't do that check message pos because it gets screwy with
// keyboard cancel. and besides, we always want it cleared after
// track menu popup is done.
// do it twice to be sure it's up due to the uDown cycling twice in
// the subclassing stuff
// pull off any button downs
PeekMessage(&msg, g_ts.hwndStart, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
SendMessage(g_ts.hwndStart, BM_SETSTATE, FALSE, 0);
SendMessage(g_ts.hwndStart, BM_SETSTATE, FALSE, 0);
// The user cancelled the Start menu, so a new press of the Windows key
// should be considered "Reopen the Start Menu" instead of "Cancel the
// Start Menu and return to the previous window."
g_hwndPrevFocus = NULL;
}
int Tray_TrackMenu(HMENU hmenu)
{
TPMPARAMS tpm;
int iret;
tpm.cbSize = SIZEOF(tpm);
GetClientRect(g_ts.hwndStart, &tpm.rcExclude);
MapWindowPoints(g_ts.hwndStart, NULL, (LPPOINT)&tpm.rcExclude, 2);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
iret = TrackPopupMenuEx(hmenu, TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_RETURNCMD, tpm.rcExclude.left, tpm.rcExclude.bottom, v_hwndTray, &tpm);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
return iret;
}
// Keep track of the menu font name and weight so we can redo the startmenu if these change before getting notified via win.ini.
BOOL StartMenu_FontChange(void)
{
NONCLIENTMETRICS ncm;
static TCHAR lfFaceName[LF_FACESIZE] = TEXT("");
static long lfHeight = 0;
ncm.cbSize = SIZEOF(ncm);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE ))
{
if (!*lfFaceName)
{
// DebugMsg(DM_TRACE, "sm_fc: No menu font - initing.");
lstrcpy(lfFaceName, ncm.lfMenuFont.lfFaceName);
lfHeight = ncm.lfMenuFont.lfHeight;
return FALSE;
}
else if ((lstrcmpi(ncm.lfMenuFont.lfFaceName, lfFaceName) == 0) && (ncm.lfMenuFont.lfHeight == lfHeight))
{
// DebugMsg(DM_TRACE, "sm_fc: Menu font unchanged.");
return FALSE;
}
else
{
// DebugMsg(DM_TRACE, "sm_fc: Menu font changed.");
lstrcpy(lfFaceName, ncm.lfMenuFont.lfFaceName);
lfHeight = ncm.lfMenuFont.lfHeight;
return TRUE;
}
}
return FALSE;
}
/*
** Respond to a button's pressing by bringing up the appropriate menu.
** Clean up the button depression when the menu is dismissed.
*/
void ToolbarMenu()
{
RECTL rcExclude;
POINTL ptPop;
DWORD dwFlags = MPPF_KEYBOARD; // Assume that we're popuping
// up because of the keyboard
// This is for the underlines on NT5
if (g_ts.hwndStartBalloon)
{
DontShowTheStartButtonBalloonAnyMore();
ShowWindow(g_ts.hwndStartBalloon, SW_HIDE);
DestroyStartButtonBalloon();
}
SetActiveWindow(v_hwndTray);
g_ts.bMainMenuInit = TRUE;
GetClientRect(g_ts.hwndStart, (RECT *)&rcExclude);
MapWindowRect(g_ts.hwndStart, HWND_DESKTOP, &rcExclude);
ptPop.x = rcExclude.left;
ptPop.y = rcExclude.top;
// Close any Context Menus
SendMessage(v_hwndTray, WM_CANCELMODE, 0, 0);
// Is the "Activate" button down (If the buttons are swapped, then it's the
// right button, otherwise the left button)
if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON)?VK_RBUTTON:VK_LBUTTON) < 0)
{
dwFlags = 0; // Then set to the default
}
else
{
// Since the user has launched the start button by Ctrl-Esc, or some other worldly
// means, then turn the rect on.
SendMessage(g_ts.hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
}
if (g_ts._pmpStartMenu) {
g_ts._pmpStartMenu->lpVtbl->Popup(g_ts._pmpStartMenu, &ptPop, &rcExclude, dwFlags);
TraceMsg(DM_MISC, "e.tbm: dwFlags=%x (0=mouse 1=key)", dwFlags);
UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, dwFlags ? UIBL_INPMENU : UIBL_INPMOUSE);
}
}
// can't use SubtractRect sometimes because of inclusion limitations
void AppBarSubtractRect(PAPPBAR pab, LPRECT lprc)
{
switch (pab->uEdge)
{
case ABE_TOP:
if (pab->rc.bottom > lprc->top)
lprc->top = pab->rc.bottom;
break;
case ABE_LEFT:
if (pab->rc.right > lprc->left)
lprc->left = pab->rc.right;
break;
case ABE_BOTTOM:
if (pab->rc.top < lprc->bottom)
lprc->bottom = pab->rc.top;
break;
case ABE_RIGHT:
if (pab->rc.left < lprc->right)
lprc->right = pab->rc.left;
break;
}
}
void AppBarSubtractRects(HMONITOR hmon, LPRECT lprc)
{
int i;
if (!g_ts.hdpaAppBars)
return;
i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--)
{
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
// autohide bars are not in our DPA or live on the edge
// BUGBUG: don't subtract the appbar if it is not always on top????
// don't subtract the appbar if it's on a different display
if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL))
AppBarSubtractRect(pab, lprc);
}
}
#define RWA_NOCHANGE 0
#define RWA_CHANGED 1
#define RWA_BOTTOMMOSTTRAY 2
// BUGBUG: (dli) This is a hack put in because bottommost tray is wierd, once
// it becomes a toolbar, this code should go away.
// In the bottommost tray case, even though the work area has not changed,
// we should notify the desktop.
int RecomputeWorkArea(HWND hwndCause, HMONITOR hmon, LPRECT prcWork)
{
int iRet = RWA_NOCHANGE;
MONITORINFO mi;
// tell everybody that this window changed positions _on_this_monitor_
// note that this notify happens even if we don't change the work area
// since it may cause another app to change the work area...
PostMessage(v_hwndTray, TM_RELAYPOSCHANGED, (WPARAM)hwndCause, (LPARAM)hmon);
// get the current info for this monitor
// we subtract down from the display rectangle to build the work area
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hmon, &mi))
{
// don't subtract the tray if it is autohide don't subtract the tray if it is not always on top
// don't subtract the tray if it's on a different display
if (!(g_ts.uAutoHide & AH_ON) && g_ts.fAlwaysOnTop && (hmon == g_ts.hmonStuck))
{
SubtractRect(prcWork, &mi.rcMonitor, &g_ts.arStuckRects[g_ts.uStuckPlace]);
}
else
*prcWork = mi.rcMonitor;
// now subtract off all the appbars on this display
AppBarSubtractRects(hmon, prcWork);
// return whether we changed anything
if (!EqualRect(prcWork, &mi.rcWork))
iRet = RWA_CHANGED;
else if (!(g_ts.uAutoHide & AH_ON) && (!g_ts.fAlwaysOnTop) && (!IsRectEmpty(&g_ts.arStuckRects[g_ts.uStuckPlace])))
// NOTE: This is the bottommost case, it only applies for the tray.
// this should be taken out when bottommost tray becomes toolbar
iRet = RWA_BOTTOMMOSTTRAY;
}
else
{
// NOTE: This should never happen, if it does because of USER problem,
// we just say NO CHANGE!!!!
ASSERTMSG(FALSE, "GetMonitorInfo should never fail! We should have updated our hmonOld or hmonStuck on time");
iRet = RWA_NOCHANGE;
}
return iRet;
}
// Nashville's debug USER draws silly "Monitor N CXxCYxBPP" crud on the display
#if !defined(WINNT) && defined(DEBUG)
#define FORCE_INVALIDATE_WORKAREA TRUE
#else
#define FORCE_INVALIDATE_WORKAREA FALSE
#endif
void RedrawDesktop(LPRECT prcWork)
{
// This rect point should always be valid (dli)
RIP(prcWork);
if (v_hwndDesktop && (FORCE_INVALIDATE_WORKAREA || g_fCleanBoot))
{
MapWindowPoints(NULL, v_hwndDesktop, (LPPOINT)prcWork, 2);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac invalidating desktop rect {%d,%d,%d,%d}"), prcWork->left, prcWork->top, prcWork->right, prcWork->bottom);
RedrawWindow(v_hwndDesktop, prcWork, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
}
}
void StuckAppChange(HWND hwndCause, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray)
{
RECT rcWork1, rcWork2;
HMONITOR hmon1, hmon2 = 0;
int iChange = 0;
// BUGBUG:
// there are cases where we end up setting the work area multiple times
// we need to keep a static array of displays that have changed and a
// reenter count so we can avoid pain of sending notifies to the whole
// planet...
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac from AppBar %08X"), hwndCause);
// see if the work area changed on the display containing prcOld
if (prcOld)
{
if (bTray)
hmon1 = g_ts.hmonOld;
else
hmon1 = MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac old pos {%d,%d,%d,%d} on monitor %08X"), prcOld->left, prcOld->top, prcOld->right, prcOld->bottom, hmon1);
if (hmon1)
{
int iret = RecomputeWorkArea(hwndCause, hmon1, &rcWork1);
if (iret == RWA_CHANGED)
iChange = 1;
if (iret == RWA_BOTTOMMOSTTRAY)
iChange = 4;
}
}
else
hmon1 = NULL;
// see if the work area changed on the display containing prcNew
if (prcNew)
{
hmon2 = MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac new pos {%d,%d,%d,%d} on monitor %08X"), prcNew->left, prcNew->top, prcNew->right, prcNew->bottom, hmon2);
if (hmon2 && (hmon2 != hmon1))
{
int iret = RecomputeWorkArea(hwndCause, hmon2, &rcWork2);
if (iret == RWA_CHANGED)
iChange |= 2;
else if (iret == RWA_BOTTOMMOSTTRAY && (!iChange))
iChange = 4;
}
}
// did the prcOld's display's work area change?
if (iChange & 1)
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon1);
// only send SENDWININICHANGE if the desktop has been created (otherwise
// we will hang the explorer because the main thread is currently blocked)
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork1, (iChange == 1 && v_hwndDesktop)? SPIF_SENDWININICHANGE : 0);
RedrawDesktop(&rcWork1);
}
// did the prcOld's display's work area change?
if (iChange & 2)
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon2);
// only send SENDWININICHANGE if the desktop has been created (otherwise
// we will hang the explorer because the main thread is currently blocked)
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork2, v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
RedrawDesktop(&rcWork2);
}
// only send if the desktop has been created...
// need to send if it's from the tray or any outside app that causes size change
// from the tray because autohideness will affect desktop size even if it's not always on top
if ((bTray || iChange == 4) && v_hwndDesktop )
SendMessage(v_hwndDesktop, WM_SIZE, 0, 0);
}
void Tray_StuckTrayChange()
{
// We used to blow off the StuckAppChange when the tray was in autohide
// mode, since moving or resizing an autohid tray doesn't change the
// work area. Now we go ahead with the StuckAppChange in this case
// too. The reason is that we can get into a state where the work area
// size is incorrect, and we want the taskbar to always be self-repairing
// in this case (so that resizing or moving the taskbar will correct the work area size).
// pass a NULL window here since we don't want to hand out our window and
// the tray doesn't get these anyway (nobody cares as long as its not them)
StuckAppChange(NULL, &g_ts.rcOldTray, &g_ts.arStuckRects[g_ts.uStuckPlace], TRUE);
// save off the new tray position...
g_ts.rcOldTray = g_ts.arStuckRects[g_ts.uStuckPlace];
}
UINT Tray_RecalcStuckPos(LPRECT prc)
{
RECT rcDummy;
POINT pt;
if (!prc)
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp no rect supplied, using window rect"));
prc = &rcDummy;
GetWindowRect(v_hwndTray, prc);
}
// use the center of the original drag rect as a staring point
pt.x = prc->left + RECTWIDTH(*prc) / 2;
pt.y = prc->top + RECTHEIGHT(*prc) / 2;
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp rect is {%d, %d, %d, %d} point is {%d, %d}"), prc->left, prc->top, prc->right, prc->bottom, pt.x, pt.y);
// reset this so the drag code won't give it preference
g_ts.uMoveStuckPlace = (UINT)-1;
// simulate a drag back to figure out where we originated from
// you may be tempted to remove this. before you do think about dragging
// the tray across monitors and then hitting ESC...
return Tray_CalcDragPlace(pt);
}
/*
** the position is changing in response to a move operation.
** if the docking status changed, we need to get a new size and
** maybe a new frame style. change the WINDOWPOS to reflect these changes accordingly.
*/
void Tray_DoneMoving(LPWINDOWPOS lpwp)
{
RECT rc, *prc;
if (g_ts.uMoveStuckPlace == (UINT)-1)
return;
if (g_ts.fSysSizing)
g_ts._fDeferedPosRectChange = TRUE;
rc.left = lpwp->x;
rc.top = lpwp->y;
rc.right = lpwp->x + lpwp->cx;
rc.bottom = lpwp->y + lpwp->cy;
prc = &g_ts.arStuckRects[g_ts.uMoveStuckPlace];
if (!EqualRect(prc, &rc))
{
g_ts.uMoveStuckPlace = Tray_RecalcStuckPos(&rc);
prc = &g_ts.arStuckRects[g_ts.uMoveStuckPlace];
}
// Get the new hmonitor
g_ts.hmonStuck = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
if (g_ts.hwndView)
Tray_HandleSizing(0, prc, g_ts.uMoveStuckPlace);
lpwp->x = prc->left;
lpwp->y = prc->top;
lpwp->cx = RECTWIDTH(*prc);
lpwp->cy = RECTHEIGHT(*prc);
lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
// if we were autohiding, we need to update our appbar autohide rect
if (g_ts.uAutoHide & AH_ON)
{
// unregister us from the old side
IAppBarSetAutoHideBar(v_hwndTray, FALSE, g_ts.uStuckPlace);
}
// All that work might've changed g_ts.uMoveStuckPlace (since there
// was a lot of message traffic), so check one more time.
// Somehow, NT Stress manages to get us in here with an invalid
// uMoveStuckPlace.
if (IsValidSTUCKPLACE(g_ts.uMoveStuckPlace))
{
// remember the new state
g_ts.uStuckPlace = g_ts.uMoveStuckPlace;
}
g_ts.uMoveStuckPlace = (UINT)-1;
BandSite_SetMode(g_ts.ptbs, STUCK_HORIZONTAL(g_ts.uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
if ((g_ts.uAutoHide & AH_ON) && !IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace))
{
Tray_AutoHideCollision();
}
}
UINT Tray_CalcDragPlace(POINT pt)
{
UINT uPlace = g_ts.uMoveStuckPlace;
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp starting point is {%d, %d}"), pt.x, pt.y);
// if the mouse is currently over the tray position leave it alone
if ((uPlace == (UINT)-1) || !PtInRect(&g_ts.arStuckRects[uPlace], pt))
{
HMONITOR hmonDrag;
SIZE screen, error;
UINT uHorzEdge, uVertEdge;
RECT rcDisplay, *prcStick;
// which display is the mouse on?
hmonDrag = Tray_GetDisplayRectFromPoint(&rcDisplay, pt, MONITOR_DEFAULTTOPRIMARY);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp monitor is %08X"), hmonDrag);
// re-origin at zero to make calculations simpler
screen.cx = RECTWIDTH(rcDisplay);
screen.cy = RECTHEIGHT(rcDisplay);
pt.x -= rcDisplay.left;
pt.y -= rcDisplay.top;
// are we closer to the left or right side of this display?
if (pt.x < (screen.cx / 2))
{
uVertEdge = STICK_LEFT;
error.cx = pt.x;
}
else
{
uVertEdge = STICK_RIGHT;
error.cx = screen.cx - pt.x;
}
// are we closer to the top or bottom side of this display?
if (pt.y < (screen.cy / 2))
{
uHorzEdge = STICK_TOP;
error.cy = pt.y;
}
else
{
uHorzEdge = STICK_BOTTOM;
error.cy = screen.cy - pt.y;
}
// closer to a horizontal or vertical edge?
uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))? uVertEdge : uHorzEdge;
// which StuckRect should we use?
prcStick = &g_ts.arStuckRects[uPlace];
// need to recalc stuck rect for new monitor?
if ((hmonDrag != Tray_GetDisplayRectFromRect(NULL, prcStick, MONITOR_DEFAULTTONULL)))
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp re-snapping rect for new display"));
Tray_MakeStuckRect(prcStick, &rcDisplay, g_ts.sStuckWidths, uPlace);
}
}
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp edge is %d, rect is {%d, %d, %d, %d}"),
uPlace, g_ts.arStuckRects[uPlace].left, g_ts.arStuckRects[uPlace].top, g_ts.arStuckRects[uPlace].right, g_ts.arStuckRects[uPlace].bottom);
ASSERT(IsValidSTUCKPLACE(uPlace));
return uPlace;
}
void Tray_HandleMoving(WPARAM wParam, LPRECT lprc)
{
POINT ptCursor;
GetCursorPos(&ptCursor);
g_ts.uMoveStuckPlace = Tray_CalcDragPlace(ptCursor);
*lprc = g_ts.arStuckRects[g_ts.uMoveStuckPlace];
Tray_HandleSizing(wParam, lprc, g_ts.uMoveStuckPlace);
}
// store the tray size when dragging is finished
void Tray_SnapshotStuckRectSize(UINT uPlace)
{
RECT rcDisplay, *prc = &g_ts.arStuckRects[uPlace];
// record the width of this stuck rect
if (STUCK_HORIZONTAL(uPlace))
g_ts.sStuckWidths.cy = RECTHEIGHT(*prc);
else
g_ts.sStuckWidths.cx = RECTWIDTH(*prc);
// we only present a horizontal or vertical size to the end user
// so update the StuckRect on the other side of the screen to match
Tray_GetStuckDisplayRect(uPlace, &rcDisplay);
uPlace += 2;
uPlace %= 4;
prc = &g_ts.arStuckRects[uPlace];
Tray_MakeStuckRect(prc, &rcDisplay, g_ts.sStuckWidths, uPlace);
}
// Size the icon area to fill as much of the tray window as it can.
void Tray_SizeWindows()
{
RECT rcView, rcClock, rcClient, rcDesktop;
int fHiding;
if (!g_ts.hwndRebar || !g_ts.hwndMain || !g_ts.hwndNotify)
return;
fHiding = (g_ts.uAutoHide & AH_HIDING);
if (fHiding)
{
TrayShowWindow(SW_HIDE);
}
// remember our current size
Tray_SnapshotStuckRectSize(g_ts.uStuckPlace);
GetClientRect(g_ts.hwndMain, &rcClient);
Tray_AlignStartButton();
Tray_GetWindowSizes(&rcClient, &rcView, &rcClock, &rcDesktop);
InvalidateRect(g_ts.hwndStart, NULL, TRUE);
// position the view
SetWindowPos(g_ts.hwndRebar, NULL, rcView.left, rcView.top, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOZORDER | SWP_NOACTIVATE);
// And the clock
SetWindowPos(g_ts.hwndNotify, NULL, rcClock.left, rcClock.top, RECTWIDTH(rcClock), RECTHEIGHT(rcClock), SWP_NOZORDER | SWP_NOACTIVATE);
// And the desktop button
if (g_hwndDesktopTB)
SetWindowPos(g_hwndDesktopTB, NULL, rcDesktop.left, rcDesktop.top, RECTWIDTH(rcDesktop), RECTHEIGHT(rcDesktop), SWP_NOZORDER | SWP_NOACTIVATE);
{
TOOLINFO ti;
HWND hwndClock = Tray_GetClockWindow();
ti.cbSize = SIZEOF(ti);
ti.uFlags = 0;
ti.hwnd = g_ts.hwndMain;
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.uId = (UINT_PTR)hwndClock;
GetWindowRect(hwndClock, &ti.rect);
MapWindowPoints(HWND_DESKTOP, g_ts.hwndMain, (LPPOINT)&ti.rect, 2);
SendMessage(g_ts.hwndTrayTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
}
if (fHiding)
{
TrayShowWindow(SW_SHOWNA);
}
}
#define MAX_SIZING_RECUR 7000
void Tray_NegotiateSizing(WPARAM code, PRECT prcClient)
{
RECT rcView, rcClock, rcDesktop;
int iHeight;
UINT uRecur = 0;
while (uRecur++ < MAX_SIZING_RECUR) // guard against infinite loop
{
// given this sizing, what would our children's sizes be?
// use Client coords
prcClient->bottom -= prcClient->top;
prcClient->right -= prcClient->left;
prcClient->top = prcClient->left = 0;
Tray_GetWindowSizes(prcClient, &rcView, &rcClock, &rcDesktop);
iHeight = RECTHEIGHT(rcView);
// maybe we should translate this to screen coordinates?
// that would allow our children to prevent being in certain
// physical locations, but we're more concerned with size, not loc
if (g_ts.hwndView)
// Avoid rips because at WM_CREATE time, this is null.
SendMessage(g_ts.hwndView, WM_SIZING, code, (LPARAM)(LPRECT)&rcView);
// did it change?
if (RECTHEIGHT(rcView) != iHeight) {
// grow the prcClient by the same amound rcView grew;
prcClient->bottom += RECTHEIGHT(rcView) - iHeight;
}
else
return;
}
}
int Window_GetClientGapHeight(HWND hwnd)
{
RECT rc;
SetRectEmpty(&rc);
AdjustWindowRectEx(&rc, GetWindowLong(hwnd, GWL_STYLE), FALSE, GetWindowLong(hwnd, GWL_EXSTYLE));
return RECTHEIGHT(rc);
}
void Tray_HandleSize()
{
// if somehow we got minimized go ahead and un-minimize
if (((GetWindowLong(v_hwndTray, GWL_STYLE)) & WS_MINIMIZE))
{
ASSERT(FALSE);
ShowWindow(v_hwndTray, SW_RESTORE);
}
// if we are in the move/size loop and are visible then
// re-snap the current stuck rect to the new window size
#ifdef DEBUG
if (g_ts.fSysSizing && (g_ts.uAutoHide & AH_HIDING)) {
TraceMsg(DM_TRACE, "fSysSize && hiding");
ASSERT(0);
}
#endif
if (g_ts.fSysSizing &&
((g_ts.uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING)))
{
g_ts.uStuckPlace = Tray_RecalcStuckPos(NULL);
}
// if we are in fulldrag or we are not in the middle of a move/size then
// we should resize all our child windows to reflect our new size
if (g_fDragFullWindows || !g_ts.fSysSizing)
Tray_SizeWindows();
// if we are just plain resized and we are visible we may need re-dock
if (!g_ts.fSysSizing && !g_ts.fSelfSizing && IsWindowVisible(v_hwndTray))
{
if (g_ts.uAutoHide & AH_ON)
{
UINT uPlace = g_ts.uStuckPlace;
HWND hwndOther = AppBarGetAutoHideBar(uPlace);
// we sometimes defer checking for this until after a move
// so as to avoid interrupting a full-window-drag in progress
// if there is a different autohide window in our slot then whimper
if (hwndOther? (hwndOther != v_hwndTray) : !IAppBarSetAutoHideBar(v_hwndTray, TRUE, uPlace))
{
Tray_AutoHideCollision();
}
}
Tray_StuckTrayChange();
// make sure we clip to tray to the current monitor (if necessary)
Tray_ClipWindow(TRUE);
}
if (g_ts.hwndStartBalloon)
{
RECT rc;
GetWindowRect(g_ts.hwndStart, &rc);
SendMessage(g_ts.hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
SetWindowZorder(g_ts.hwndStartBalloon, HWND_TOPMOST);
}
}
void Tray_HandleSizing(WPARAM code, LPRECT lprc, UINT uStuckPlace)
{
RECT rc, rcDisplay;
int iHeight, iMax;
int iYExtra; // Y difference between the window and client height;
SIZE sNewWidths;
RECT rcTemp;
BOOL fHiding;
if (!lprc) {
rcTemp = g_ts.arStuckRects[uStuckPlace];
lprc = &rcTemp;
}
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
#if 1
fHiding = (g_ts.uAutoHide & AH_HIDING);
if (fHiding) {
TrayShowWindow(SW_HIDE);
}
#else
// old code pre-980504
if (g_ts.uAutoHide & AH_HIDING) {
TraceMsg(DM_TRACE, "e.t_hs AH_HIDING => early-out");
return;
}
#endif
KillConfigDesktopDlg();
// get the a bunch of relevant dimensions
// (dli) need to change this funciton or get rid of it
Tray_GetDisplayRectFromRect(&rcDisplay, lprc, MONITOR_DEFAULTTONEAREST);
iYExtra = Window_GetClientGapHeight(g_ts.hwndMain);
if (g_cyTrayBorders == -1)
g_cyTrayBorders = iYExtra; // on initial boot
if (code) {
// if code != 0, this is the user sizing.
// make sure they clip it to the screen.
RECT rcTemp = rcDisplay;
InflateRect(&rcTemp, g_cxEdge, g_cyEdge);
// don't do intersect rect because of sizing up from the bottom (when taskbar docked on bottom)
// confuses people
switch (uStuckPlace)
{
case STICK_LEFT: lprc->left = rcTemp.left; break;
case STICK_TOP: lprc->top = rcTemp.top; break;
case STICK_RIGHT: lprc->right = rcTemp.right; break;
case STICK_BOTTOM: lprc->bottom = rcTemp.bottom; break;
}
}
// compute the new widths
// don't let either be more than half the screen
sNewWidths.cx = RECTWIDTH(*lprc);
iMax = RECTWIDTH(rcDisplay) / 2;
if (sNewWidths.cx > iMax)
sNewWidths.cx = iMax;
sNewWidths.cy = RECTHEIGHT(*lprc);
iMax = RECTHEIGHT(rcDisplay) / 2;
if (sNewWidths.cy > iMax)
sNewWidths.cy = iMax;
if (STUCK_HORIZONTAL(uStuckPlace))
g_cyTrayBorders = iYExtra;
// compute an initial size
Tray_MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
// negotiate the exact size with our children
rc = *lprc;
rc.bottom -= g_cyTrayBorders;
Tray_NegotiateSizing(code, &rc);
iHeight = RECTHEIGHT(rc);
// was there a change?
if ((iHeight + iYExtra) != sNewWidths.cy)
{
// yes, update the final rectangle
sNewWidths.cy = iHeight + iYExtra;
Tray_MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
}
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs final rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
// store the new size in the appropriate StuckRect
g_ts.arStuckRects[uStuckPlace] = *lprc;
// remember the current client gap
g_cyTrayBorders = iYExtra;
if (fHiding) {
TrayShowWindow(SW_SHOWNA);
}
if (g_ts.hwndStartBalloon)
{
RECT rc;
GetWindowRect(g_ts.hwndStart, &rc);
SendMessage(g_ts.hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
SetWindowZorder(g_ts.hwndStartBalloon, HWND_TOPMOST);
}
}
/*
** the screen size changed, and we need to adjust some stuff, mostly
** globals. if the tray was docked, it needs to be resized, too.
** TRICKINESS: the handling of WM_WINDOWPOSCHANGING is used to
** actually do all the real sizing work. this saves a bit of
** extra code here.
*/
STDAPI_(BOOL) SHIsTempDisplayMode();
BOOL CALLBACK Tray_MonitorEnumCallBack(HMONITOR hMonitor, HDC hdc, LPRECT lprc, LPARAM lData)
{
RECT rcWork;
int iRet = RecomputeWorkArea(NULL, hMonitor, &rcWork);
if (iRet == RWA_CHANGED)
{
// only send SENDWININICHANGE if the desktop has been created (otherwise
// we will hang the explorer because the main thread is currently blocked)
// BUGBUG: it will be nice to send WININICHANGE only once, but we can't
// because each time the rcWork is different, and there is no way to do it all
// bug opened on this.
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork, v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
RedrawDesktop(&rcWork);
}
return TRUE;
}
void Tray_RecomputeAllWorkareas()
{
EnumDisplayMonitors(NULL, NULL, Tray_MonitorEnumCallBack, (LPARAM)NULL);
}
void Tray_ScreenSizeChange(HWND hwnd)
{
if (SHIsTempDisplayMode())
{
// this will suppress updates so warn to be nice
TraceMsg(DM_TRACE, "t_ssc: IsTempDisplayMode=1 => nop!");
return;
}
// Set our new HMONITOR in case there is some change
{
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
// Is our old HMONITOR still valid?
// NOTE: This test is used to tell whether somethings happened to the
// HMONITOR's or just the screen size changed
if (!GetMonitorInfo(g_ts.hmonStuck, &mi))
{
// No, this means the HMONITORS changed, our monitor might have gone away
Tray_SetStuckMonitor();
Tray_RecomputeAllWorkareas();
}
}
// screen size changed, so we need to adjust globals
g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
ResizeStuckRects(g_ts.arStuckRects);
if (hwnd)
{
// set a bogus windowpos and actually repaint with the right
// shape/size in handling the WINDOWPOSCHANGING message
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);
}
Tray_SizeWindows();
// In the multi-monitor case, we need to turn on clipping for the dynamic
// monitor changes i.e. when the user add monitors or remove them from the
// control panel.
Tray_ClipWindow(TRUE);
}
BOOL _UpdateAlwaysOnTop(BOOL fAlwaysOnTop)
{
BOOL fChanged = ((g_ts.fAlwaysOnTop == 0) != (fAlwaysOnTop == 0));
// The user clicked on the AlwaysOnTop menu item, we should now toggle
// the state and update the window accordingly...
g_ts.fAlwaysOnTop = fAlwaysOnTop;
Tray_ResetZorder();
// Make sure the screen limits are update to the new state.
Tray_StuckTrayChange();
return fChanged;
}
void ViewOptionsUpdateDisplay(HWND hDlg);
void _ApplyViewOptionsFromDialog(LPPROPSHEETPAGE psp, HWND hDlg)
{
// We need to get the Cabinet structure from the property sheet info.
BOOL fSmallPrev;
BOOL fSmallNew;
BOOL fAutoHide;
BOOL fChanged;
#ifdef DESKBTN
BOOL fShowDeskBtn;
#endif
LONG lRet;
fChanged = _UpdateAlwaysOnTop(IsDlgButtonChecked(hDlg, IDC_TRAYOPTONTOP));// First check for Always on Top
// And change the Autohide state
fAutoHide = IsDlgButtonChecked(hDlg, IDC_TRAYOPTAUTOHIDE);
lRet = Tray_SetAutoHideState(fAutoHide);
if (!HIWORD(lRet) && fAutoHide) {
// we tried and failed.
if (!(g_ts.uAutoHide & AH_ON)) {
CheckDlgButton(hDlg, IDC_TRAYOPTAUTOHIDE, FALSE);
ViewOptionsUpdateDisplay(hDlg);
}
}
fChanged |= LOWORD(lRet);
if (fChanged)
AppBarNotifyAll(NULL, ABN_STATECHANGE, NULL, 0);
// show/hide the clock
g_ts.fHideClock = !IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWCLOCK);
SendMessage(g_ts.hwndNotify, TNM_HIDECLOCK, 0, g_ts.fHideClock);
#ifdef DESKBTN
// show/hide the desktop button
fShowDeskBtn = IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWDESKBTN);
ShowHideDeskBtnOnQuickLaunch(fShowDeskBtn);
g_ts.fShowDeskBtn = fShowDeskBtn;
#endif
Tray_SizeWindows();
// check if it's changed
fSmallPrev = (g_ts.fSMSmallIcons ? TRUE : FALSE);
fSmallNew = (IsDlgButtonChecked(hDlg, IDC_SMSMALLICONS) ? TRUE : FALSE);
if (fSmallPrev != fSmallNew)
{
g_ts.fSMSmallIcons = fSmallNew;
IMenuPopup_SetIconSize(g_ts._pmpStartMenu, g_ts.fSMSmallIcons ? BMICON_SMALL : BMICON_LARGE);
}
if (!SHRestricted(REST_INTELLIMENUS))
{
LPTSTR psz = IsDlgButtonChecked(hDlg, IDC_PERSONALIZEDMENUS)? TEXT("YES") : TEXT("No");
SHRegSetUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("IntelliMenus"), REG_SZ, psz, lstrlen(psz) * sizeof(TCHAR), SHREGSET_FORCE_HKCU);
PostMessage(v_hwndTray, TM_REFRESH, 0, 0);
}
}
void ViewOptionsInitBitmaps(HWND hDlg)
{
int i;
for (i = 0; i <= (IDB_VIEWOPTIONSLAST - IDB_VIEWOPTIONSFIRST); i++) {
HBITMAP hbm;
hbm = LoadImage(hinstCabinet, MAKEINTRESOURCE(IDB_VIEWOPTIONSFIRST + i), IMAGE_BITMAP, 0,0, LR_LOADMAP3DCOLORS);
hbm = (HBITMAP)SendDlgItemMessage(hDlg, IDC_VIEWOPTIONSICONSFIRST + i, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm);
if (hbm)
DeleteBitmap(hbm);
}
}
void ViewOptionsDestroyBitmaps(HWND hDlg)
{
int i;
for (i = 0; i <= (IDB_VIEWOPTIONSLAST - IDB_VIEWOPTIONSFIRST); i++) {
HBITMAP hbmOld;
hbmOld = (HBITMAP)SendDlgItemMessage(hDlg, IDC_VIEWOPTIONSICONSFIRST + i, STM_GETIMAGE, 0,0);
if (hbmOld)
DeleteBitmap(hbmOld);
}
}
void ViewOptionsUpdateDisplay(HWND hDlg)
{
HWND hwnd;
BOOL fShown;
BOOL fAutoHide, fShowDeskBtn, fShowClock;
// this works by having the background bitmap having the
// small menu, the tray in autohide/ontop mode and we lay other statics
// over it to give the effect we want.
SetWindowZorder(GetDlgItem(hDlg, IDC_VOBASE), HWND_BOTTOM);
ShowWindow(GetDlgItem(hDlg, IDC_VOLARGEMENU), !IsDlgButtonChecked(hDlg, IDC_SMSMALLICONS) ? SW_SHOWNA : SW_HIDE);
fAutoHide = IsDlgButtonChecked(hDlg, IDC_TRAYOPTAUTOHIDE);
#ifdef DESKBTN
fShowDeskBtn = IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWDESKBTN);
#else
fShowDeskBtn = FALSE;
#endif
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAY), (!fAutoHide && !fShowDeskBtn) ? SW_SHOWNA : SW_HIDE);
#ifdef DESKBTN
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAYD), (!fAutoHide && fShowDeskBtn) ? SW_SHOWNA : SW_HIDE);
#endif
fShowClock = IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWCLOCK);
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAYNOCLOCK), (!fAutoHide && !fShowClock && !fShowDeskBtn) ? SW_SHOWNA : SW_HIDE);
#ifdef DESKBTN
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAYNOCLOCKD), (!fAutoHide && !fShowClock && fShowDeskBtn) ? SW_SHOWNA : SW_HIDE);
#endif
ShowWindow(hwnd = GetDlgItem(hDlg, IDC_VOWINDOW), (FALSE != (fShown = !IsDlgButtonChecked(hDlg, IDC_TRAYOPTONTOP)) ? SW_SHOWNA : SW_HIDE));
if (fShown)
SetWindowZorder(hwnd, HWND_TOP);
}
#define CXVOBASE 274
#define CYVOBASE 130
#define XVOMENU 2
#define YVOMENU 0
#define CXVOMENU 150
#define CYVOMENU 98
#define XVOTRAY 2
#define YVOTRAY 100
#define CXVOTRAY 268
#define CYVOTRAY 28
#define XVOTRAYNOCLOCK (XVOTRAY + 184)
#define YVOTRAYNOCLOCK (YVOTRAY + 3)
#define CXVOTRAYNOCLOCK 79
#define CYVOTRAYNOCLOCK 22
#define XVOTRAYNOCLOCKD (XVOTRAY + 148)
#define YVOTRAYNOCLOCKD YVOTRAYNOCLOCK
#define CXVOTRAYNOCLOCKD 119
#define CYVOTRAYNOCLOCKD CYVOTRAYNOCLOCK
#define XVOWINDOW 212
#define YVOWINDOW 100
#define CXVOWINDOW 61
#define CYVOWINDOW 6
// need to do this by hand because dialog units to pixels will change, but the bitmaps won't
void ViewOptionsSizeControls(HWND hDlg)
{
POINT ptBase; // coordinates of origin of VOBase.bmp
HWND hwnd;
ptBase.x = ptBase.y = 0;
hwnd = GetDlgItem(hDlg, IDC_VOBASE);
MapWindowPoints(hwnd, hDlg, &ptBase, 1);
// over it to give the effect we want.
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, CXVOBASE, CYVOBASE, SWP_NOMOVE | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_VOLARGEMENU), NULL, ptBase.x + XVOMENU, ptBase.y + YVOMENU, CXVOMENU, CYVOMENU, SWP_NOACTIVATE | SWP_NOZORDER);
SetWindowPos(GetDlgItem(hDlg, IDC_VOTRAY), NULL, ptBase.x + XVOTRAY, ptBase.y + YVOTRAY, CXVOTRAY, CYVOTRAY, SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_VOTRAYNOCLOCK), HWND_TOP, ptBase.x + XVOTRAYNOCLOCK, ptBase.y + YVOTRAYNOCLOCK, CXVOTRAYNOCLOCK, CYVOTRAYNOCLOCK, SWP_NOACTIVATE);
#ifdef DESKBTN
SetWindowPos(GetDlgItem(hDlg, IDC_VOTRAYD), NULL, ptBase.x + XVOTRAY, ptBase.y + YVOTRAY, CXVOTRAY, CYVOTRAY, SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_VOTRAYNOCLOCKD), HWND_TOP, ptBase.x + XVOTRAYNOCLOCKD, ptBase.y + YVOTRAYNOCLOCKD, CXVOTRAYNOCLOCKD, CYVOTRAYNOCLOCKD, SWP_NOACTIVATE);
#endif
{
BITMAP bmp;
HBITMAP hbmpVoBase = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(IDB_VOBASE));
GetObject(hbmpVoBase, sizeof(BITMAP), &bmp);
DeleteObject(hbmpVoBase);
SetWindowPos(GetDlgItem(hDlg, IDC_VOWINDOW), NULL, ptBase.x + bmp.bmWidth - CXVOWINDOW -1, ptBase.y + YVOWINDOW, CXVOWINDOW, CYVOWINDOW, SWP_NOACTIVATE);
}
}
const static DWORD aTaskOptionsHelpIDs[] = { // Context Help IDs
IDC_VOBASE, IDH_TASKBAR_OPTIONS_BITMAP,
IDC_VOLARGEMENU, IDH_TASKBAR_OPTIONS_BITMAP,
IDC_VOTRAY, IDH_TASKBAR_OPTIONS_BITMAP,
IDC_VOWINDOW, IDH_TASKBAR_OPTIONS_BITMAP,
IDC_VOTRAYD, IDH_TASKBAR_OPTIONS_BITMAP,
IDC_TRAYOPTONTOP, IDH_TRAY_TASKBAR_ONTOP,
IDC_TRAYOPTAUTOHIDE, IDH_TRAY_TASKBAR_AUTOHIDE,
IDC_SMSMALLICONS, IDH_STARTMENU_SMALLICONS,
IDC_TRAYOPTSHOWCLOCK, IDH_TRAY_SHOW_CLOCK,
IDC_PERSONALIZEDMENUS,IDH_TRAY_PERSONALIZED_MENUS,
0, 0
};
#define REGSTR_EXPLORER_ADVANCED REGSTR_PATH_EXPLORER TEXT("\\Advanced")
BOOL_PTR CALLBACK TrayViewOptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPPROPSHEETPAGE psp = GetWindowPtr(hDlg, DWLP_USER);
INSTRUMENT_WNDPROC(SHCNFI_TRAYVIEWOPTIONS_DLGPROC, hDlg, uMsg, wParam, lParam);
switch (uMsg) {
case WM_COMMAND:
ViewOptionsUpdateDisplay(hDlg);
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
break;
case WM_INITDIALOG:
{
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
if (g_ts.fAlwaysOnTop)
CheckDlgButton(hDlg, IDC_TRAYOPTONTOP, TRUE);
if (g_ts.uAutoHide & AH_ON)
CheckDlgButton(hDlg, IDC_TRAYOPTAUTOHIDE, TRUE);
if (g_ts.fSMSmallIcons)
CheckDlgButton(hDlg, IDC_SMSMALLICONS, TRUE);
if (!g_ts.fHideClock)
CheckDlgButton(hDlg, IDC_TRAYOPTSHOWCLOCK, TRUE);
if (SHRestricted(REST_INTELLIMENUS))
{
// If there is a restriction of any kine, hide the window
ShowWindow(GetDlgItem(hDlg, IDC_PERSONALIZEDMENUS), FALSE);
}
else if (SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("IntelliMenus"), FALSE, TRUE))
{
CheckDlgButton(hDlg, IDC_PERSONALIZEDMENUS, TRUE);
}
#ifdef DESKBTN
if (g_ts.fShowDeskBtn)
CheckDlgButton(hDlg, IDC_TRAYOPTSHOWDESKBTN, TRUE);
#endif
ViewOptionsSizeControls(hDlg);
ViewOptionsInitBitmaps(hDlg);
ViewOptionsUpdateDisplay(hDlg);
}
break;
case WM_SYSCOLORCHANGE:
ViewOptionsInitBitmaps(hDlg);
return TRUE;
case WM_DESTROY:
SetForegroundWindow(v_hwndTray);
ViewOptionsDestroyBitmaps(hDlg);
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_APPLY:
// save settings here
_ApplyViewOptionsFromDialog(psp, hDlg);
return TRUE;
case PSN_KILLACTIVE:
case PSN_SETACTIVE:
return TRUE;
}
break;
case WM_HELP:
WinHelp(((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aTaskOptionsHelpIDs);
break;
case WM_CONTEXTMENU:
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aTaskOptionsHelpIDs);
break;
}
return FALSE;
}
BOOL_PTR CALLBACK InitStartMenuDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); // cfgstart.c
/*
** Method to add view property sheet pages to the tray property sheet
*/
STDMETHODIMP CShellTray_AddViewPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam)
{
HPROPSHEETPAGE hpage;
PROPSHEETPAGE psp;
psp.dwSize = SIZEOF(psp);
psp.dwFlags = PSP_DEFAULT;
psp.hInstance = hinstCabinet;
psp.pszTemplate = MAKEINTRESOURCE(DLG_TRAY_VIEW_OPTIONS);
psp.pfnDlgProc = TrayViewOptionsDlgProc;
psp.lParam = 0;
hpage = CreatePropertySheetPage(&psp);
if (hpage)
lpfn(hpage, lParam);
psp.pszTemplate = MAKEINTRESOURCE(DLG_STARTMENU_CONFIG);
psp.pfnDlgProc = InitStartMenuDlgProc;
psp.lParam = 0;
hpage = CreatePropertySheetPage(&psp);
if (hpage)
lpfn(hpage, lParam);
return S_OK;
}
void _SaveTray(void)
{
if (SHRestricted(REST_NOSAVESET))
return;
if (SHRestricted(REST_CLEARRECENTDOCSONEXIT))
ClearRecentDocumentsAndMRUStuff(FALSE);
HotkeyList_Save();
_SaveTrayStuff();
}
void _SaveTrayAndDesktop(void)
{
_SaveTray();
if (v_hwndDesktop)
SendMessage(v_hwndDesktop, DTM_SAVESTATE, 0, 0);
}
void SlideStep(HWND hwnd, const RECT *prcMonitor, const RECT *prcOld, const RECT *prcNew)
{
SIZE sizeOld = {prcOld->right - prcOld->left, prcOld->bottom - prcOld->top};
SIZE sizeNew = {prcNew->right - prcNew->left, prcNew->bottom - prcNew->top};
BOOL fClipFirst = FALSE;
UINT flags;
DAD_ShowDragImage(FALSE); // Make sure this is off - client function must turn back on!!!
if (prcMonitor)
{
RECT rcClip, rcClipSafe, rcClipTest;
Tray_CalcClipCoords(&rcClip, prcMonitor, prcNew);
rcClipTest = rcClip;
OffsetRect(&rcClipTest, prcOld->left, prcOld->top);
IntersectRect(&rcClipSafe, &rcClipTest, prcMonitor);
fClipFirst = EqualRect(&rcClipTest, &rcClipSafe);
if (fClipFirst)
Tray_ClipInternal(&rcClip);
}
flags = SWP_NOZORDER|SWP_NOACTIVATE;
if ((sizeOld.cx == sizeNew.cx) && (sizeOld.cy == sizeNew.cy))
flags |= SWP_NOSIZE;
SetWindowPos(hwnd, NULL, prcNew->left, prcNew->top, sizeNew.cx, sizeNew.cy, flags);
if (prcMonitor && !fClipFirst)
{
RECT rcClip;
Tray_CalcClipCoords(&rcClip, prcMonitor, prcNew);
Tray_ClipInternal(&rcClip);
}
}
void Tray_SlideWindow(HWND hwnd, RECT *prc, BOOL fShow)
{
RECT rcLast;
RECT rcMonitor;
const RECT *prcMonitor;
int dt;
BOOL fAnimate;
if (!IsWindowVisible(hwnd))
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw window is hidden, just moving"));
MoveWindow(v_hwndTray, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc), FALSE);
return;
}
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw -BEGIN-"));
if (GetSystemMetrics(SM_CMONITORS) > 1)
{
Tray_GetStuckDisplayRect(g_ts.uStuckPlace, &rcMonitor);
prcMonitor = &rcMonitor;
}
else
prcMonitor = NULL;
GetWindowRect(hwnd, &rcLast);
dt = fShow? g_dtSlideShow : g_dtSlideHide;
// See if we can use animation effects.
SystemParametersInfo(SPI_GETMENUANIMATION, 0, &fAnimate, 0);
if (g_fDragFullWindows && fAnimate && (dt > 0))
{
RECT rcOld, rcNew, rcMove;
int dx, dy, t, t2, t0, priority;
HANDLE me;
rcOld = rcLast;
rcNew = *prc;
dx = ((rcNew.left + rcNew.right) - (rcOld.left + rcOld.right)) / 2;
dy = ((rcNew.top + rcNew.bottom) - (rcOld.top + rcOld.bottom)) / 2;
ASSERT(dx == 0 || dy == 0);
me = GetCurrentThread();
priority = GetThreadPriority(me);
SetThreadPriority(me, THREAD_PRIORITY_HIGHEST);
t2 = t0 = GetTickCount();
rcMove = rcOld;
while ((t = GetTickCount()) < (t0 + dt))
{
int dtdiff;
LRESULT lres;
if (t != t2)
{
rcMove.right -= rcMove.left;
rcMove.left = rcOld.left + (dx) * (t - t0) / dt;
rcMove.right += rcMove.left;
rcMove.bottom -= rcMove.top;
rcMove.top = rcOld.top + (dy) * (t - t0) / dt;
rcMove.bottom += rcMove.top;
SlideStep(hwnd, prcMonitor, &rcLast, &rcMove);
if (fShow)
UpdateWindow(hwnd);
rcLast = rcMove;
t2 = t;
}
// don't draw frames faster than user can see, e.g. 20ms
#define ONEFRAME 20
dtdiff = GetTickCount();
if ((dtdiff - t) < ONEFRAME)
Sleep(ONEFRAME - (dtdiff - t));
// try to give desktop a chance to update
// only do it on hide because desktop doesn't need to paint on taskbar show
if (!fShow)
SendMessageTimeout(v_hwndDesktop, DTM_UPDATENOW, 0, 0, SMTO_ABORTIFHUNG, 50, &lres);
}
SetThreadPriority(me, priority);
}
SlideStep(hwnd, prcMonitor, &rcLast, prc);
if (fShow)
UpdateWindow(hwnd);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw --END--"));
}
void Tray_UnhideNow()
{
if (g_ts.uModalMode == MM_SHUTDOWN)
return;
g_ts.fSelfSizing = TRUE;
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
Tray_SlideWindow(v_hwndTray, &g_ts.arStuckRects[g_ts.uStuckPlace], g_dtSlideShow);
DAD_ShowDragImage(TRUE); // restore the lock state.
g_ts.fSelfSizing = FALSE;
ClockCtl_HandleTrayHide(FALSE);
}
void Tray_ComputeHiddenRect(LPRECT prc, UINT uStuck)
{
int dwh;
HMONITOR hMon;
RECT rcMon;
hMon = MonitorFromRect(prc, MONITOR_DEFAULTTONULL);
if (!hMon)
return;
GetMonitorRect(hMon, &rcMon);
if (STUCK_HORIZONTAL(uStuck))
dwh = prc->bottom - prc->top;
else
dwh = prc->right - prc->left;
switch (uStuck)
{
case STICK_LEFT:
prc->right = rcMon.left + (g_cxFrame / 2);
prc->left = prc->right - dwh;
break;
case STICK_RIGHT:
prc->left = rcMon.right - (g_cxFrame / 2);
prc->right = prc->left + dwh;
break;
case STICK_TOP:
prc->bottom = rcMon.top + (g_cyFrame / 2);
prc->top = prc->bottom - dwh;
break;
case STICK_BOTTOM:
prc->top = rcMon.bottom - (g_cyFrame / 2);
prc->bottom = prc->top + dwh;
break;
}
}
UINT Tray_GetDockedRect(LPRECT prc, BOOL fMoving)
{
UINT uPos;
if (fMoving && (g_ts.uMoveStuckPlace != (UINT)-1))
uPos = g_ts.uMoveStuckPlace;
else
uPos = g_ts.uStuckPlace;
*prc = g_ts.arStuckRects[uPos];
if ((g_ts.uAutoHide & (AH_ON | AH_HIDING)) == (AH_ON | AH_HIDING))
{
Tray_ComputeHiddenRect(prc, uPos);
}
return uPos;
}
void Tray_CalcClipCoords(RECT *prcClip, const RECT *prcMonitor, const RECT *prcNew)
{
RECT rcMonitor;
RECT rcWindow;
if (!prcMonitor)
{
Tray_GetStuckDisplayRect(g_ts.uStuckPlace, &rcMonitor);
prcMonitor = &rcMonitor;
}
if (!prcNew)
{
GetWindowRect(v_hwndTray, &rcWindow);
prcNew = &rcWindow;
}
IntersectRect(prcClip, prcMonitor, prcNew);
OffsetRect(prcClip, -prcNew->left, -prcNew->top);
}
void Tray_ClipInternal(const RECT *prcClip)
{
HRGN hrgnClip;
// don't worry about clipping if there's only one monitor
if (GetSystemMetrics(SM_CMONITORS) <= 1)
prcClip = NULL;
if (prcClip)
{
g_ts.fMonitorClipped = TRUE;
hrgnClip = CreateRectRgnIndirect(prcClip);
}
else
{
// SetWindowRgn is expensive, skip ones that are NOPs
if (!g_ts.fMonitorClipped)
return;
g_ts.fMonitorClipped = FALSE;
hrgnClip = NULL;
}
SetWindowRgn(v_hwndTray, hrgnClip, TRUE);
}
void Tray_ClipWindow(BOOL fClipState)
{
RECT rcClip;
RECT *prcClip;
if (g_ts.fSelfSizing || g_ts.fSysSizing)
{
TraceMsg(TF_WARNING, "Tray_ClipWindow: g_ts.fSelfSizing %x, g_ts.fSysSizing %x", g_ts.fSelfSizing, g_ts.fSysSizing);
return;
}
if (GetSystemMetrics(SM_CMONITORS) <= 1)
fClipState = FALSE;
if (fClipState)
{
prcClip = &rcClip;
Tray_CalcClipCoords(prcClip, NULL, NULL);
}
else
prcClip = NULL;
Tray_ClipInternal(prcClip);
}
void Tray_Hide()
{
RECT rcNew;
// if we're in shutdown or if we're on boot up
// don't hide
if (g_ts.uModalMode == MM_SHUTDOWN)
{
TraceMsg(TF_TRAY, "e.th: suppress hide (shutdown || Notify)");
return;
}
KillTimer(v_hwndTray, IDT_AUTOHIDE);
g_ts.fSelfSizing = TRUE;
// update the flags here to prevent race conditions
g_ts.uAutoHide = AH_ON | AH_HIDING;
Tray_GetDockedRect(&rcNew, FALSE);
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
Tray_SlideWindow(v_hwndTray, &rcNew, g_dtSlideHide);
DAD_ShowDragImage(FALSE); // Another thread could have locked while we were gone
DAD_ShowDragImage(TRUE); // restore the lock state.
ClockCtl_HandleTrayHide(TRUE);
g_ts.fSelfSizing = FALSE;
}
void Tray_AutoHideCollision()
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_ahc COLLISION! (posting UI request)"));
PostMessage(v_hwndTray, TM_WARNNOAUTOHIDE, ((g_ts.uAutoHide & AH_ON) != 0), 0L);
}
LONG Tray_SetAutoHideState(BOOL fAutoHide)
{
// make sure we have something to do
if ((fAutoHide != 0) == ((g_ts.uAutoHide & AH_ON) != 0))
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sahs nothing to do"));
return MAKELONG(FALSE, TRUE);
}
// make sure we can do it
if (!IAppBarSetAutoHideBar(v_hwndTray, fAutoHide, g_ts.uStuckPlace))
{
Tray_AutoHideCollision();
return MAKELONG(FALSE, FALSE);
}
// do it
if (fAutoHide)
{
Tray_Hide();
#ifdef DEBUG
// Tray_Hide updates the flags for us (sanity)
if (!(g_ts.uAutoHide & AH_ON))
{
TraceMsg(DM_WARNING, "e.sahs: !AH_ON"); // ok to fail on boot/shutdown
}
#endif
}
else
{
g_ts.uAutoHide = 0;
KillTimer(v_hwndTray, IDT_AUTOHIDE);
Tray_UnhideNow();
}
// brag about it
Tray_StuckTrayChange();
return MAKELONG(TRUE, TRUE);
}
void Tray_HandleEnterMenuLoop()
{
// kill the timer when we're in the menu loop so that we don't
// pop done while browsing the menus.
if (g_ts.uAutoHide & AH_ON)
{
KillTimer(v_hwndTray, IDT_AUTOHIDE);
}
}
void Tray_SetAutoHideTimer()
{
if (g_ts.uAutoHide & AH_ON)
{
SetTimer(v_hwndTray, IDT_AUTOHIDE, 500, NULL);
}
}
__inline void Tray_HandleExitMenuLoop()
{
// when we leave the menu stuff, start checking again.
Tray_SetAutoHideTimer();
}
void Tray_Unhide()
{
// handle autohide
if ((g_ts.uAutoHide & AH_ON) && (g_ts.uAutoHide & AH_HIDING))
{
Tray_UnhideNow();
g_ts.uAutoHide &= ~AH_HIDING;
Tray_SetAutoHideTimer();
if (g_ts.fShouldResize)
{
ASSERT(0);
ASSERT(!(g_ts.uAutoHide & AH_HIDING));
Tray_SizeWindows();
g_ts.fShouldResize = FALSE;
}
}
}
void TraySetUnhideTimer(LONG x, LONG y)
{
// handle autohide
if ((g_ts.uAutoHide & AH_ON) && (g_ts.uAutoHide & AH_HIDING))
{
LONG dx = x-g_ts.ptLastHittest.x;
LONG dy = y-g_ts.ptLastHittest.y;
LONG rr = dx*dx + dy*dy;
LONG dd = GetSystemMetrics(SM_CXDOUBLECLK) * GetSystemMetrics(SM_CYDOUBLECLK);
if (rr > dd)
{
SetTimer(v_hwndTray, IDT_AUTOUNHIDE, 50, NULL);
g_ts.ptLastHittest.x = x;
g_ts.ptLastHittest.y = y;
}
}
}
void StartButton_Reset()
{
HBITMAP hbmOld, hbmNew;
DebugMsg(DM_IANELHK, TEXT("c.sb_rs: Recreating start button."));
hbmOld = (HBITMAP)SendMessage(g_ts.hwndStart, BM_GETIMAGE, IMAGE_BITMAP, 0 );
if (hbmOld)
{
hbmNew = CreateStartBitmap(g_ts.hwndMain);
if (hbmNew)
{
SendMessage(g_ts.hwndStart, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmNew);
DeleteObject(hbmOld);
}
}
g_ts.sizeStart.cy = 0;
}
void Tray_OnNewSystemSizes()
{
TraceMsg(TF_TRAY, "Handling win ini change.");
StartButton_Reset();
Tray_VerifySize(TRUE);
}
//** CheckWindowPositions -- flag which windows actually changed
// ENTRY/EXIT
// g_ts.pPositions->hdsaWP[*]->fRestore modified
// NOTES
// in order to correctly implement 'Undo Minimize-all(Cascade/Tile)',
// we need to tell which windows were changed by the 'Do' operation.
// (nt5:183421: we used to restore *every* top-level window).
int CALLBACK CWPCallback(void *pItem, void *pData)
{
HWNDANDPLACEMENT *pI2 = (HWNDANDPLACEMENT *)pItem;
WINDOWPLACEMENT wp;
wp.length = SIZEOF(wp);
pI2->fRestore = TRUE;
if (GetWindowPlacement(pI2->hwnd, &wp)) {
if (memcmp(&pI2->wp, &wp, SIZEOF(wp)) == 0)
pI2->fRestore = FALSE;
}
TraceMsg(TF_TRAY, "cwp: (hwnd=0x%x) fRestore=%d", pI2->hwnd, pI2->fRestore);
return 1; // 1:continue enum
}
void CheckWindowPositions()
{
ENTERCRITICAL; // i think this is needed...
if (g_ts.pPositions) {
if (g_ts.pPositions->hdsaWP) {
DSA_EnumCallback(g_ts.pPositions->hdsaWP, CWPCallback, NULL);
}
}
LEAVECRITICAL;
}
void Tray_HandleTimer(WPARAM wTimerID)
{
if (wTimerID == IDT_HANDLEDELAYBOOTSTUFF)
{
KillTimer(v_hwndTray, IDT_HANDLEDELAYBOOTSTUFF);
PostMessage(v_hwndTray, TM_HANDLEDELAYBOOTSTUFF, 0, 0);
return;
}
if (wTimerID == IDT_STARTMENU)
{
SetForegroundWindow(v_hwndTray);
KillTimer(v_hwndTray, wTimerID);
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
SendMessage(g_ts.hwndStart, BM_SETSTATE, TRUE, 0 );
UpdateWindow(g_ts.hwndStart);
DAD_ShowDragImage(TRUE); // restore the lock state.
}
if (wTimerID == IDT_SAVESETTINGS)
{
KillTimer(v_hwndTray, IDT_SAVESETTINGS);
_SaveTray();
}
if (wTimerID == IDT_ENABLEUNDO)
{
KillTimer(v_hwndTray, IDT_SAVESETTINGS);
CheckWindowPositions();
g_ts.fUndoEnabled = TRUE;
}
if (g_ts.uAutoHide & AH_ON)
{
if (g_ts.fSysSizing)
return;
switch (wTimerID)
{
POINT pt;
RECT rc;
case IDT_AUTOHIDE:
{
// Don't hide if we're already hiding or if any apps are flashing.
if (!(g_ts.uAutoHide & AH_HIDING) && !g_ts.fFlashing && !g_ts.fBalloonUp)
{
GetCursorPos(&pt);// Get the cursor position.
// Get the tray rect and inflate it a bit.
rc = g_ts.arStuckRects[g_ts.uStuckPlace];
InflateRect(&rc, g_cxEdge * 4, g_cyEdge*4);
// Don't hide if cursor is within inflated tray rect.
if (!PtInRect(&rc, pt))
{
// Don't hide if the tray is active
if (!Tray_IsActive())
{
// Don't hide if the view has a system menu up.
if (!SendMessage(g_ts.hwndView, TM_SYSMENUCOUNT, 0, 0L))
{
// Phew! We made it. Hide the tray.
Tray_Hide();
}
}
}
}
break;
}
case IDT_AUTOUNHIDE:
{
KillTimer(v_hwndTray, wTimerID);
g_ts.ptLastHittest.x = -0x0fff;
g_ts.ptLastHittest.y = -0x0fff;
GetWindowRect(v_hwndTray, &rc);
if (g_ts.uAutoHide & AH_HIDING)
{
GetCursorPos(&pt);
if (PtInRect(&rc, pt))
Tray_Unhide();
}
break;
}
}
}
}
// Search for the first sub menu of the given menu, who's first item's ID
// is id. Returns NULL, if nothing is found.
HMENU Menu_FindSubMenuByFirstID(HMENU hmenu, UINT id)
{
int cMax, c;
HMENU hmenuSub;
MENUITEMINFO mii;
ASSERT(hmenu);
// REVIEW There's a bug in the thunks such that GMIC() returns
// 0x0000ffff for an invalid menu which gets us confused.
if (!IsMenu(hmenu))
return NULL;
// Search all items.
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_ID;
cMax = GetMenuItemCount(hmenu);
for (c=0; c < cMax; c++)
{
// Is this item a submenu?
hmenuSub = GetSubMenu(hmenu, c);
if (hmenuSub && GetMenuItemInfo(hmenuSub, 0, TRUE, &mii))
{
if (mii.wID == id)
{
// Found it!
return hmenuSub;
}
}
}
return NULL;
}
BOOL ExecItemByPidls(HWND hwnd, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
BOOL fRes = FALSE;
if (pidlFolder && pidlItem)
{
IShellFolder *psf = BindToFolder(pidlFolder);
if (psf)
{
fRes = SUCCEEDED(SHInvokeDefaultCommand(hwnd, psf, pidlItem));
}
else
{
TCHAR szPath[MAX_PATH];
SHGetPathFromIDList(pidlFolder, szPath);
ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE( IDS_CANTFINDSPECIALDIR), NULL, MB_ICONEXCLAMATION, szPath);
}
}
return fRes;
}
LRESULT Tray_HandleDestroy()
{
#ifdef OLDTSTARTMENU
HMENU hmenuSub;
#endif
MINIMIZEDMETRICS mm;
TraceMsg(DM_SHUTDOWN, "Tray_HD: enter");
mm.cbSize = SIZEOF(mm);
SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
mm.iArrange &= ~ARW_HIDE;
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
PrintNotify_Exit();
CStartDropTarget_Revoke();
StartMenu_Destroy();
ENTERCRITICAL;
if (g_ts.pPositions)
{
DestroySavedWindowPositions(g_ts.pPositions);
g_ts.pPositions = NULL;
}
LEAVECRITICAL;
if (g_ts.hBIOS!=INVALID_HANDLE_VALUE)
CloseHandle(g_ts.hBIOS);
Tray_UnregisterGlobalHotkeys();
if (g_ts.ptbs) {
g_ts.ptbs->lpVtbl->Release(g_ts.ptbs);
g_ts.ptbs = NULL;
}
// Cleanup notify handlers.
UnregisterNotify(g_ts.uProgNotify);
UnregisterNotify(g_ts.uFastNotify);
UnregisterNotify(g_ts.uRecentNotify);
UnregisterNotify(g_ts.uFavoritesNotify);
UnregisterNotify(g_ts.uDesktopNotify);
UnregisterNotify(g_ts.uCommonDesktopNotify);
UnregisterNotify(g_ts.uCommonProgNotify);
UnregisterNotify(g_ts.uCommonFastNotify);
if (g_ts.pcmFind)
g_ts.pcmFind->lpVtbl->Release(g_ts.pcmFind);
Tray_DestroyShellView();
if (g_ts.hwndTrayTips) {
DestroyWindow(g_ts.hwndTrayTips);
g_ts.hwndTrayTips = NULL;
}
DestroyStartButtonBalloon();
// REVIEW
PostQuitMessage(0);
if (g_ts.hbmpStartBkg)
DeleteBitmap(g_ts.hbmpStartBkg);
Tray_HandleShellServiceObject(SSOCMDID_CLOSE, 0);
#ifdef WINNT
if ( g_hShellReadyEvent )
{
CloseHandle( g_hShellReadyEvent );
g_hShellReadyEvent = NULL;
}
#endif
v_hwndTray = NULL;
g_ts.hwndStart = NULL;
TraceMsg(DM_SHUTDOWN, "Tray_HD: leave");
return 0;
}
void Tray_SetFocus(HWND hwnd)
{
UnkUIActivateIO(g_ts.ptbs, FALSE, NULL);
SetFocus(hwnd);
}
#define TRIEDTOOMANYTIMES 100
void Tray_ActAsSwitcher()
{
if (g_ts.uModalMode) {
if (g_ts.uModalMode != MM_SHUTDOWN) {
SwitchToThisWindow(GetLastActivePopup(g_ts.hwndMain), TRUE);
}
MessageBeep(0);
} else {
HWND hwndForeground;
HWND hwndActive;
#if (defined(WINNT) || defined(DEBUG))
static int s_iRecurse = 0;
s_iRecurse++;
#endif
#ifdef DEBUG
ASSERT(s_iRecurse < TRIEDTOOMANYTIMES);
TraceMsg(TF_TRAY, "s_iRecurse = %d", s_iRecurse);
#endif
hwndForeground = GetForegroundWindow();
hwndActive = GetActiveWindow();
// only do the button once we're the foreground dude.
if ((hwndForeground == v_hwndTray) && (hwndActive == v_hwndTray))
{
// This code path causes the start button to do something because
// of the keyboard. So reflect that with the focus rect.
SendMessage(g_ts.hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
if (SendMessage(g_ts.hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
{
if (g_ts._pmpStartMenu)
g_ts._pmpStartMenu->lpVtbl->OnSelect(g_ts._pmpStartMenu, MPOS_FULLCANCEL);
_ForceStartButtonUp();
}
else
{
// This pushes the start button and causes the start menu to popup.
SendMessage(GetDlgItem(v_hwndTray, IDC_START), BM_SETSTATE, TRUE, 0);
}
#if (defined(WINNT) || defined(DEBUG))
s_iRecurse = 0;
#endif
} else {
#ifdef WINNT
// On NT we don't want to loop endlessly trying to become
// foreground. With NT's new SetForegroundWindow rules, it would
// be pointless to try and hopefully we won't need to anyhow.
// Randomly, I picked a quarter as many times as the debug squirty would indicate
// as the number of times to try on NT.
// Hopefully that is enough on most machines.
if (s_iRecurse > (TRIEDTOOMANYTIMES / 4)) {
s_iRecurse = 0;
return;
}
#endif
// until then, try to come forward.
Tray_HandleFullScreenApp(NULL);
if (hwndForeground == v_hwndDesktop) {
Tray_SetFocus(g_ts.hwndStart);
if (GetFocus() != g_ts.hwndStart)
return;
}
SwitchToThisWindow(g_ts.hwndMain, TRUE);
SetForegroundWindow(v_hwndTray);
Sleep(20); // give some time for other async activation messages to get posted
PostMessage(v_hwndTray, TM_ACTASTASKSW, 0, 0);
}
}
}
void Tray_OnWinIniChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
Cabinet_InitGlobalMetrics(0, (LPTSTR)NULL);
// Reset the programs menu.
// REVIEW IANEL - We should only need to listen to the SPI_SETNONCLIENT stuff
// but deskcpl doesn't send one.
if (wParam == SPI_SETNONCLIENTMETRICS || (!wParam && (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("WindowMetrics")) == 0))))
{
#ifdef DEBUG
if (wParam == SPI_SETNONCLIENTMETRICS)
TraceMsg(TF_TRAY, "c.t_owic: Non-client metrics (probably) changed.");
else
TraceMsg(TF_TRAY, "c.t_owic: Window metrics changed.");
#endif
Tray_OnNewSystemSizes();
}
// Handle old extensions.
if (!lParam || (lParam && (lstrcmpi((LPTSTR)lParam, TEXT("Extensions")) == 0)))
{
TraceMsg(TF_TRAY, "t_owic: Extensions section change.");
CheckWinIniForAssocs();
}
// Get the printer thread to re-do the icon.
if (g_ts.htPrinterPoll && g_ts.idPrinterPoll)
{
PostThreadMessage(g_ts.idPrinterPoll, WM_USER+SHCNE_UPDATEITEM, 0, 0);
}
// Tell shell32 to refresh its cache
SHSettingsChanged(wParam, lParam);
}
HWND HotkeyList_HotkeyInUse(WORD wHK)
{
HWND hwnd;
LRESULT lrHKInUse = 0;
int nMod;
WORD wHKNew;
#ifdef DEBUG
TCHAR sz[MAX_PATH];
#endif
// Map the modifiers back.
nMod = 0;
if (HIBYTE(wHK) & MOD_SHIFT)
nMod |= HOTKEYF_SHIFT;
if (HIBYTE(wHK) & MOD_CONTROL)
nMod |= HOTKEYF_CONTROL;
if (HIBYTE(wHK) & MOD_ALT)
nMod |= HOTKEYF_ALT;
wHKNew = (WORD)((nMod*256)+LOBYTE(wHK));
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: Checking for %x"), wHKNew);
hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
while (hwnd)
{
SendMessageTimeout(hwnd, WM_GETHOTKEY, 0, 0, SMTO_ABORTIFHUNG| SMTO_BLOCK, 3000, &lrHKInUse);
if (wHKNew == (WORD)lrHKInUse)
{
#ifdef DEBUG
GetWindowText(hwnd, sz, ARRAYSIZE(sz));
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
#endif
return hwnd;
}
#ifdef DEBUG
else if (lrHKInUse)
{
GetWindowText(hwnd, sz, ARRAYSIZE(sz));
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
}
#endif
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
}
return NULL;
}
void HotkeyList_HandleHotkey(int nID)
{
PHOTKEYITEM phki;
BOOL fRes;
HWND hwnd;
TraceMsg(TF_TRAY, "c.hkl_hh: Handling hotkey (%d).", nID);
// Find it in the list.
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
phki = DSA_GetItemPtr(g_ts.hdsaHKI, nID);
if (phki && phki->wGHotkey)
{
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey listed.");
// Are global hotkeys enabled?
if (!g_ts.fGlobalHotkeyDisable)
{
// Yep.
hwnd = HotkeyList_HotkeyInUse(phki->wGHotkey);
// Make sure this hotkey isn't already in use by someone.
if (hwnd)
{
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is already in use.");
// Activate it.
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
}
else
{
DECLAREWAITCURSOR;
// Exec the item.
SetWaitCursor();
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is not in use, execing item.");
ASSERT(phki->pidlFolder && phki->pidlItem);
fRes = ExecItemByPidls(g_ts.hwndMain, phki->pidlFolder, phki->pidlItem);
ResetWaitCursor();
#ifdef DEBUG
if (!fRes)
{
DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Can't exec command ."));
}
#endif
}
}
else
{
DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Global hotkeys have been disabled."));
}
}
else
{
DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Hotkey not listed."));
}
}
LRESULT Tray_UnregisterHotkey(HWND hwnd, int i)
{
TraceMsg(TF_TRAY, "c.t_uh: Unregistering hotkey (%d).", i);
if (!UnregisterHotKey(hwnd, i))
{
DebugMsg(DM_ERROR, TEXT("c.t_rh: Unable to unregister hotkey %d."), i);
}
return TRUE;
}
// Add hotkey to the shell's list of global hotkeys.
LRESULT Tray_ShortcutRegisterHotkey(HWND hwnd, WORD wHotkey, ATOM atom)
{
int i;
LPITEMIDLIST pidl;
TCHAR szPath[MAX_PATH];
ASSERT(atom);
if (GlobalGetAtomName(atom, szPath, MAX_PATH))
{
TraceMsg(TF_TRAY, "c.t_srh: Hotkey %d for %s", wHotkey, szPath);
pidl = ILCreateFromPath(szPath);
if (pidl)
{
i = HotkeyList_AddCached(MapHotkeyToGlobalHotkey(wHotkey), pidl);
if (i != -1)
{
Tray_RegisterHotkey(v_hwndTray, i);
}
}
return TRUE;
}
else
{
return FALSE;
}
}
// Remove hotkey from shell's list.
LRESULT Tray_ShortcutUnregisterHotkey(HWND hwnd, WORD wHotkey)
{
int i;
// DebugMsg(DM_TRACE, "c.t_suh: Hotkey %d", wHotkey);
i = HotkeyList_Remove(wHotkey);
if (i == -1)
i = HotkeyList_RemoveCached(MapHotkeyToGlobalHotkey(wHotkey));
if (i != -1)
Tray_UnregisterHotkey(hwnd, i);
return TRUE;
}
LRESULT Tray_RegisterHotkey(HWND hwnd, int i)
{
PHOTKEYITEM phki;
WORD wGHotkey;
int iCached;
ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
TraceMsg(TF_TRAY, "c.t_rh: Registering hotkey (%d).", i);
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
ASSERT(phki);
if (phki)
{
wGHotkey = phki->wGHotkey;
if (wGHotkey)
{
// Is the hotkey available?
if (RegisterHotKey(hwnd, i, HIBYTE(wGHotkey), LOBYTE(wGHotkey)))
{
// Yes.
return TRUE;
}
else
{
// Delete any cached items that might be using this hotkey.
iCached = HotkeyList_RemoveCached(wGHotkey);
ASSERT(iCached != i);
if (iCached != -1)
{
// Free up the hotkey for us.
Tray_UnregisterHotkey(hwnd, iCached);
// Yep, nuked the cached item. Try again.
if (RegisterHotKey(hwnd, i, HIBYTE(wGHotkey), LOBYTE( wGHotkey)))
{
return TRUE;
}
}
}
// Can't set hotkey for this item.
DebugMsg(DM_ERROR, TEXT("c.t_rh: Unable to register hotkey %d."), i);
// Null out this item.
phki->wGHotkey = 0;
phki->pidlFolder = NULL;
phki->pidlItem = NULL;
}
else
{
DebugMsg(DM_ERROR, TEXT("c.t_rh: Hotkey item is invalid."));
}
}
return FALSE;
}
void AppBarGetTaskBarPos(PTRAYAPPBARDATA ptabd)
{
APPBARDATA *pabd;
pabd = (APPBARDATA*)SHLockShared(ptabd->hSharedABD, ptabd->dwProcId);
if (pabd)
{
pabd->rc = g_ts.arStuckRects[g_ts.uStuckPlace];
pabd->uEdge = g_ts.uStuckPlace; // compat: new to ie4
SHUnlockShared(pabd);
}
}
void NukeAppBar(int i)
{
LocalFree(DPA_GetPtr(g_ts.hdpaAppBars, i));
DPA_DeletePtr(g_ts.hdpaAppBars, i);
}
void AppBarRemove(PTRAYAPPBARDATA ptabd)
{
int i;
if (!g_ts.hdpaAppBars)
return;
i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--)
{
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
if (ptabd->abd.hWnd == pab->hwnd)
{
RECT rcNuke = pab->rc;
NukeAppBar(i);
StuckAppChange(ptabd->abd.hWnd, &rcNuke, NULL, FALSE);
}
}
}
PAPPBAR FindAppBar(HWND hwnd)
{
if (g_ts.hdpaAppBars)
{
int i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--)
{
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
if (hwnd == pab->hwnd)
return pab;
}
}
return NULL;
}
void AppBarNotifyAll(HMONITOR hmon, UINT uMsg, HWND hwndExclude, LPARAM lParam)
{
int i;
if (!g_ts.hdpaAppBars)
return;
i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--)
{
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
// We need to check pab here as an appbar can delete other
// appbars on the callback.
if (pab && (hwndExclude != pab->hwnd))
{
if (!IsWindow(pab->hwnd))
{
NukeAppBar(i);
continue;
}
// if a monitor was specified only tell appbars on that display
if (hmon && (hmon != MonitorFromWindow(pab->hwnd, MONITOR_DEFAULTTONULL)))
{
continue;
}
SendMessage(pab->hwnd, pab->uCallbackMessage, uMsg, lParam);
}
}
}
BOOL AppBarNew(PTRAYAPPBARDATA ptabd)
{
PAPPBAR pab;
if (!g_ts.hdpaAppBars) {
g_ts.hdpaAppBars = DPA_Create(4);
if (!g_ts.hdpaAppBars)
return FALSE;
} else if (FindAppBar(ptabd->abd.hWnd)) {
// already have this hwnd
return FALSE;
}
pab = (PAPPBAR)LocalAlloc(LPTR, SIZEOF(APPBAR));
if (!pab)
return FALSE;
pab->hwnd = ptabd->abd.hWnd;
pab->uCallbackMessage = ptabd->abd.uCallbackMessage;
pab->uEdge = (UINT)-1;
return ( DPA_AppendPtr(g_ts.hdpaAppBars, pab) != -1);
}
UINT CDeskTray_AppBarGetState(IDeskTray* pdt)
{
return (g_ts.uAutoHide ? ABS_AUTOHIDE : 0) | (g_ts.fAlwaysOnTop ? ABS_ALWAYSONTOP : 0);
}
BOOL AppBarOutsideOf(PAPPBAR pabReq, PAPPBAR pab)
{
if (pabReq->uEdge == pab->uEdge) {
switch (pab->uEdge) {
case ABE_RIGHT:
return (pab->rc.right >= pabReq->rc.right);
case ABE_BOTTOM:
return (pab->rc.bottom >= pabReq->rc.bottom);
case ABE_TOP:
return (pab->rc.top <= pabReq->rc.top);
case ABE_LEFT:
return (pab->rc.left <= pabReq->rc.left);
}
}
return FALSE;
}
void AppBarQueryPos(PTRAYAPPBARDATA ptabd)
{
int i;
PAPPBAR pabReq = FindAppBar(ptabd->abd.hWnd);
if (pabReq)
{
APPBARDATA *pabd;
pabd = (APPBARDATA*)SHLockShared(ptabd->hSharedABD, ptabd->dwProcId);
if (pabd)
{
HMONITOR hmon;
pabd->rc = ptabd->abd.rc;
// default to the primary display for this call because old appbars
// sometimes pass a huge rect and let us pare it down. if they do
// something like that they don't support multiple displays anyway
// so just put them on the primary display...
hmon = MonitorFromRect(&pabd->rc, MONITOR_DEFAULTTOPRIMARY);
// always subtract off the tray if it's on the same display
if (!g_ts.uAutoHide && (hmon == g_ts.hmonStuck))
{
APPBAR ab;
ab.uEdge = Tray_GetDockedRect(&ab.rc, FALSE);
AppBarSubtractRect(&ab, &pabd->rc);
}
i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--)
{
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
// give top and bottom preference
// ||
// if we're not changing edges,
// subtract anything currently on the outside of us
// ||
// if we are changing sides,
// subtract off everything on the new side.
// of course ignore appbars which are not on the same display...
if ((((pabReq->hwnd != pab->hwnd) && STUCK_HORIZONTAL(pab->uEdge) && !STUCK_HORIZONTAL(ptabd->abd.uEdge)) ||
((pabReq->hwnd != pab->hwnd) && (pabReq->uEdge == ptabd->abd.uEdge) && AppBarOutsideOf(pabReq, pab)) ||
((pabReq->hwnd != pab->hwnd) && (pabReq->uEdge != ptabd->abd.uEdge) && (pab->uEdge == ptabd->abd.uEdge))) &&
(hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL)))
{
AppBarSubtractRect(pab, &pabd->rc);
}
}
SHUnlockShared(pabd);
}
}
}
void AppBarSetPos(PTRAYAPPBARDATA ptabd)
{
PAPPBAR pab = FindAppBar(ptabd->abd.hWnd);
if (pab)
{
RECT rcOld;
APPBARDATA *pabd;
BOOL fChanged = FALSE;
AppBarQueryPos(ptabd);
pabd = (APPBARDATA*)SHLockShared(ptabd->hSharedABD, ptabd->dwProcId);
if (pabd)
{
if (!EqualRect(&pab->rc, &pabd->rc)) {
rcOld = pab->rc;
pab->rc = pabd->rc;
pab->uEdge = ptabd->abd.uEdge;
fChanged = TRUE;
}
SHUnlockShared(pabd);
}
if (fChanged)
StuckAppChange(ptabd->abd.hWnd, &rcOld, &pab->rc, FALSE);
}
}
// BUGBUG: need to get rid of this array-based implementation to allow autohide
// appbars on secondary display (or a/h tray on 2nd with a/h appbar on primary)
// change it to an AppBarFindAutoHideBar that keeps flags on the appbardata...
HWND AppBarGetAutoHideBar(UINT uEdge)
{
if (uEdge >= ABE_MAX)
return FALSE;
else {
HWND hwndAutoHide = g_hwndAutoHide[uEdge];
if (!IsWindow(hwndAutoHide)) {
g_hwndAutoHide[uEdge] = NULL;
}
return g_hwndAutoHide[uEdge];
}
}
BOOL IAppBarSetAutoHideBar(HWND hwnd, BOOL fAutoHide, UINT uEdge)
{
HWND hwndAutoHide = g_hwndAutoHide[uEdge];
if (!IsWindow(hwndAutoHide))
{
g_hwndAutoHide[uEdge] = NULL;
}
if (fAutoHide)
{
// register
if (!g_hwndAutoHide[uEdge])
{
g_hwndAutoHide[uEdge] = hwnd;
}
return g_hwndAutoHide[uEdge] == hwnd;
}
else
{
// unregister
if (g_hwndAutoHide[uEdge] == hwnd)
{
g_hwndAutoHide[uEdge] = NULL;
}
return TRUE;
}
}
BOOL AppBarSetAutoHideBar(PTRAYAPPBARDATA ptabd)
{
UINT uEdge = ptabd->abd.uEdge;
if (uEdge >= ABE_MAX)
return FALSE;
else {
return IAppBarSetAutoHideBar(ptabd->abd.hWnd, BOOLFROMPTR(ptabd->abd.lParam), uEdge);
}
}
void IAppBarActivationChange(HWND hwnd, UINT uEdge)
{
// BUGBUG: make this multi-monitor cool
HWND hwndAutoHide = AppBarGetAutoHideBar(uEdge);
if (hwndAutoHide && (hwndAutoHide != hwnd))
{
// the AppBar got this notification inside a SendMessage from USER
// and is now in a SendMessage to us. don't try to do a SetWindowPos
// right now...
PostMessage(v_hwndTray, TM_BRINGTOTOP, (WPARAM)hwndAutoHide, uEdge);
}
}
void AppBarActivationChange(PTRAYAPPBARDATA ptabd)
{
PAPPBAR pab = FindAppBar(ptabd->abd.hWnd);
if (pab) {
UINT i;
// if this is an autohide bar and they're claiming to be on an edge not the same as their autohide edge,
// we don't do any activation of other autohides
for (i = 0; i < ABE_MAX; i++) {
if (g_hwndAutoHide[i] == ptabd->abd.hWnd && i != pab->uEdge)
return;
}
IAppBarActivationChange(ptabd->abd.hWnd, pab->uEdge);
}
}
LRESULT TrayAppBarMessage(PCOPYDATASTRUCT pcds)
{
PTRAYAPPBARDATA ptabd = (PTRAYAPPBARDATA)pcds->lpData;
ASSERT(pcds->cbData == SIZEOF(TRAYAPPBARDATA));
ASSERT(ptabd->abd.cbSize == SIZEOF(APPBARDATA));
switch (ptabd->dwMessage) {
case ABM_NEW:
return AppBarNew(ptabd);
case ABM_REMOVE:
AppBarRemove(ptabd);
break;
case ABM_QUERYPOS:
AppBarQueryPos(ptabd);
break;
case ABM_SETPOS:
AppBarSetPos(ptabd);
break;
case ABM_GETSTATE:
return CDeskTray_AppBarGetState(NULL);
case ABM_GETTASKBARPOS:
AppBarGetTaskBarPos(ptabd);
break;
case ABM_WINDOWPOSCHANGED:
case ABM_ACTIVATE:
AppBarActivationChange(ptabd);
break;
case ABM_GETAUTOHIDEBAR:
return (LRESULT)AppBarGetAutoHideBar(ptabd->abd.uEdge);
case ABM_SETAUTOHIDEBAR:
return AppBarSetAutoHideBar(ptabd);
default:
return FALSE;
}
return TRUE;
}
// EA486701-7F92-11cf-9E05-444553540000
const GUID CLSID_HIJACKINPROC = {0xEA486701, 0x7F92, 0x11cf, 0x9E, 0x05, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00};
UINT TrayLoadInProc(PCOPYDATASTRUCT pcds)
{
IUnknown *punk;
HRESULT hres;
// Hack to allow us to kill W95 shell extensions that do reall hacky things that
// we can not support. In this case Hijack pro
if (IsEqualIID((CLSID *)pcds->lpData, &CLSID_HIJACKINPROC))
return (UINT)E_FAIL;
hres = SHCoCreateInstance(NULL, (CLSID *)pcds->lpData, NULL, &IID_IUnknown, &punk);
if (SUCCEEDED(hres))
{
punk->lpVtbl->Release(punk);
}
return (UINT)hres;
}
// Allow the trays global hotkeys to be disabled for a while.
LRESULT Tray_SetHotkeyEnable(HWND hwnd, BOOL fEnable)
{
g_ts.fGlobalHotkeyDisable = (fEnable ? FALSE : TRUE);
return TRUE;
}
BOOL IsPosInHwnd(LPARAM lParam, HWND hwnd)
{
RECT r1;
POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
GetWindowRect(hwnd, &r1);
return PtInRect(&r1, pt);
}
void Tray_HandleWindowPosChanging(LPWINDOWPOS lpwp)
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc"));
if (g_ts.uMoveStuckPlace != (UINT)-1)
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc handling pending move"));
Tray_DoneMoving(lpwp);
}
else if (g_ts.fSysSizing || !g_ts.fSelfSizing)
{
RECT rc;
if (g_ts.fSysSizing)
{
GetWindowRect(v_hwndTray, &rc);
if (!(lpwp->flags & SWP_NOMOVE))
{
rc.left = lpwp->x;
rc.top = lpwp->y;
}
if (!(lpwp->flags & SWP_NOSIZE))
{
rc.right = rc.left + lpwp->cx;
rc.bottom = rc.top + lpwp->cy;
}
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc sys sizing to rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
g_ts.uStuckPlace = Tray_RecalcStuckPos(&rc);
}
Tray_GetDockedRect(&rc, g_ts.fSysSizing);
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc using rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
lpwp->x = rc.left;
lpwp->y = rc.top;
lpwp->cx = RECTWIDTH(rc);
lpwp->cy = RECTHEIGHT(rc);
lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
}
}
void Tray_HandlePowerStatus(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL fResetDisplay = FALSE;
// always reset the display when the machine wakes up from a
// suspend. NOTE: we don't need this for a standby suspend.
// a critical resume does not generate a WM_POWERBROADCAST
// to windows for some reason, but it does generate an old WM_POWER message.
switch (uMsg)
{
case WM_POWER:
fResetDisplay = (wParam == PWR_CRITICALRESUME);
break;
#ifndef WINNT // BUGBUG - Fix this when NT gets APM
case WM_POWERBROADCAST:
switch (wParam)
{
case PBT_APMRESUMECRITICAL:
case PBT_APMRESUMESUSPEND:
fResetDisplay = TRUE;
break;
}
break;
#endif
}
if (fResetDisplay)
ChangeDisplaySettings(NULL, CDS_RESET);
}
BOOL LoadShellServiceObjects(HKEY hkeyParent, LPCTSTR szSubkey);
void Tray_HandleShellServiceObject(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case SSOCMDID_LOAD:
LoadShellServiceObjects(HKEY_LOCAL_MACHINE, REGSTR_PATH_SHELLSERVICEOBJECTDELAYED);
LoadShellServiceObjects(HKEY_CURRENT_USER, REGSTR_PATH_SHELLSERVICEOBJECTDELAYED);
break;
case SSOCMDID_OPEN:
CTExecShellServiceObjects(NULL, SSOCMDID_OPEN, 0, 0);
break;
case SSOCMDID_CLOSE:
// This can be called multiple times (because we can quit Explorer
// in a number of places) so we need to make sure we don't send
// this message multiple times so free the DSA when done.
if (g_hdsaShellServiceObjects)
{
CTExecShellServiceObjects(NULL, SSOCMDID_CLOSE, 0, CTEXECSSOF_REVERSE);
DSA_Destroy(g_hdsaShellServiceObjects);
g_hdsaShellServiceObjects=NULL;
}
break;
}
}
// Try tacking a 1, 2, 3 or whatever on to a file or
// directory name until it is unique. When on a file,
// stick it before the extension.
void MakeBetterUniqueName(LPTSTR pszPathName)
{
TCHAR szNewPath[MAX_PATH];
int i = 1;
if (PathIsDirectory(pszPathName))
{
do
{
wsprintf(szNewPath, TEXT("%s%d"), pszPathName, i++);
} while (-1 != GetFileAttributes(szNewPath));
lstrcpy(pszPathName, szNewPath);
}
else
{
TCHAR szExt[MAX_PATH];
LPTSTR pszExt;
pszExt = PathFindExtension(pszPathName);
lstrcpy(szExt, pszExt);
*pszExt = 0;
do
{
wsprintf(szNewPath, TEXT("%s%d%s"), pszPathName, i++,szExt);
} while (-1 != GetFileAttributes(szNewPath));
lstrcpy(pszPathName, szNewPath);
}
}
static BOOL_PTR CALLBACK RogueProgramFileDlgProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
TCHAR szBuffer[MAX_PATH*2];
TCHAR szBuffer2[MAX_PATH*2];
static TCHAR szBetterPath[MAX_PATH];
static TCHAR *pszPath = NULL;
switch (iMsg)
{
case WM_INITDIALOG:
pszPath = (TCHAR *)lParam;
lstrcpy(szBetterPath, pszPath);
MakeBetterUniqueName(szBetterPath);
SendDlgItemMessage(hWnd, IDC_MSG, WM_GETTEXT, (WPARAM)(MAX_PATH*2), (LPARAM)szBuffer);
wsprintf(szBuffer2, szBuffer, pszPath, szBetterPath);
SendDlgItemMessage(hWnd, IDC_MSG, WM_SETTEXT, (WPARAM)0, (LPARAM)szBuffer2);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_RENAME:
//rename and fall through
if (pszPath)
{
MoveFile(pszPath, szBetterPath);
}
EndDialog(hWnd, IDC_RENAME);
return TRUE;
case IDIGNORE:
EndDialog(hWnd, IDIGNORE);
return TRUE;
}
break;
}
return FALSE;
}
// Check to see if there are any files or folders that could interfere
// with the fact that Program Files has a space in it.
// An example would be a directory called: "C:\Program" or a file called"C:\Program.exe".
// This can prevent apps that dont quote strings in the registry or call CreateProcess with
// unquoted strings from working properly since CreateProcess wont know what the real exe is.
void Tray_CheckForRogueProgramFile()
{
TCHAR szProgramFilesPath[MAX_PATH];
TCHAR szProgramFilesShortName[MAX_PATH];
if (S_OK == SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szProgramFilesPath))
{
LPTSTR pszRoguePattern;
pszRoguePattern = StrChr(szProgramFilesPath, TEXT(' '));
if (pszRoguePattern)
{
HANDLE hFind;
WIN32_FIND_DATA wfd;
// Remember short name for folder name comparison below
*pszRoguePattern = TEXT('\0');
lstrcpy(szProgramFilesShortName, szProgramFilesPath);
// turn "C:\program files" into "C:\program.*"
lstrcpy(pszRoguePattern, TEXT(".*"));
pszRoguePattern = szProgramFilesPath;
hFind = FindFirstFile(pszRoguePattern, &wfd);
while (hFind != INVALID_HANDLE_VALUE)
{
int iRet;
TCHAR szRogueFileName[MAX_PATH];
// we found a file (eg "c:\Program.txt")
lstrcpyn(szRogueFileName, pszRoguePattern, ARRAYSIZE(szRogueFileName));
PathRemoveFileSpec(szRogueFileName);
lstrcatn(szRogueFileName, wfd.cFileName, ARRAYSIZE(szRogueFileName));
// don't worry about folders unless they are called "Program"
if (!((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && lstrcmpi(szProgramFilesShortName, szRogueFileName) != 0))
{
iRet = SHMessageBoxCheckEx(GetDesktopWindow(),
hinstCabinet,
MAKEINTRESOURCE(DLG_PROGRAMFILECONFLICT),
RogueProgramFileDlgProc,
(LPVOID)szRogueFileName,
IDIGNORE,
TEXT("RogueProgramName"));
}
if ((iRet == IDIGNORE) || !FindNextFile(hFind, &wfd))
{
// user hit ignore or we are done, so don't keep going
break;
}
}
if (hFind != INVALID_HANDLE_VALUE)
{
FindClose(hFind);
}
}
}
}
void Tray_OnWaitCursorNotify(LPNMHDR pnm)
{
g_ts.iWaitCount += (pnm->code == NM_STARTWAIT ? 1 :-1);
ASSERT(g_ts.iWaitCount >= 0);
// Don't let it go negative or we'll never get rid of it.
if (g_ts.iWaitCount < 0)
g_ts.iWaitCount = 0;
// what we really want is for user to simulate a mouse move/setcursor
SetCursor(LoadCursor(NULL, g_ts.iWaitCount ? IDC_APPSTARTING : IDC_ARROW));
}
void Tray_HandlePrivateCommand(LPARAM lParam)
{
LPSTR psz = (LPSTR) lParam; // lParam always ansi.
if (!lstrcmpiA(psz, "ToggleDesktop")) {
ToggleDesktop();
} else if (!lstrcmpiA(psz, "Explorer")) {
// Fast way to bring up explorer window on root of
// windows dir.
SHELLEXECUTEINFO shei = {0};
TCHAR szPath[MAX_PATH];
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
PathStripToRoot(szPath);
shei.lpIDList = ILCreateFromPath(szPath);
if (shei.lpIDList)
{
shei.cbSize = sizeof(shei);
shei.fMask = SEE_MASK_IDLIST;
shei.nShow = SW_SHOWNORMAL;
shei.lpVerb = TEXT("explore");
ShellExecuteEx(&shei);
ILFree(shei.lpIDList);
}
}
LocalFree(psz);
}
void Tray_OnFocusMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL fActivate = (BOOL) wParam;
switch (uMsg)
{
case TM_UIACTIVATEIO:
{
int dtb = (int) lParam;
TraceMsg(DM_FOCUS, "tiois: TM_UIActIO fAct=%d dtb=%d", fActivate, dtb);
ASSERT(dtb == 1 || dtb == -1);
if (fActivate)
{
// Since we are tabbing into the tray, turn the focus rect on.
SendMessage(g_ts.hwndMain, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
SendMessage(v_hwndDesktop, DTM_ONFOCUSCHANGEIS, TRUE, (LPARAM) v_hwndTray);
SetForegroundWindow(v_hwndTray);
// fake an UnkUIActivateIO(g_ts.ptbs, TRUE, &msg);
TraceMsg(DM_FOCUS, "tiois: TM_UIActIO fake UIAct");
if (GetAsyncKeyState(VK_SHIFT))
{
if (g_hwndDesktopTB && g_ts.fShowDeskBtn)
Tray_SetFocus(g_hwndDesktopTB);
else
Tray_SetFocus(g_ts.hwndNotify);
}
else
{
Tray_SetFocus(g_ts.hwndStart);
}
}
else
{
Ldeact:
UnkUIActivateIO(g_ts.ptbs, FALSE, NULL);
SetForegroundWindow(v_hwndDesktop);
}
break;
}
case TM_ONFOCUSCHANGEIS:
{
HWND hwnd = (HWND) lParam;
TraceMsg(DM_FOCUS, "tiois: TM_OnFocChgIS hwnd=%x fAct=%d", hwnd, fActivate);
if (fActivate)
{
// someone else is activating, so we need to deactivate
goto Ldeact;
}
break;
}
default:
ASSERT(0);
break;
}
}
#define TSVC_NTIMER (IDT_SERVICELAST - IDT_SERVICE0 + 1)
struct {
#ifdef DEBUG
UINT_PTR idtWin;
#endif
TIMERPROC pfnSvc;
} g_timerService[TSVC_NTIMER];
#define TSVC_IDToIndex(id) ((id) - IDT_SERVICE0)
#define TSVC_IndexToID(i) ((i) + IDT_SERVICE0)
int Tray_OnTimerService(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int i;
UINT_PTR idt;
TIMERPROC pfn;
BOOL b;
switch (uMsg) {
case TM_SETTIMER:
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: wP=0x%x lP=%x", wParam, lParam);
ASSERT(IS_VALID_CODE_PTR(lParam, TIMERPROC));
for (i = 0; i < TSVC_NTIMER; i++) {
if (g_timerService[i].pfnSvc == 0) {
g_timerService[i].pfnSvc = (TIMERPROC)lParam;
idt = SetTimer(v_hwndTray, TSVC_IndexToID(i), (UINT)wParam, 0);
if (idt == 0) {
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ST()=%d (!)", idt);
break;
}
ASSERT(idt == (UINT_PTR)TSVC_IndexToID(i));
DBEXEC(TRUE, (g_timerService[i].idtWin = idt));
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0x%x", TSVC_IndexToID(i));
return TSVC_IndexToID(i); // idtWin
}
}
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0 (!)");
return 0;
case TM_KILLTIMER: // lP=idtWin
TraceMsg(DM_UEMTRACE, "e.TM_KILLTIMER: wP=0x%x lP=%x", wParam, lParam);
if (EVAL(IDT_SERVICE0 <= lParam && lParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
i = (int)TSVC_IDToIndex(lParam);
if (g_timerService[i].pfnSvc) {
ASSERT(g_timerService[i].idtWin == (UINT)lParam);
b = KillTimer(v_hwndTray, lParam);
ASSERT(b);
g_timerService[i].pfnSvc = 0;
DBEXEC(TRUE, (g_timerService[i].idtWin = 0));
return TRUE;
}
}
return 0;
case WM_TIMER: // wP=idtWin lP=0
TraceMsg(DM_UEMTRACE, "e.TM_TIMER: wP=0x%x lP=%x", wParam, lParam);
if (EVAL(IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
i = (int)TSVC_IDToIndex(wParam);
pfn = g_timerService[i].pfnSvc;
if (EVAL(IS_VALID_CODE_PTR(pfn, TIMERPROC)))
(*pfn)(v_hwndTray, WM_TIMER, wParam, GetTickCount());
}
return 0;
}
ASSERT(0); /*NOTREACHED*/
return 0;
}
int g_fInSizeMove;
UINT GetDDEExecMsg()
{
static UINT uDDEExec = 0;
if (!uDDEExec)
uDDEExec = RegisterWindowMessage(TEXT("DDEEXECUTESHORTCIRCUIT"));
return uDDEExec;
}
extern void Task_RealityCheck(HWND hwndView);
void Tray_RealityCheck()
{
// Make sure that the tray's actual z-order position agrees with what we think it is.
// We need to do this because there's a recurring bug where the tray gets bumped out of TOPMOST position.
// (Lots of things, like a tray-owned window moving itself to non-TOPMOST or a random app messing with the tray window position, can cause this.)
Tray_ResetZorder();
// The taskbar needs to do some reality-checking as well (for things like ghost app buttons).
Task_RealityCheck(g_ts.hwndView);
}
void Tray_HandleDelayBootStuff()
{
// This posted message is the last one processed by the primary
// thread (tray thread) when we boot. At this point we will
// want to load the shell services (which usually create threads)
// and resume both the background start menu thread and the fs_notfiy thread.
if (!g_fHandledDelayBootStuff)
{
IShellTaskScheduler* pScheduler;
g_fHandledDelayBootStuff = TRUE;
PostMessage(v_hwndTray, TM_SHELLSERVICEOBJECTS, SSOCMDID_LOAD, 0);
PostMessage(v_hwndTray, TM_SHELLSERVICEOBJECTS, SSOCMDID_OPEN, 0);
BandSite_HandleDelayBootStuff(g_ts.ptbs);
// get the system background scheduler thread
if (SUCCEEDED(CoCreateInstance(&CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC, &IID_IShellTaskScheduler, (void **) &pScheduler)))
{
const static c_rgcsidl[] =
{
CSIDL_STARTMENU,
CSIDL_COMMON_STARTMENU,
// Don't do programs, since normally programs is already
// under startmenu
// CSIDL_PROGRAMS,
// CSIDL_COMMON_PROGRAMS,
// Don't do favorites since it is huge and better done on demand.
// CSIDL_FAVORITES,
CSIDL_RECENT
};
int i;
StartMenu_AddTask(pScheduler, CSIDL_DESKTOPDIRECTORY, THREAD_PRIORITY_NORMAL);
if (!SHRestricted(REST_NOCOMMONGROUPS))
StartMenu_AddTask(pScheduler, CSIDL_COMMON_DESKTOPDIRECTORY, THREAD_PRIORITY_NORMAL);
// Add the tasks that enumerate to set the hotkeys and
// build the startmenu
for (i = 0; i < ARRAYSIZE(c_rgcsidl); i++)
{
StartMenu_AddTask(pScheduler, c_rgcsidl[i], THREAD_PRIORITY_BELOW_NORMAL);
}
pScheduler->lpVtbl->Release(pScheduler);
}
//check to see if there are any files or folders that could interfere
//with the fact that Program Files has a space in it. An example would
//be a folder called "C:\Program" or a file called "C:\Program.exe"
Tray_CheckForRogueProgramFile();
// We spin a thread that will process "Load=", "Run=", CU\Run, and CU\RunOnce
RunStartupApps();
#ifdef WINNT
// NT5 hack, create a named event and fire it so that the services can
// go to work, giving the illusion that we booted faster
g_hShellReadyEvent = CreateEvent(0, FALSE, FALSE, TEXT("ShellReadyEvent"));
if ( g_hShellReadyEvent )
SetEvent( g_hShellReadyEvent );
#endif
}
}
LRESULT CALLBACK Tray_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static UINT uDDEExec = 0;
LRESULT lres = 0;
RECT r1;
POINT pt;
DWORD dw;
MSG msg;
msg.hwnd = hwnd;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
if (g_ts._pmbStartMenu && g_ts._pmbStartMenu->lpVtbl->TranslateMenuMessage(g_ts._pmbStartMenu, &msg, &lres) == S_OK)
return lres;
wParam = msg.wParam;
lParam = msg.lParam;
INSTRUMENT_WNDPROC(SHCNFI_TRAY_WNDPROC, hwnd, uMsg, wParam, lParam);
switch (uMsg) {
case WMTRAY_PRINTCHANGE:
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
// BUGBUG (scotth): shouldn't need to filter out update images,
// but to avoid regressions so close to beta 1...
if (SHCNE_UPDATEIMAGE != lEvent)
PrintNotify_HandleFSNotify(g_ts.hwndMain, ppidl, lEvent);
lres = 1;
SHChangeNotification_Unlock(pshcnl);
}
break;
}
case WMTRAY_PRINTICONNOTIFY:
PrintNotify_IconNotify(hwnd, lParam);
break;
case WMTRAY_REGISTERHOTKEY:
return Tray_RegisterHotkey(hwnd, (int)wParam);
case WMTRAY_UNREGISTERHOTKEY:
return Tray_UnregisterHotkey(hwnd, (int)wParam);
case WMTRAY_SCREGISTERHOTKEY:
return Tray_ShortcutRegisterHotkey(hwnd, (WORD)wParam, (ATOM)lParam);
case WMTRAY_SCUNREGISTERHOTKEY:
return Tray_ShortcutUnregisterHotkey(hwnd, (WORD)wParam);
case WMTRAY_SETHOTKEYENABLE:
return Tray_SetHotkeyEnable(hwnd, (BOOL)wParam);
case WMTRAY_QUERY_MENU:
return (LRESULT)g_ts.hmenuStart;
case WMTRAY_QUERY_VIEW:
return (LRESULT)g_ts.hwndView;
case WM_COPYDATA:
// Check for NULL it can happen if user runs out of selectors or memory...
if (lParam)
{
switch (((PCOPYDATASTRUCT)lParam)->dwData) {
case TCDM_NOTIFY:
{
BOOL bRefresh = TRUE;
lres = TrayNotify(g_ts.hwndNotify, (HWND)wParam, (PCOPYDATASTRUCT)lParam, &bRefresh);
if(bRefresh)
{
Tray_SizeWindows();
}
return(lres);
}
case TCDM_APPBAR:
return TrayAppBarMessage((PCOPYDATASTRUCT)lParam);
case TCDM_LOADINPROC:
return TrayLoadInProc((PCOPYDATASTRUCT)lParam);
}
}
return FALSE;
case WM_NCLBUTTONDBLCLK:
if (IsPosInHwnd(lParam, g_ts.hwndNotify)) {
Tray_Command(IDM_SETTIME);
// Hack! If you click on the tray clock, this tells the tooltip
// "Hey, I'm using this thing; stop putting up a tip for me."
// You can get the tooltip to lose track of when it needs to
// reset the "stop it!" flag and you get stuck in "stop it!" mode.
// It's particularly easy to make happen on Terminal Server.
// So let's assume that the only reason people click on the
// tray clock is to change the time. when they change the time,
// kick the tooltip in the head to reset the "stop it!" flag.
SendMessage(g_ts.hwndTrayTips, TTM_POP, 0, 0);
}
break;
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCMOUSEMOVE:
if (IsPosInHwnd(lParam, g_ts.hwndNotify)) {
MSG msg;
msg.lParam = lParam;
msg.wParam = wParam;
msg.message = uMsg;
msg.hwnd = hwnd;
SendMessage(g_ts.hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msg);
if (uMsg == WM_NCLBUTTONDOWN)
Tray_SetFocus(g_ts.hwndNotify);
}
goto DoDefault;
case WM_CREATE:
return Tray_OnCreate(hwnd);
break;
case WM_DESTROY:
return Tray_HandleDestroy();
#ifdef DEBUG
case WM_QUERYENDSESSION:
TraceMsg(DM_SHUTDOWN, "Tray.wp WM_QUERYENDSESSION");
goto DoDefault;
#endif
case WM_ENDSESSION:
// save our settings if we are shutting down
TraceMsg(DM_SHUTDOWN, "Tray.wp WM_ENDSESSION wP=%d lP=%d", wParam, lParam);
if (wParam)
{
TraceMsg(TF_TRAY, "Tray WM_ENDSESSION");
_SaveTrayAndDesktop();
ShowWindow(v_hwndTray, SW_HIDE);
ShowWindow(v_hwndDesktop, SW_HIDE);
RegisterShellHook(g_ts.hwndView, FALSE);
Tray_HandleShellServiceObject(SSOCMDID_CLOSE, 0);
TraceMsg(DM_SHUTDOWN, "Tray.wp WM_ENDSESSION call DestroyWindow v_hwndTray=%x", v_hwndTray);
DestroyWindow(v_hwndTray);
v_hwndTray = NULL;
}
TraceMsg(DM_SHUTDOWN, "Tray.wp WM_ENDSESSION return 0");
break;
case WM_PAINT:
// draw etched line around on either side of the bandsite
if (g_ts.fCoolTaskbar) {
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
GetWindowRect(g_ts.hwndRebar, &rc);
MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rc, 2);
InflateRect(&rc, g_cxEdge, g_cyEdge);
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOPLEFT); // NT5 VFREEZE: remove right & bottom etch
EndPaint(hwnd, &ps);
} else
goto DoDefault;
break;
case WM_POWER:
case WM_POWERBROADCAST:
_PropagateMessage(hwnd, uMsg, wParam, lParam);
Tray_HandlePowerStatus(uMsg, wParam, lParam);
goto DoDefault;
case WM_DEVICECHANGE:
if (wParam == DBT_CONFIGCHANGED)
{
Tray_UpdateDockingFlags();
// Set this bit, so that the API
if (IsEjectAllowed(TRUE))
{
HandleDisplayChange(0, 0, TRUE);
}
RefreshStartMenu();// We got an update. Refresh.
}
else if (wParam == DBT_QUERYCHANGECONFIG)
{
if (IsEjectAllowed(FALSE) && !IsDisplayChangeSafe())
{
if (ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_DISPLAY_WARN), MAKEINTRESOURCE(IDS_WINDOWS), MB_ICONEXCLAMATION|MB_YESNO|MB_DEFBUTTON2) == IDNO)
{
return BROADCAST_QUERY_DENY;
}
}
// drop down to 640x480 (or the lowest res for all configs)
// before the config change. this makes sure we dont screw
// up some laptop display panels.
dw = GetMinDisplayRes();
HandleDisplayChange(LOWORD(dw), HIWORD(dw), FALSE);
}
else if (wParam == DBT_MONITORCHANGE)
{
// handle monitor change
HandleDisplayChange(LOWORD(lParam), HIWORD(lParam), TRUE);
}
else if (wParam == DBT_CONFIGCHANGECANCELED)
{
// if the config change was canceled go back
HandleDisplayChange(0, 0, FALSE);
}
goto DoDefault;
case WM_NOTIFY:
{
NMHDR *pnm = (NMHDR*)lParam;
if (!BandSite_HandleMessage(g_ts.ptbs, hwnd, uMsg, wParam, lParam, &lres)) {
switch (pnm->code)
{
case SEN_DDEEXECUTE:
if (((LPNMHDR)lParam)->idFrom == 0)
{
LPNMVIEWFOLDER pnmPost = DDECreatePostNotify((LPNMVIEWFOLDER)pnm);
if (pnmPost)
{
PostMessage(hwnd, GetDDEExecMsg(), 0, (LPARAM)pnmPost);
return TRUE;
}
}
break;
case NM_STARTWAIT:
case NM_ENDWAIT:
Tray_OnWaitCursorNotify((NMHDR *)lParam);
PostMessage(v_hwndDesktop, ((NMHDR*)lParam)->code == NM_STARTWAIT ? DTM_STARTWAIT : DTM_ENDWAIT, 0, 0); // forward it along
break;
case TTN_NEEDTEXT:
{
#ifdef DESKBTN
#define lpttt ((LPTOOLTIPTEXT)lParam)
POINT pt;
GetCursorPos(&pt);
if (WindowFromPoint(pt) == g_hwndDesktopTB) {
if (g_hdlgDesktopConfig)
lpttt->szText[0] = TEXT('\0');
else
LoadString(hinstCabinet, IDS_SHOWDESKTOP, lpttt->szText, ARRAYSIZE(lpttt->szText));
} else
#endif
{
// Make the clock manage its own tooltip.
return SendMessage(Tray_GetClockWindow(), WM_NOTIFY, wParam, lParam);
}
return TRUE;
}
case TTN_SHOW:
SetWindowZorder(g_ts.hwndTrayTips, HWND_TOP);
break;
}
}
break;
}
case WM_CLOSE:
DoExitWindows(v_hwndDesktop);
return 0;
case WM_NCHITTEST:
GetClientRect(hwnd, &r1);
MapWindowPoints(hwnd, NULL, (LPPOINT)&r1, 2);
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
TraySetUnhideTimer(pt.x, pt.y);
// allow dragging if mouse is in client area of v_hwndTray
if (PtInRect(&r1, pt)) {
return HTCAPTION;
}
else {
switch (g_ts.uStuckPlace)
{
case STICK_LEFT:
dw = ((pt.x > r1.right) ? HTRIGHT : HTBORDER);
break;
case STICK_TOP:
dw = ((pt.y > r1.bottom) ? HTBOTTOM : HTBORDER);
break;
case STICK_RIGHT:
dw = ((pt.x < r1.left) ? HTLEFT : HTBORDER);
break;
case STICK_BOTTOM:
default:
dw = ((pt.y < r1.top) ? HTTOP : HTBORDER);
break;
}
return dw;
}
break;
case WM_WINDOWPOSCHANGING:
Tray_HandleWindowPosChanging((LPWINDOWPOS)lParam);
break;
case WM_ENTERSIZEMOVE:
DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_ENTERSIZEMOVE"));
g_fInSizeMove = TRUE;
// unclip the tray from the current monitor.
// keeping the tray properly clipped in the MoveSize loop is extremely
// hairy and provides almost no benefit. we'll re-clip when it lands.
Tray_ClipWindow(FALSE);
g_ts.hmonOld = g_ts.hmonStuck;// Remember the old monitor we were on
// set up for WM_MOVING/WM_SIZING messages
g_ts.uMoveStuckPlace = (UINT)-1;
g_ts.fSysSizing = TRUE;
break;
case WM_EXITSIZEMOVE:
DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_EXITSIZEMOVE"));
// done sizing
g_ts.fSysSizing = FALSE;
g_ts._fDeferedPosRectChange = FALSE;
// kick the size code one last time after the loop is done.
// NOTE: we rely on the WM_SIZE code re-clipping the tray.
PostMessage(hwnd, WM_SIZE, 0, 0L);
g_fInSizeMove = FALSE;
break;
case WM_MOVING:
Tray_HandleMoving(wParam, (LPRECT)lParam);
break;
case WM_ENTERMENULOOP:
// DebugMsg(DM_TRACE, "c.twp: Enter menu loop.");
Tray_HandleEnterMenuLoop();
break;
case WM_EXITMENULOOP:
// DebugMsg(DM_TRACE, "c.twp: Exit menu loop.");
Tray_HandleExitMenuLoop();
break;
case WM_TIMER:
if (IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICELAST)
return Tray_OnTimerService(uMsg, wParam, lParam);
Tray_HandleTimer(wParam);
break;
case WM_SIZING:
Tray_HandleSizing(wParam, (LPRECT)lParam, g_ts.uStuckPlace);
break;
case WM_SIZE:
Tray_HandleSize();
break;
case WM_DISPLAYCHANGE:
// NOTE: we get WM_DISPLAYCHANGE in the below two situations
// 1. a display size changes (HMON will not change in USER)
// 2. a display goes away or gets added (HMON will change even if
// the monitor that went away has nothing to do with our hmonStuck)
// In the above two situations we actually need to do different things
// because in 1, we do not want to update our hmonStuck because we might
// end up on another monitor, but in 2 we do want to update hmonStuck because
// our hmon is invalid
// The way we handle this is to call GetMonitorInfo on our old HMONITOR
// and see if it's still valid, if not, we update it by calling Tray_SetStuckMonitor
// all these code is in Tray_ScreenSizeChange;
Tray_ScreenSizeChange(hwnd);
break;
// Don't go to default wnd proc for this one...
case WM_INPUTLANGCHANGEREQUEST:
return(LRESULT)0L;
case WM_GETMINMAXINFO:
((MINMAXINFO *)lParam)->ptMinTrackSize.x = g_cxFrame;
((MINMAXINFO *)lParam)->ptMinTrackSize.y = g_cyFrame;
break;
case WM_WININICHANGE:
if (lParam && (0 == lstrcmpi((LPCTSTR)lParam, TEXT("SaveTaskbar"))))
{
_SaveTrayAndDesktop();
}
else
{
BandSite_HandleMessage(g_ts.ptbs, hwnd, uMsg, wParam, lParam, NULL);
_PropagateMessage(hwnd, uMsg, wParam, lParam);
Tray_OnWinIniChange(hwnd, wParam, lParam);
}
if (lParam)
TraceMsg(TF_TRAY, "Tray Got: lParam=%s", (LPCSTR)lParam);
break;
case WM_TIMECHANGE:
_PropagateMessage(hwnd, uMsg, wParam, lParam);
break;
case WM_SYSCOLORCHANGE:
Tray_OnNewSystemSizes();
BandSite_HandleMessage(g_ts.ptbs, hwnd, uMsg, wParam, lParam, NULL);
_PropagateMessage(hwnd, uMsg, wParam, lParam);
break;
case WM_SETCURSOR:
if (g_ts.iWaitCount) {
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
return TRUE;
} else
goto DoDefault;
case WM_SETFOCUS:
if (g_ts.hwndView)
SetFocus(g_ts.hwndView);
break;
case WM_SYSCHAR:
if (wParam == TEXT(' ')) {
HMENU hmenu;
int idCmd;
SetWindowStyleBit(hwnd, WS_SYSMENU, WS_SYSMENU);
hmenu = GetSystemMenu(hwnd, FALSE);
if (hmenu) {
EnableMenuItem(hmenu, SC_RESTORE, MFS_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenu, SC_MAXIMIZE, MFS_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenu, SC_MINIMIZE, MFS_GRAYED | MF_BYCOMMAND);
idCmd = Tray_TrackMenu(hmenu);
if (idCmd)
SendMessage(v_hwndTray, WM_SYSCOMMAND, idCmd, 0L);
}
SetWindowStyleBit(hwnd, WS_SYSMENU, 0L);
}
break;
case WM_SYSCOMMAND:
// if we are sizing, make the full screen accessible
switch (wParam & 0xFFF0) {
case SC_CLOSE:
DoExitWindows(v_hwndDesktop);
break;
default:
goto DoDefault;
}
break;
case TM_DESKTOPSTATE:
Tray_OnDesktopState(lParam);
break;
#ifdef DEBUG
case TM_NEXTCTL:
#endif
case TM_UIACTIVATEIO:
case TM_ONFOCUSCHANGEIS:
Tray_OnFocusMsg(uMsg, wParam, lParam);
break;
case TM_MARSHALBS: // wParam=IID lRes=pstm
return Tray_OnMarshalBS(wParam, lParam);
case TM_SETTIMER:
case TM_KILLTIMER:
return Tray_OnTimerService(uMsg, wParam, lParam);
break;
case TM_REFRESH: // cross-component FCIDM_REFRESH
TraceMsg(DM_TRACE, "TM_REFRESH");
Tray_Command(FCIDM_REFRESH);
break;
case TM_ACTASTASKSW:
Tray_ActAsSwitcher();
break;
case TM_RELAYPOSCHANGED:
AppBarNotifyAll((HMONITOR)lParam, ABN_POSCHANGED, (HWND)wParam, 0);
break;
case TM_BRINGTOTOP:
SetWindowZorder((HWND)wParam, HWND_TOP);
break;
case TM_WARNNOAUTOHIDE:
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp collision UI request"));
// this may look a little funny but what we do is post this message all
// over the place and ignore it when we think it is a bad time to put
// up a message (like the middle of a fulldrag...)
// wParam tells us if we need to try to clear the state
// the lowword of Tray_SetAutoHideState's return tells if anything changed
if ((!g_ts.fSysSizing || !g_fDragFullWindows) && (!wParam || LOWORD(Tray_SetAutoHideState(FALSE))))
{
ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR), MAKEINTRESOURCE(IDS_TASKBAR), MB_OK | MB_ICONINFORMATION);
}
else
{
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp blowing off extraneous collision UI request"));
}
break;
case TM_WARNNODROP:
// tell the user they can't drop objects on the taskbar
ShellMessageBox(hinstCabinet, v_hwndTray,
MAKEINTRESOURCE(IDS_TASKDROP_ERROR), MAKEINTRESOURCE(IDS_TASKBAR),
MB_ICONHAND | MB_OK);
break;
case TM_PRIVATECOMMAND:
Tray_HandlePrivateCommand(lParam);
break;
case TM_HANDLEDELAYBOOTSTUFF:
Tray_HandleDelayBootStuff();
break;
case TM_SHELLSERVICEOBJECTS:
Tray_HandleShellServiceObject(wParam, lParam);
break;
case TM_GETHMONITOR:
*((HMONITOR *)lParam) = g_ts.hmonStuck;
break;
case WM_NCRBUTTONUP:
uMsg = WM_CONTEXTMENU;
wParam = (WPARAM)g_ts.hwndView;
goto L_WM_CONTEXTMENU;
case WM_CONTEXTMENU:
L_WM_CONTEXTMENU:
if (SHRestricted(REST_NOTRAYCONTEXTMENU))
{
break;
}
if (((HWND)wParam) == g_ts.hwndStart)
{
// Don't display of the Start Menu is up.
if (SendMessage(g_ts.hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
break;
StartMenuFolder_ContextMenu((DWORD)lParam);
}
else if (g_hwndDesktopTB && g_hwndDesktopTB == (HWND)wParam)
{
Tray_DesktopMenu((DWORD)-1);
}
else if (IsPosInHwnd(lParam, g_ts.hwndNotify))
{
// if click was inthe clock, include
// the time
Tray_ContextMenu((DWORD)lParam, TRUE);
}
else
{
BandSite_HandleMessage(g_ts.ptbs, hwnd, uMsg, wParam, lParam, &lres);
}
break;
case TM_DOEXITWINDOWS:
DoExitWindows(v_hwndDesktop);
break;
case WM_HOTKEY:
if (wParam < GHID_FIRST)
{
HotkeyList_HandleHotkey((WORD)wParam);
}
else
{
Tray_HandleGlobalHotkey(wParam);
}
break;
case WM_COMMAND:
if (!BandSite_HandleMessage(g_ts.ptbs, hwnd, uMsg, wParam, lParam, &lres))
Tray_Command(GET_WM_COMMAND_ID(wParam, lParam));
break;
case SBM_CANCELMENU:
if (g_ts._pmpStartMenu)
g_ts._pmpStartMenu->lpVtbl->OnSelect(g_ts._pmpStartMenu, MPOS_FULLCANCEL);
break;
case WM_WINDOWPOSCHANGED:
IAppBarActivationChange(hwnd, g_ts.uStuckPlace);
SendMessage(g_ts.hwndNotify, TNM_TRAYPOSCHANGED, 0, 0);
goto DoDefault;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
if (g_ts.hwndStartBalloon)
{
RECT rc;
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
GetWindowRect(g_ts.hwndStartBalloon, &rc);
MapWindowRect(HWND_DESKTOP, v_hwndTray, &rc);
if (PtInRect(&rc, pt))
{
ShowWindow(g_ts.hwndStartBalloon, SW_HIDE);
DontShowTheStartButtonBalloonAnyMore();
DestroyStartButtonBalloon();
}
}
break;
case WM_ACTIVATE:
IAppBarActivationChange(hwnd, g_ts.uStuckPlace);
if (wParam != WA_INACTIVE)
{
Tray_Unhide();
}
else
{
// When tray is deactivated, remove our keyboard cues:
SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
UnkUIActivateIO(g_ts.ptbs, FALSE, NULL);
}
// Tray activation is a good time to do a reality check
// (make sure "always-on-top" agrees with actual window
// position, make sure there are no ghost buttons, etc).
Tray_RealityCheck();
goto L_default;
default:
L_default:
if (uMsg == GetDDEExecMsg())
{
ASSERT(lParam && 0 == ((LPNMHDR)lParam)->idFrom);
DDEHandleViewFolderNotify(NULL, g_ts.hwndMain, (LPNMVIEWFOLDER)lParam);
LocalFree((LPNMVIEWFOLDER)lParam);
return TRUE;
}
else if (uMsg == g_uStartButtonBalloonTip)
{
ShowStartButtonToolTip();
}
else if (uMsg == g_uLogoffUser)
{
// Log off the current user (message from U&P control panel)
ExitWindowsEx(EWX_LOGOFF, 0);
}
DoDefault:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return lres;
}
// Just like shells SHRestricted() only this put up a message if the restricion is in effect.
BOOL _Restricted(HWND hwnd, RESTRICTIONS rest)
{
if (SHRestricted(rest))
{
ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_RESTRICTIONS), MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK|MB_ICONSTOP);
return TRUE;
}
return FALSE;
}
void DoExitWindows(HWND hwnd)
{
static BOOL g_fShellShutdown = FALSE;
if (!g_fShellShutdown)
{
if (_Restricted(hwnd, REST_NOCLOSE))
return;
{
UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, FALSE, -1);
// really #ifdef DEBUG, but want for testing
// however can't do unconditionally due to perf
if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuForceRefresh"), NULL, NULL, NULL) || GetAsyncKeyState(VK_SHIFT) < 0)
{
RefreshStartMenu();
}
}
_SaveTrayAndDesktop();
g_ts.uModalMode = MM_SHUTDOWN;
ExitWindowsDialog(hwnd);
// NB User can have problems if the focus is forcebly changed to the desktop while
// shutting down since it tries to serialize the whole process by making windows sys-modal.
// If we hit this code at just the wrong moment (ie just after the sharing dialog appears)
// the desktop will become sys-modal so you can't switch back to the sharing dialog
// and you won't be able to shutdown.
// SetForegroundWindow(hwnd);
// SetFocus(hwnd);
g_ts.uModalMode = 0;
if ((GetKeyState(VK_SHIFT) < 0) && (GetKeyState(VK_CONTROL) < 0) && (GetKeyState(VK_MENU) < 0))
{
// User cancelled...
// The shift key means exit the tray...
// ??? - Used to destroy all cabinets...
// PostQuitMessage(0);
g_fFakeShutdown = TRUE; // Don't blow away session state; the session will survive
TraceMsg(TF_TRAY, "c.dew: Posting quit message for tid=%#08x hwndDesk=%x(IsWnd=%d) hwndTray=%x(IsWnd=%d)", GetCurrentThreadId(),
v_hwndDesktop,IsWindow(v_hwndDesktop), v_hwndTray,IsWindow(v_hwndTray));
// 1 means close all the shell windows too
PostMessage(v_hwndDesktop, WM_QUIT, 0, 1);
PostMessage(v_hwndTray, WM_QUIT, 0, 0);
g_fShellShutdown = TRUE;
}
}
}
#define MAX_FILE_PROP_PAGES 5 // More than enough
BOOL CALLBACK _TrayAddPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam)
{
PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
if (ppsh->nPages < MAX_FILE_PROP_PAGES)
{
ppsh->phpage[ppsh->nPages++] = hpage;
return TRUE;
}
return FALSE;
}
void RealTrayProperties(HWND hwndParent, INT nStartPage)
{
HPROPSHEETPAGE ahpage[MAX_FILE_PROP_PAGES];
TCHAR szPath[MAX_PATH];
PROPSHEETHEADER psh;
LoadString(hinstCabinet, IDS_STARTMENUANDTASKBAR, szPath, ARRAYSIZE(szPath));
psh.dwSize = SIZEOF(psh);
psh.dwFlags = PSH_PROPTITLE;
psh.hInstance = hinstCabinet;
psh.hwndParent = hwndParent;
psh.pszCaption = szPath;
psh.nPages = 0; // incremented in callback
psh.nStartPage = nStartPage;
psh.phpage = ahpage;
CShellTray_AddViewPropertySheetPages(0L, _TrayAddPropSheetPage, (LPARAM)(LPPROPSHEETHEADER)&psh);
// Open the property sheet, only if we have some pages.
if (psh.nPages > 0)
{
//g_ts.uModalMode = MM_OTHER;
PropertySheet(&psh);
//g_ts.uModalMode = 0;
}
}
DWORD WINAPI TrayPropertiesThread(void *lpData)
{
HWND hwnd;
RECT rc;
DWORD dwExStyle = WS_EX_TOOLWINDOW;
INT nStartPage = PtrToUlong(lpData);
CoInitialize(0);
GetWindowRect(g_ts.hwndStart, &rc);
dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
g_ts.hwndProp = hwnd = CreateWindowEx(dwExStyle, TEXT("static"), NULL, 0 , rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
if (g_ts.hwndProp) {
// SwitchToThisWindow(hwnd, TRUE);
// SetForegroundWindow(hwnd);
RealTrayProperties(hwnd, nStartPage);
g_ts.hwndProp = NULL;
DestroyWindow(hwnd);
}
CoUninitialize();
return TRUE;
}
void DoTrayProperties(INT nStartPage)
{
if (!_Restricted(g_ts.hwndMain, REST_NOSETTASKBAR))
{
int i = RUNWAITSECS;
while (g_ts.hwndProp == ((HWND)-1) &&i--) {
// we're in the process of coming up. wait
Sleep(SECOND);
}
// failed! blow it off.
if (g_ts.hwndProp == (HWND)-1)
g_ts.hwndProp = NULL;
if (g_ts.hwndProp)
{
// there's a window out there... activate it
SwitchToThisWindow(GetLastActivePopup(g_ts.hwndProp), TRUE);
} else {
g_ts.hwndProp = (HWND)-1;
if (!SHCreateThread(TrayPropertiesThread, (void *)nStartPage, 0, NULL)) {
g_ts.hwndProp = 0;
}
}
}
}
void ShowFolder(HWND hwnd, UINT csidl, UINT uFlags)
{
SHELLEXECUTEINFO shei = { 0 };
shei.cbSize = sizeof(shei);
shei.fMask = SEE_MASK_IDLIST | SEE_MASK_INVOKEIDLIST;
shei.nShow = SW_SHOWNORMAL;
if (_Restricted(hwnd, REST_NOSETFOLDERS))
return;
if (uFlags & COF_EXPLORE)
shei.lpVerb = TEXT("explore");
shei.lpIDList = SHCloneSpecialIDList(NULL, csidl, FALSE);
if (shei.lpIDList)
{
ShellExecuteEx(&shei);
ILFree(shei.lpIDList);
}
}
BOOL Tray_IsAutoHide()
{
return g_ts.uAutoHide & AH_ON;
}
DWORD Tray_GetStuckPlace()
{
return g_ts.uStuckPlace;
}
BOOL CanMinimizeAll(HWND hwndView);
BOOL CanTileWindow(HWND hwnd, LPARAM lParam)
{
BOOL *pf = (BOOL*)lParam;
if (IsWindowVisible(hwnd) && !IsIconic(hwnd) && ((GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) && (hwnd != v_hwndTray) && hwnd != v_hwndDesktop) {
*pf = TRUE;
}
return !*pf;
}
BOOL CanTileAnyWindows()
{
BOOL f = FALSE;
EnumWindows(CanTileWindow, (LPARAM)&f);
return f;
}
HMENU Tray_BuildContextMenu(BOOL fIncludeTime)
{
HKEY hKeyPolicy;
HMENU hmContext = LoadMenuPopup(MAKEINTRESOURCE(MENU_TRAYCONTEXT));
if (!hmContext)
return NULL;
if (fIncludeTime) {
INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT_CLOCK);
SetMenuDefaultItem(hmContext, IDM_SETTIME, MF_BYCOMMAND);
} else {
INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT);
DeleteMenu(hmContext, IDM_SETTIME, MF_BYCOMMAND);
}
if (!g_ts.fUndoEnabled || !g_ts.pPositions) {
DeleteMenu(hmContext, IDM_UNDO, MF_BYCOMMAND);
} else {
TCHAR szTemplate[30];
TCHAR szCommand[30];
TCHAR szMenu[64];
LoadString(hinstCabinet, IDS_UNDOTEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
LoadString(hinstCabinet, g_ts.pPositions->idRes, szCommand, ARRAYSIZE(szCommand));
wsprintf(szMenu, szTemplate, szCommand);
ModifyMenu(hmContext, IDM_UNDO, MF_BYCOMMAND | MF_STRING, IDM_UNDO, szMenu);
}
if (!CanMinimizeAll(g_ts.hwndView))
EnableMenuItem(hmContext, IDM_MINIMIZEALL, MFS_GRAYED | MF_BYCOMMAND);
if (!CanTileAnyWindows()) {
EnableMenuItem(hmContext, IDM_CASCADE, MFS_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmContext, IDM_HORIZTILE, MFS_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmContext, IDM_VERTTILE, MFS_GRAYED | MF_BYCOMMAND);
}
if (RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"), 0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS)
{
DWORD dwType, dwData = 0, dwSize;
dwSize = sizeof(dwData);
RegQueryValueEx (hKeyPolicy, TEXT("DisableTaskMgr"), NULL, &dwType, (LPBYTE) &dwData, &dwSize);
RegCloseKey (hKeyPolicy);
if (dwData)
EnableMenuItem(hmContext, IDM_SHOWTASKMAN, MFS_GRAYED | MF_BYCOMMAND);
}
BandSite_AddMenus(g_ts.ptbs, hmContext, 0, 0, IDM_TRAYCONTEXTFIRST);
return hmContext;
}
void Tray_ContextMenuInvoke(int idCmd)
{
if (idCmd) {
if (idCmd < IDM_TRAYCONTEXTFIRST)
BandSite_HandleMenuCommand(g_ts.ptbs, idCmd);
else
Tray_Command(idCmd);
}
}
// We need to save our tray settings, but there may be a bunch of these calls coming,
// (at startup time or when dragging
// items in the task bar) so gather them up into one save that will happen in at least 2 seconds.
STDAPI_(void) Tray_AsyncSaveSettings()
{
if (!g_fHandledDelayBootStuff) // no point in saving if we're not done booting
return;
KillTimer(v_hwndTray, IDT_SAVESETTINGS);
SetTimer(v_hwndTray, IDT_SAVESETTINGS, 2000, NULL);
}
void Tray_ContextMenu(DWORD dwPos, BOOL fIncludeTime)
{
HMENU hmContext;
int idCmd;
POINT pt = {LOWORD(dwPos), HIWORD(dwPos)};
SwitchToThisWindow(g_ts.hwndMain, TRUE);
SetForegroundWindow(g_ts.hwndMain);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
if (dwPos != (DWORD)-1 && IsChildOrHWND(g_ts.hwndRebar, WindowFromPoint(pt))) {
// if the context menu came from below us, reflect down
BandSite_HandleMessage(g_ts.ptbs, v_hwndTray, WM_CONTEXTMENU, 0, dwPos, NULL);
} else {
if (dwPos == (DWORD)-1) {
pt.x = pt.y = 0;
ClientToScreen(g_ts.hwndView, &pt);
dwPos = MAKELONG(pt.x, pt.y);
}
hmContext = Tray_BuildContextMenu(fIncludeTime);
if (!hmContext)
return;
idCmd = TrackPopupMenu(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, v_hwndTray, NULL);
DestroyMenu(hmContext);
Tray_ContextMenuInvoke(idCmd);
}
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
}
void Tray_DesktopMenu(DWORD dwPos)
{
POINT pt;
RECT rc;
if (dwPos == (DWORD)-1)
{
if (g_hwndDesktopTB && g_ts.fShowDeskBtn)
{
GetClientRect(g_hwndDesktopTB, &rc);
pt.x = rc.right;
pt.y = rc.bottom;
ClientToScreen(g_hwndDesktopTB, &pt);
}
else
{
GetCursorPos(&pt);
}
dwPos = MAKELONG(pt.x, pt.y);
}
PostMessage(v_hwndDesktop, DTM_DESKTOPCONTEXTMENU, 0, dwPos);
}
void _RunFileDlg(HWND hwnd, UINT idIcon, LPCITEMIDLIST pidlWorkingDir, UINT idTitle, UINT idPrompt, DWORD dwFlags)
{
HICON hIcon;
LPCTSTR lpszTitle;
LPCTSTR lpszPrompt;
TCHAR szTitle[256];
TCHAR szPrompt[256];
TCHAR szWorkingDir[MAX_PATH];
dwFlags |= RFD_USEFULLPATHDIR;
szWorkingDir[0] = 0;
hIcon = idIcon ? LoadIcon(hinstCabinet, MAKEINTRESOURCE(idIcon)) : NULL;
if (!pidlWorkingDir || !SHGetPathFromIDList(pidlWorkingDir, szWorkingDir))
{
// This is either the Tray, or some non-file system folder, so
// we will "suggest" the Desktop as a working dir, but if the
// user types a full path, we will use that instead. This is what WIN31 Progman did (except they started in the Windows dir instead of the Desktop).
goto UseDesktop;
}
// if it's a removable dir, make sure it's still there
if (szWorkingDir[0])
{
int idDrive = PathGetDriveNumber(szWorkingDir);
if ((idDrive != -1))
{
UINT dtype = DriveType(idDrive);
if ( ( (dtype == DRIVE_REMOVABLE) || (dtype == DRIVE_CDROM) )
&& !PathFileExists(szWorkingDir) )
{
goto UseDesktop;
}
}
}
// Check if this is a directory. Notice that it could be a in-place
// navigated document.
if (PathIsDirectory(szWorkingDir)) {
goto UseWorkingDir;
}
UseDesktop:
SHGetSpecialFolderPath(hwnd, szWorkingDir, CSIDL_DESKTOPDIRECTORY, FALSE);
UseWorkingDir:
if (idTitle)
{
LoadString(hinstCabinet, idTitle, szTitle, ARRAYSIZE(szTitle));
lpszTitle = szTitle;
}
else
lpszTitle = NULL;
if (idPrompt)
{
LoadString(hinstCabinet, idPrompt, szPrompt, ARRAYSIZE(szPrompt));
lpszPrompt = szPrompt;
}
else
lpszPrompt = NULL;
RunFileDlg(hwnd, hIcon, szWorkingDir, lpszTitle, lpszPrompt, dwFlags);
}
#ifdef DEBUG
BOOL ShouldMinMax(HWND hwnd, BOOL fMin);
#endif
BOOL SavePosEnumProc(HWND hwnd, LPARAM lParam)
{
// dont need to entercritical here since we are only ever
// called from SaveWindowPositions, which as already entered the critical secion
// for g_ts.pPositions
ASSERT(g_ts.pPositions);
if (IsWindowVisible(hwnd) && (hwnd != v_hwndTray) && (hwnd != v_hwndDesktop))
{
HWNDANDPLACEMENT hap;
hap.wp.length = SIZEOF(WINDOWPLACEMENT);
GetWindowPlacement(hwnd, &hap.wp);
#ifdef DEBUG
TraceMsg(TF_TRAY, "SaveWindowPostitionsCallback(hwnd=0x%x lP=%x) sc=%d par=0x%x own=0x%x", hwnd, (int)lParam, hap.wp.showCmd, GetParent(hwnd), GetWindow(hwnd, GW_OWNER));
#endif
if (hap.wp.showCmd != SW_SHOWMINIMIZED) {
hap.hwnd = hwnd;
hap.fRestore = TRUE;
#ifdef DEBUG
TraceMsg(TF_TRAY, "SaveWindowPostitionsCallback: sm()=%d add hwnd=0x%x sc=%d fRestore=%d", ShouldMinMax(hwnd, TRUE), hwnd, hap.wp.showCmd, hap.fRestore);
#endif
DSA_AppendItem(g_ts.pPositions->hdsaWP, &hap);
}
}
return TRUE;
}
void SaveWindowPositions(UINT idRes)
{
ENTERCRITICAL;
if (g_ts.pPositions)
{
if (g_ts.pPositions->hdsaWP)
DSA_DeleteAllItems(g_ts.pPositions->hdsaWP);
}
else
{
g_ts.pPositions = (LPWINDOWPOSITIONS)LocalAlloc(LPTR, SIZEOF(WINDOWPOSITIONS));
if (!g_ts.pPositions)
{
LEAVECRITICAL;
return;
}
g_ts.pPositions->hdsaWP = DSA_Create(SIZEOF(HWNDANDPLACEMENT), 4);
}
g_ts.pPositions->idRes = idRes;
// CheckWindowPositions tested for these...
ASSERT(idRes == IDS_MINIMIZEALL || idRes == IDS_CASCADE || idRes == IDS_TILE);
EnumWindows(SavePosEnumProc, 0);
LEAVECRITICAL;
}
DWORD RestoreWindowPositionsThread( LPWINDOWPOSITIONS pPositions )
{
int i;
LPHWNDANDPLACEMENT phap;
LONG iAnimate;
ANIMATIONINFO ami;
ami.cbSize = SIZEOF(ANIMATIONINFO);
SystemParametersInfo(SPI_GETANIMATION, SIZEOF(ami), &ami, FALSE);
iAnimate = ami.iMinAnimate;
ami.iMinAnimate = FALSE;
SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
i = DSA_GetItemCount(pPositions->hdsaWP) - 1;
for ( ; i >= 0; i--) {
phap = DSA_GetItemPtr(pPositions->hdsaWP, i);
if (IsWindow(phap->hwnd)) {
#if (defined(DBCS) || defined(FE_IME))
// Word6/J relies on WM_SYSCOMMAND/SC_RESTORE to invalidate their
// MDI childrens' client area. If we just do SetWindowPlacement
// the app leaves its MDI children unpainted. I wanted to patch
// the app but couldn't afford to because the patch will remove
// its optimizing code (will affect performance).
// bug#11258-win95d.
#if !defined(WINNT)
if (ImmGetAppIMECompatFlags(GetWindowThreadProcessId(phap->hwnd, NULL)) & IMECOMPAT_SENDSC_RESTORE)
{
SendMessage(phap->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
continue;
}
#endif
#endif
#ifndef WPF_ASYNCWINDOWPLACEMENT
#define WPF_ASYNCWINDOWPLACEMENT 0x0004
#endif
#ifdef WINNT
// pass this async.
if (!IsHungAppWindow(phap->hwnd))
#endif
{
phap->wp.length = sizeof(WINDOWPLACEMENT);
phap->wp.flags |= WPF_ASYNCWINDOWPLACEMENT;
TraceMsg(TF_TRAY, "rwpt: hwnd=0x%x fRestore=%d ?SWP", phap->hwnd, phap->fRestore);
if (phap->fRestore) {
// only restore those guys we've actually munged.
SetWindowPlacement(phap->hwnd, &phap->wp);
}
}
}
}
ami.iMinAnimate = iAnimate;
SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
DestroySavedWindowPositions(pPositions);
return 1;
}
void RestoreWindowPositions()
{
ENTERCRITICAL;
if (g_ts.pPositions)
{
if (SHCreateThread(RestoreWindowPositionsThread, g_ts.pPositions, 0, NULL))
g_ts.pPositions = NULL;
}
LEAVECRITICAL;
}
void DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions)
{
ENTERCRITICAL;
if (pPositions)
{
// free the global struct
DSA_Destroy(pPositions->hdsaWP);
LocalFree(pPositions);
}
LEAVECRITICAL;
}
void TrayHandleWindowDestroyed(HWND hwnd)
{
// enter critical section so we dont corrupt the hdsaWP
ENTERCRITICAL;
if (g_ts.pPositions)
{
int i = DSA_GetItemCount(g_ts.pPositions->hdsaWP) - 1;
for ( ; i >= 0; i--) {
LPHWNDANDPLACEMENT phap = DSA_GetItemPtr(g_ts.pPositions->hdsaWP, i);
if (phap->hwnd == hwnd || !IsWindow(phap->hwnd)) {
DSA_DeleteItem(g_ts.pPositions->hdsaWP, i);
}
}
if (!DSA_GetItemCount(g_ts.pPositions->hdsaWP))
{
DestroySavedWindowPositions(g_ts.pPositions);
g_ts.pPositions = NULL;
}
}
LEAVECRITICAL;
}
// Allow us to bump the activation of the run dlg hidden window.
// Certain lame apps (Norton Desktop setup) use the active window at RunDlg time
// as the parent for their dialogs. If that window disappears then they fault.
// We don't want the tray to get the activation coz it will cause it to appeare
// if you're in auto-hide mode.
LRESULT CALLBACK RunDlgStaticSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == RDM_ACTIVATE)
{
// there's a window out there... activate it
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
return 0;
}
else if ((uMsg == WM_ACTIVATE) && (wParam == WA_ACTIVE))
{
// Bump the activation to the desktop.
if (v_hwndDesktop)
{
SetForegroundWindow(v_hwndDesktop);
return 0;
}
}
else if (uMsg == WM_NOTIFY)
{
return SendMessage(v_hwndTray, uMsg, wParam, lParam);// relay it to the tray
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
DWORD CALLBACK RunDlgThread(void *data)
{
HWND hwnd;
RECT rc, rcTemp;
MONITORINFO monitorInfo;
CoInitialize(0);
// 99/04/12 #316424 vtan: Get the rectangle for the "Start" button.
// If this is off the screen the tray is probably in auto hide mode.
// In this case offset the rectangle into the monitor where it should
// belong. This may be up, down, left or right depending on the position of the tray.
// First thing to do is to establish the dimensions of the monitor on
// which the tray resides. If no monitor can be found then use the primary monitor.
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfo(g_ts.hmonStuck, &monitorInfo) == 0)
{
TBOOL(SystemParametersInfo(SPI_GETWORKAREA, 0, &monitorInfo.rcMonitor, 0));
}
// Get the co-ordinates of the "Start" button.
GetWindowRect(g_ts.hwndStart, &rc);
// Look for an intersection in the monitor.
if (IntersectRect(&rcTemp, &rc, &monitorInfo.rcMonitor) == 0)
{
LONG lDeltaX, lDeltaY;
// Does not exist in the monitor. Move the co-ordinates by the width or height of the tray so that it does.
// This bizarre arithmetic is used because Tray_ComputeHiddenRect()
// takes into account the frame and that right/bottom of RECT
// is exclusive in GDI.
lDeltaX = g_ts.sStuckWidths.cx - g_cxFrame;
lDeltaY = g_ts.sStuckWidths.cy - g_cyFrame;
if (rc.left < monitorInfo.rcMonitor.left)
{
--lDeltaX;
lDeltaY = 0;
}
else if (rc.top < monitorInfo.rcMonitor.top)
{
lDeltaX = 0;
--lDeltaY;
}
else if (rc.right > monitorInfo.rcMonitor.right)
{
lDeltaX = -lDeltaX;
lDeltaY = 0;
}
else if (rc.bottom > monitorInfo.rcMonitor.bottom)
{
lDeltaX = 0;
lDeltaY = -lDeltaY;
}
TBOOL(OffsetRect(&rc, lDeltaX, lDeltaY));
}
hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("static"), NULL, 0 , rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
if (hwnd) {
// SwitchToThisWindow(hwnd, TRUE);
// SetForegroundWindow(hwnd);
// UINT idPidl;
HANDLE hMemWorkDir = NULL;
LPITEMIDLIST pidlWorkingDir = NULL;
#ifdef WINNT
TCHAR szDir[MAX_PATH];
TCHAR szPath[MAX_PATH];
BOOL fSimple;
#endif
// Subclass it.
SubclassWindow(hwnd, RunDlgStaticSubclassWndProc);
if (data)
SetProp(hwnd, TEXT("WaitingThreadID"), (HANDLE)data);
#ifdef WINNT
// On NT, we like to start apps in the HOMEPATH directory. This
// should be the current directory for the current process.
fSimple = FALSE;
GetEnvironmentVariable(TEXT("HOMEDRIVE"), szDir, ARRAYSIZE(szDir));
GetEnvironmentVariable(TEXT("HOMEPATH"), szPath, ARRAYSIZE(szPath));
if (PathAppend(szDir, szPath) && PathIsDirectory(szDir))
{
pidlWorkingDir = SHSimpleIDListFromPath(szDir);
fSimple = TRUE;
}
#endif
if (!pidlWorkingDir)
{
// If the last active window was a folder/explorer window with the
// desktop as root, use its as the current dir
if (g_ts.hwndLastActive)
{
ENTERCRITICAL;
if (g_ts.hwndLastActive && !IsMinimized(g_ts.hwndLastActive) &&
(Cabinet_IsExplorerWindow(g_ts.hwndLastActive) || Cabinet_IsFolderWindow(g_ts.hwndLastActive)))
{
// BUGBUG: BobDay - If the window is hung, shell hangs!
// Should ask OTRegister for appropriate COMPAREROOT data
hMemWorkDir = (HANDLE)SendMessage(g_ts.hwndLastActive, CWM_CLONEPIDL, GetCurrentProcessId(), 0L);
pidlWorkingDir = SHLockShared(hMemWorkDir, GetCurrentProcessId());
}
LEAVECRITICAL;
}
}
_RunFileDlg(hwnd, 0, pidlWorkingDir, 0, 0, 0);
if (pidlWorkingDir)
{
#ifdef WINNT
if (fSimple)
ILFree(pidlWorkingDir);
else
#endif
{
SHUnlockShared(pidlWorkingDir);
SHFreeShared(hMemWorkDir, GetCurrentProcessId());
}
}
if (data)
RemoveProp(hwnd, TEXT("WaitingThreadID"));
DestroyWindow(hwnd);
}
CoUninitialize();
return TRUE;
}
void Tray_RunDlg()
{
HANDLE hEvent;
void *pvThreadParam;
if (!_Restricted(v_hwndTray, REST_NORUN))
{
TCHAR szRunDlgTitle[MAX_PATH];
HWND hwndOldRun;
LoadString(hinstCabinet, IDS_RUNDLGTITLE, szRunDlgTitle, ARRAYSIZE(szRunDlgTitle));
// See if there is already a run dialog up, and if so, try to activate it
hwndOldRun = FindWindow(WC_DIALOG, szRunDlgTitle);
if (hwndOldRun)
{
DWORD dwPID;
GetWindowThreadProcessId(hwndOldRun, &dwPID);
if (dwPID == GetCurrentProcessId())
{
if (IsWindowVisible(hwndOldRun))
{
SetForegroundWindow(hwndOldRun);
return;
}
}
}
// Create an event so we can wait for the run dlg to appear before
// continue - this allows it to capture any type-ahead.
hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("MSShellRunDlgReady"));
if (hEvent)
pvThreadParam = (void *)GetCurrentThreadId();
else
pvThreadParam = NULL;
if (SHQueueUserWorkItem(RunDlgThread, pvThreadParam, 0, (DWORD_PTR)0, (DWORD_PTR)NULL, NULL, TPS_LONGEXECTIME | TPS_DEMANDTHREAD))
{
if (hEvent)
{
MsgWaitForMultipleObjectsLoop(hEvent, 10*1000);
DebugMsg(DM_TRACE, TEXT("c.t_rd: Done waiting."));
}
}
if (hEvent)
CloseHandle(hEvent);
}
}
DWORD MsgWaitForMultipleObjectsLoop(HANDLE hEvent, DWORD dwTimeout)
{
while (1)
{
MSG msg;
// NB We need to let the run dialog become active so we have to handle sent
// messages but we don't want to handle any input events or we'll swallow the type-ahead.
// NOTE: If hEvent never signals and we get at least one message every dwTimeout
// milliseconds then we will never timeout and could hang in this function. If
// this ever becomes a problem then see the shell32 function SHProcessMessagesUntilEvent of one of many similar examples for the solution.
DWORD dwObject = MsgWaitForMultipleObjects(1, &hEvent, FALSE, dwTimeout, QS_SENDMESSAGE);
// Are we done waiting?
switch (dwObject) {
case WAIT_OBJECT_0:
case WAIT_FAILED:
return dwObject;
case WAIT_TIMEOUT:
return WAIT_TIMEOUT;
case WAIT_OBJECT_0 + 1:
// This PeekMessage has the side effect of processing any broadcast messages.
// It doesn't matter what message we actually peek for but if we don't peek then other threads that have sent broadcast sendmessages will hang until
// hEvent is signaled. Since the process we're waiting on could be the one that sent the broadcast message that could cause a deadlock otherwise.
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
break;
}
}
// never gets here
// return dwObject;
}
void ExploreCommonStartMenu(BOOL bExplore)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
TCHAR szPath[MAX_PATH];
TCHAR szCmdLine[MAX_PATH + 50];
// Get the common start menu path.
// On NT we want to force the directory to exist, but not on W95 machines
#ifdef WINNT
if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_STARTMENU, FALSE)) {
#else
if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_STARTMENU, TRUE)) {
#endif
return;
}
// If we are starting in explorer view, then the command line
// has a "/e, " before the quoted diretory.
if (bExplore) {
lstrcpy (szCmdLine, TEXT("explorer.exe /e, \""));
} else {
lstrcpy (szCmdLine, TEXT("explorer.exe \""));
}
lstrcat (szCmdLine, szPath);
lstrcat (szCmdLine, TEXT("\""));
// Initialize process startup info
si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpTitle = NULL;
si.lpDesktop = NULL;
si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
si.lpReserved2 = NULL;
si.cbReserved2 = 0;
// Start explorer
if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {
// Close the process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
#ifdef FULL_DEBUG
typedef struct tagDCTPFINO
{
UINT idCmd;
HWND hwndOwner;
} DCTPINFO;
DWORD WINAPI DebugCommandThreadProc(void *pv)
{
DCTPINFO * pinfo = (DCTPINFO *)pv;
switch (pinfo->idCmd)
{
case IDM_DEBUGURLDB:
// Inproc debugging of URL database
InvokeURLDebugDlg(pinfo->hwndOwner);
break;
}
LocalFree(pinfo);
return 0;
}
void Tray_OnDebugCommand(UINT idCmd)
{
DCTPINFO * pinfo;
switch (idCmd)
{
case IDM_DEBUGURLDB:
pinfo = LocalAlloc(LPTR, sizeof(*pinfo));
if (pinfo)
{
pinfo->hwndOwner = NULL;
pinfo->idCmd = idCmd;
SHCreateThread(DebugCommandThreadProc, pinfo, CTF_INSIST, NULL);
}
break;
}
}
void Tray_AddDebugMenu(HMENU hmenu)
{
HMENU hmenuSrc = LoadMenu(hinstCabinet, MAKEINTRESOURCE(MENU_DEBUG));
if (hmenuSrc)
{
Shell_MergeMenus(hmenu, hmenuSrc, (UINT)-1, 0, (UINT)-1, MM_ADDSEPARATOR);
DestroyMenu(hmenuSrc);
}
}
#endif // FULL_DEBUG
void StartMenuFolder_ContextMenu(DWORD dwPos)
{
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(v_hwndTray, CSIDL_STARTMENU, TRUE);
INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT_START);
Tray_HandleFullScreenApp(NULL);
SetForegroundWindow(g_ts.hwndMain);
if (pidlStart)
{
LPITEMIDLIST pidlLast = ILClone(ILFindLastID(pidlStart));
ILRemoveLastID(pidlStart);
if (pidlLast)
{
IShellFolder *psf = BindToFolder(pidlStart);
if (psf)
{
HMENU hmenu = CreatePopupMenu();
if (hmenu)
{
IContextMenu *pcm;
HRESULT hres = psf->lpVtbl->GetUIObjectOf(psf, v_hwndTray, 1, &pidlLast, &IID_IContextMenu, NULL, &pcm);
if (SUCCEEDED(hres))
{
hres = pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, IDSYSPOPUP_FIRST, IDSYSPOPUP_LAST, CMF_VERBSONLY);
if (SUCCEEDED(hres))
{
int idCmd;
if (!SHRestricted(REST_NOCOMMONGROUPS))
{
// If the user has access to the Common Start Menu, then we can add those items. If not,
// then we should not.
TCHAR szCommon[MAX_PATH];
BOOL fAddCommon = (S_OK == SHGetFolderPath(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, szCommon));
if (fAddCommon)
fAddCommon = IsUserAnAdmin();
// Since we don't show this on the start button when the user is not an admin, don't show it here... I guess...
if (fAddCommon)
{
AppendMenu (hmenu, MF_SEPARATOR, 0, NULL);
LoadString (hinstCabinet, IDS_OPENCOMMON, szCommon, ARRAYSIZE(szCommon));
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_OPENCOMMON, szCommon);
LoadString (hinstCabinet, IDS_EXPLORECOMMON, szCommon, ARRAYSIZE(szCommon));
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_EXPLORECOMMON, szCommon);
}
}
#ifdef FULL_DEBUG
// Add the Debug cascaded menu to the context
// menu
Tray_AddDebugMenu(hmenu);
#endif
if (dwPos == (DWORD)-1)
{
idCmd = Tray_TrackMenu(hmenu);
}
else
{
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
idCmd = TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, v_hwndTray, NULL);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
}
if (idCmd)
{
if (idCmd == IDSYSPOPUP_OPENCOMMON)
{
ExploreCommonStartMenu(FALSE);
}
else if (idCmd == IDSYSPOPUP_EXPLORECOMMON)
{
ExploreCommonStartMenu(TRUE);
}
#ifdef FULL_DEBUG
else if (InRange(idCmd, IDSYSPOPUP_DEBUGFIRST, IDSYSPOPUP_DEBUGLAST))
{
Tray_OnDebugCommand(idCmd);
}
#endif
else
{
TCHAR szPath[MAX_PATH];
CMINVOKECOMMANDINFOEX ici;
#ifdef UNICODE
CHAR szPathAnsi[MAX_PATH];
#endif
ZeroMemory(&ici, SIZEOF(CMINVOKECOMMANDINFOEX));
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
// ici.fMask = 0;
ici.hwnd = v_hwndTray;
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - IDSYSPOPUP_FIRST);
// ici.lpParameters = NULL;
// ici.lpDirectory = NULL;
ici.nShow = SW_NORMAL;
#ifdef UNICODE
SHGetPathFromIDListA(pidlStart, szPathAnsi);
SHGetPathFromIDList(pidlStart, szPath);
ici.lpDirectory = szPathAnsi;
ici.lpDirectoryW = szPath;
ici.fMask |= CMIC_MASK_UNICODE;
#else
SHGetPathFromIDList(pidlStart, szPath);
ici.lpDirectory = szPath;
#endif
pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&ici);
}
}
}
pcm->lpVtbl->Release(pcm);
}
DestroyMenu(hmenu);
}
psf->lpVtbl->Release(psf);
}
ILFree(pidlLast);
}
ILFree(pidlStart);
}
}
#ifdef WINNT
// RunSystemMonitor
// Launches system monitor (taskmgr.exe), which is expected to be able to find any currently running instances of itself
void RunSystemMonitor(void)
{
STARTUPINFO startup;
PROCESS_INFORMATION pi;
TCHAR szName[] = TEXT("taskmgr.exe");
startup.cb = SIZEOF(startup);
startup.lpReserved = NULL;
startup.lpDesktop = NULL;
startup.lpTitle = NULL;
startup.dwFlags = 0L;
startup.cbReserved2 = 0;
startup.lpReserved2 = NULL;
startup.wShowWindow = SW_SHOWNORMAL;
// Used to pass "taskmgr.exe" here, but NT faulted in CreateProcess
// Probably they are wacking on the command line, which is bogus, but
// then again the paremeter is not marked const...
if (CreateProcess(NULL, szName, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
#endif
void GiveDesktopFocus()
{
SetForegroundWindow(v_hwndDesktop);
SendMessage(v_hwndDesktop, DTM_UIACTIVATEIO, (WPARAM) TRUE, /*dtb*/0);
}
void FlushMessages()
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*
Purpose: loads the given resource string and executes it.
The resource string should follow this format:
"program.exe>parameters"
If there are no parameters, the format should simply be:
"program.exe"
*/
void _ExecResourceCmd(UINT ids)
{
TCHAR szCmd[2*MAX_PATH];
if (LoadString(hinstCabinet, ids, szCmd, SIZECHARS(szCmd)))
{
SHELLEXECUTEINFO sei = {0};
// Find list of parameters (if any)
LPTSTR pszParam = StrChr(szCmd, TEXT('>'));
if (pszParam)
{
// Replace the '>' with a null terminator
*pszParam = 0;
pszParam++;
}
sei.cbSize = sizeof(sei);
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = szCmd;
sei.lpParameters = pszParam;
ShellExecuteEx(&sei);
}
}
void RefreshStartMenu()
{
// BUGBUG (lamadio): We should use IUnknown_Exec, but we need to extern "c" it...
IOleCommandTarget* poct;
if (g_ts._pmbStartMenu && SUCCEEDED(g_ts._pmbStartMenu->lpVtbl->QueryInterface(g_ts._pmbStartMenu, &IID_IOleCommandTarget, (void**)&poct) ))
{
poct->lpVtbl->Exec(poct, &CLSID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
poct->lpVtbl->Release(poct);
}
}
void Tray_Command(UINT idCmd)
{
INSTRUMENT_ONCOMMAND(SHCNFI_TRAYCOMMAND, g_ts.hwndMain, idCmd);
UEMFireEvent(&UEMIID_SHELL, UEME_RUNWMCMD, UEMF_XEVENT, UIM_EXPLORER, idCmd);
switch (idCmd)
{
case IDM_CONTROLS:
case IDM_PRINTERS:
ShowFolder(g_ts.hwndMain, idCmd == IDM_CONTROLS ? CSIDL_CONTROLS : CSIDL_PRINTERS, COF_USEOPENSETTINGS);
break;
case IDM_EJECTPC:
DoEjectPC(); //From apithk.c
break;
case IDM_LOGOFF:
// Let the desktop get a chance to repaint to get rid of the
// start menu bits before bringing up the logoff dialog box.
Sleep(100);
_SaveTrayAndDesktop();
LogoffWindowsDialog(v_hwndDesktop);
break;
case IDM_EXITWIN:
DoExitWindows(v_hwndDesktop);
break;
case IDM_RAISEDESKTOP:
ToggleDesktop();
break;
case IDM_FILERUN:
Tray_RunDlg();
break;
case IDM_MINIMIZEALLHOTKEY:
Tray_HandleGlobalHotkey(GHID_MINIMIZEALL);
break;
#ifdef DEBUG
case IDM_SIZEUP:
{
RECT rcView;
GetWindowRect(g_ts.hwndRebar, &rcView);
MapWindowPoints(HWND_DESKTOP, g_ts.hwndMain, (LPPOINT)&rcView, 2);
rcView.bottom -= 18;
SetWindowPos(g_ts.hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
}
break;
case IDM_SIZEDOWN:
{
RECT rcView;
GetWindowRect(g_ts.hwndRebar, &rcView);
MapWindowPoints(HWND_DESKTOP, g_ts.hwndMain, (LPPOINT)&rcView, 2);
rcView.bottom += 18;
SetWindowPos(g_ts.hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
}
break;
#endif
case IDM_MINIMIZEALL:
// minimize all window
MinimizeAll(g_ts.hwndView);
g_ts.fUndoEnabled = TRUE;
break;
case IDM_UNDO:
RestoreWindowPositions();
break;
case IDM_SETTIME:
// run the default applet in timedate.cpl
SHRunControlPanel( TEXT("timedate.cpl"), g_ts.hwndMain );
break;
#ifdef WINNT
case IDM_SHOWTASKMAN:
RunSystemMonitor();
break;
#endif
case IDM_CASCADE:
case IDM_VERTTILE:
case IDM_HORIZTILE:
if (CanTileAnyWindows())
{
SaveWindowPositions((idCmd == IDM_CASCADE)? IDS_CASCADE : IDS_TILE);
AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
if (idCmd == IDM_CASCADE)
{
CascadeWindows(GetDesktopWindow(), 0, NULL, 0, NULL);
}
else
{
TileWindows(GetDesktopWindow(), ((idCmd == IDM_VERTTILE)? MDITILE_VERTICAL : MDITILE_HORIZONTAL), NULL, 0, NULL);
}
// do it *before* ABN_xxx so don't get 'indirect' moves
// BUGBUG or should it be after?
// CheckWindowPositions();
g_ts.fUndoEnabled = FALSE;
SetTimer(v_hwndTray, IDT_ENABLEUNDO, 500, NULL);
AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
}
break;
case IDM_TRAYPROPERTIES:
DoTrayProperties(0);
break;
case IDM_SETTINGSASSIST:
SHCreateThread(SettingsUI_ThreadProc, NULL, 0, NULL);
break;
case IDM_HELPSEARCH:
if (g_bRunOnMemphis || g_bRunOnNT5)
_ExecResourceCmd(IDS_HELP_CMD);
else
WinHelp(g_ts.hwndMain, TEXT("windows.hlp"), HELP_FINDER, 0);
break;
// NB The Alt-s comes in here.
case IDC_KBSTART:
SetForegroundWindow(g_ts.hwndMain);
// This pushes the start button and causes the start menu to popup.
SendMessage(g_ts.hwndStart, BM_SETSTATE, TRUE, 0 );
// This forces the button back up.
SendMessage(g_ts.hwndStart, BM_SETSTATE, FALSE, 0);
break;
case IDC_ASYNCSTART:
KillConfigDesktopDlg();
#if 0 // (for testing UAssist locking code)
UEMFireEvent(&UEMIID_SHELL, UEME_DBSLEEP, UEMF_XEVENT, -1, (LPARAM)10000);
#endif
#ifdef DEBUG
if (GetAsyncKeyState(VK_SHIFT) < 0)
{
UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, TRUE, -1);
RefreshStartMenu();
}
#endif
// Make sure the button is down.
// DebugMsg(DM_TRACE, "c.twp: IDC_START.");
// Make sure the Start button is down.
if (!g_ts.bMainMenuInit && SendMessage(g_ts.hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
{
// DebugMsg(DM_TRACE, "c.twp: Start button down.");
// Set the focus.
Tray_SetFocus(g_ts.hwndStart);
ToolbarMenu();
}
break;
// NB LButtonDown on the Start button come in here.
// Space-bar stuff also comes in here.
case IDC_START:
// User gets a bit confused with space-bar tuff (the popup ends up
// getting the key-up and beeps).
PostMessage(g_ts.hwndMain, WM_COMMAND, IDC_ASYNCSTART, 0);
break;
case FCIDM_FINDFILES:
SHFindFiles(NULL, NULL);
break;
case FCIDM_FINDCOMPUTER:
SHFindComputer(NULL, NULL);
break;
case FCIDM_REFRESH:
RefreshStartMenu();
break;
case FCIDM_NEXTCTL:
{
MSG msg = { 0, WM_KEYDOWN, VK_TAB };
HWND hwndFocus = GetFocus();
int dtb;
// Since we are Tab or Shift Tab we should turn the focus rect on.
// Note: we don't need to do this in the GiveDesktopFocus cases below,
// but in those cases we're probably already in the UIS_CLEAR UISF_HIDEFOCUS
// state so this message is cheap to send.
SendMessage(g_ts.hwndMain, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
dtb = GetAsyncKeyState(VK_SHIFT) < 0 ? -1 : 1;
if (hwndFocus && (IsChildOrHWND(g_ts.hwndStart, hwndFocus))) {
if (dtb == -1) {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL start => deact");
// gotta deactivate manually
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL deact");
GiveDesktopFocus();
}
else {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL start -> bs");
UnkUIActivateIO(g_ts.ptbs, TRUE, &msg);
}
} else if (hwndFocus && (IsChildOrHWND(g_ts.hwndNotify, hwndFocus))) {
if (dtb == -1) {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL notifies=>bs");
UnkUIActivateIO(g_ts.ptbs, TRUE, &msg);
} else {
if (g_hwndDesktopTB && g_ts.fShowDeskBtn)
{
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL notifies->desk btn");
Tray_SetFocus(g_hwndDesktopTB);
}
else
{
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL notifies->deact");
GiveDesktopFocus();
}
}
} else if (hwndFocus && g_hwndDesktopTB && (IsChildOrHWND(g_hwndDesktopTB, hwndFocus))) {
if (dtb == -1) {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL desk btn=>notifies");
Tray_SetFocus(g_ts.hwndNotify);
} else {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL desk btn->deact");
GiveDesktopFocus();
}
} else {
if (UnkTranslateAcceleratorIO(g_ts.ptbs, &msg) != S_OK) {
if (dtb == -1) {
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL bs => start");
TraceMsg(DM_FOCUS, "tiois: TM_UIActIO fake UIAct (shift NYI)");
Tray_SetFocus(g_ts.hwndStart);
}
else {
// if you tab forward out of the bands, the next focus guy is the tray notify set
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL bs -> notify");
Tray_SetFocus(g_ts.hwndNotify);
}
}
else
{
if (dtb == -1)
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL bs => bs");
else
TraceMsg(DM_TRACE, "t FCIDM_NEXTCTL bs -> bs");
}
}
break;
}
#ifdef WINNT // hydra specific menu items
case IDM_MU_SECURITY:
MuSecurity();
break;
#endif
default:
if (IsInRange(idCmd, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST) && ! _Restricted(v_hwndTray, REST_NOFIND))
{
CMINVOKECOMMANDINFOEX ici;
ZeroMemory(&ici, SIZEOF(CMINVOKECOMMANDINFOEX));
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
ici.hwnd = g_ts.hwndMain;
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - TRAY_IDM_FINDFIRST);
ici.nShow = SW_NORMAL;
g_ts.pcmFind->lpVtbl->InvokeCommand(g_ts.pcmFind, (LPCMINVOKECOMMANDINFO)&ici);
}
else
{
DebugMsg(TF_WARNING, TEXT("tray Unknown command (%x)"), idCmd);
}
break;
}
}
// Start menu/Tray tab as a drop target
extern const IDropTarget c_dtgtStart; // forward
extern const IDropTarget c_dtgtTray; // forward
// globals used to all of the 3 drop targets to do switching
int g_iDropItem = -2;
DWORD g_dwTriggerTime = 0;
HRESULT GetStartMenuDropTarget(IDropTarget** pptgt)
{
HRESULT hres = E_FAIL;
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE);
*pptgt = NULL;
if (pidlStart)
{
IShellFolder *psf = BindToFolder(pidlStart);
if (psf)
{
hres = psf->lpVtbl->CreateViewObject(psf, v_hwndTray, &IID_IDropTarget, pptgt);
psf->lpVtbl->Release(psf);
}
ILFree(pidlStart);
}
return hres;
}
STDMETHODIMP CStartDropTarget_QueryInterface(LPDROPTARGET pdtgt, REFIID riid, void ** ppvObj)
{
if (IsEqualIID(riid, &IID_IDropTarget) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = pdtgt;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
// AddRef() not needed, static object.
return S_OK;
}
STDMETHODIMP_(ULONG) CStartDropTarget_AddRef(LPDROPTARGET pdtgt)
{
return 2;
}
STDMETHODIMP_(ULONG) CStartDropTarget_Release(LPDROPTARGET pdtgt)
{
return 1;
}
STDMETHODIMP CStartDropTarget_DragEnter(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
HWND hwndLock = v_hwndTray; // no clippy
POINT pt;
RECT rc;
IDropTarget* ptgt;
if (SUCCEEDED(GetStartMenuDropTarget(&ptgt)))
{
TraceMsg(TF_TRAY, "CStartDropTarget::DragEnter called");
TraySetUnhideTimer(ptl.x, ptl.y);
pt.x = ptl.x;
pt.y = ptl.y;
// Check to make sure that we're going to accept the drop before we expand the start menu.
ptgt->lpVtbl->DragEnter(ptgt, pdtobj, grfKeyState, ptl, pdwEffect);
// DROPEFFECT_NONE means it ain't gonna work, so don't popup the Start Menu.
// BUGBUG (lamadio): I assume that all shell folders benieth the Start Menu are
// the same type of shell folder as the Fast items portion. This might not be true.
if (*pdwEffect != DROPEFFECT_NONE)
{
GetWindowRect(g_ts.hwndStart, &rc);
if (PtInRect(&rc,pt))
{
//Make sure it really is in the start menu..
SetTimer(v_hwndTray, IDT_STARTMENU, 1000, NULL);
}
}
ptgt->lpVtbl->DragLeave(ptgt);
ptgt->lpVtbl->Release(ptgt);
}
_DragEnter(hwndLock, ptl, pdtobj);
g_iDropItem = -2; // reset to no target
return S_OK;
}
STDMETHODIMP CStartDropTarget_DragOver(LPDROPTARGET pdtgt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
TraySetUnhideTimer(ptl.x, ptl.y);
_DragMove(g_ts.hwndStart, ptl);
*pdwEffect = DROPEFFECT_LINK;
return S_OK;
}
extern int Task_HitTest(HWND hwndTask, POINTL ptl);
extern void Task_SetCurSel(HWND hwndTask, int i);
STDMETHODIMP CTabDropTarget_DragEnter(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
DWORD dwEffect = *pdwEffect;
BandSite_DragEnter(g_ts.ptbs, pdtobj, grfKeyState, ptl, &dwEffect);
return CStartDropTarget_DragEnter( pdtgt, pdtobj, grfKeyState, ptl, pdwEffect);
}
STDMETHODIMP CTabDropTarget_DragLeave(LPDROPTARGET pdtgt)
{
BandSite_DragLeave(g_ts.ptbs);
DAD_DragLeave();
return S_OK;
}
STDMETHODIMP CTabDropTarget_DragOver(LPDROPTARGET pdtgt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
int iHitNew;
iHitNew = Task_HitTest(g_ts.hwndView, ptl);
if (iHitNew == -1) {
DWORD dwEffect = *pdwEffect;
BandSite_DragOver(g_ts.ptbs, grfKeyState, ptl, &dwEffect);
}
CStartDropTarget_DragOver(pdtgt, grfKeyState, ptl, pdwEffect);
if (g_iDropItem != iHitNew)
{
TraceMsg(TF_TRAY, "CTDT::HitTest new target (%d->%d)", g_iDropItem, iHitNew);
g_iDropItem = iHitNew;
g_dwTriggerTime = GetCurrentTime() + 1000;
if (iHitNew == -1) {
g_dwTriggerTime += 1000; // make a little longer for minimize all
}
}
else if (GetCurrentTime() > g_dwTriggerTime)
{
TraceMsg(TF_TRAY, "CTDT::doing event (%d)", g_iDropItem);
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
if (g_iDropItem == -1)
RaiseDesktop();
else
{
Task_SetCurSel(g_ts.hwndView, g_iDropItem);
UpdateWindow(v_hwndTray);
}
DAD_ShowDragImage(TRUE); // restore the lock state.
g_dwTriggerTime += 10000; // don't let this happen again for 10 seconds
// simulate a single shot event
}
if (g_iDropItem != -1)
*pdwEffect = DROPEFFECT_MOVE; // try to get the move cursor
else
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
STDMETHODIMP CStartDropTarget_DragLeave(LPDROPTARGET pdtgt)
{
TraceMsg(TF_TRAY, "CStartDropTarget::DragLeave called");
DAD_DragLeave();
KillTimer(v_hwndTray, IDT_STARTMENU);
return S_OK;
}
STDMETHODIMP CStartDropTarget_Drop(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
HRESULT hres = E_OUTOFMEMORY;
IDropTarget* pdrop;
KillTimer(v_hwndTray, IDT_STARTMENU);
hres = GetStartMenuDropTarget(&pdrop);
if (SUCCEEDED(hres))
{
POINTL pt = { 0, 0 };
DWORD grfKeyState = 0;
*pdwEffect &= DROPEFFECT_LINK;
pdrop->lpVtbl->DragEnter(pdrop, pdtobj, grfKeyState, pt, pdwEffect);
hres = pdrop->lpVtbl->Drop(pdrop, pdtobj, grfKeyState, pt, pdwEffect);
pdrop->lpVtbl->DragLeave(pdrop);
pdrop->lpVtbl->Release(pdrop);
}
DAD_DragLeave();
return hres;
}
STDMETHODIMP CTabDropTarget_Drop(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
HWND hwndPt;
POINT pt = {ptl.x, ptl.y};
BandSite_DragLeave(g_ts.ptbs);
DAD_DragLeave();
hwndPt = WindowFromPoint(pt);
if (!IsChildOrHWND(g_ts.hwndRebar, hwndPt)) {
// if it was dropped on us somewhere other than in the tasks window
// then send it to bandsite
return BandSite_SimulateDrop(g_ts.ptbs, pdtobj, grfKeyState, ptl, pdwEffect);
}
// post ourselves a message to have the tray put up a message box to
// explain that you can't drag to the taskbar. we need to return from
// the Drop method now so the DragSource isn't hung while our box is up
PostMessage(v_hwndTray, TM_WARNNODROP, 0, 0L);
// be sure to clear DROPEFFECT_MOVE so apps don't delete their data
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
IDropTarget* Tray_GetDropTarget()
{
return (IDropTarget*)&c_dtgtTray;
}
// store our drop target in pdtgtTree
// CStartDropTarget : Register and Revoke
void CStartDropTarget_Register()
{
THR(RegisterDragDrop(g_ts.hwndStart, (IDropTarget *)&c_dtgtStart));
THR(RegisterDragDrop(g_ts.hwndMain, (IDropTarget*)&c_dtgtTray));
}
void CStartDropTarget_Revoke()
{
RevokeDragDrop(g_ts.hwndMain);
RevokeDragDrop(g_ts.hwndStart);
}
// CStartDropTarget : VTable
const IDropTargetVtbl c_CStartDropTargetVtbl = {
CStartDropTarget_QueryInterface, CStartDropTarget_AddRef, CStartDropTarget_Release,
CStartDropTarget_DragEnter,
CStartDropTarget_DragOver,
CStartDropTarget_DragLeave,
CStartDropTarget_Drop
};
const IDropTargetVtbl c_CTabDropTargetVtbl = {
CStartDropTarget_QueryInterface, CStartDropTarget_AddRef, CStartDropTarget_Release,
CTabDropTarget_DragEnter,
CTabDropTarget_DragOver,
CTabDropTarget_DragLeave,
CTabDropTarget_Drop
};
const IDropTarget c_dtgtStart = { (IDropTargetVtbl *)&c_CStartDropTargetVtbl };
const IDropTarget c_dtgtTray = { (IDropTargetVtbl *)&c_CTabDropTargetVtbl };
// end Start Drop target
// Begin Print Notify stuff
typedef BOOL (* PFNENUMJOBS) (HANDLE, DWORD, DWORD, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
typedef BOOL (* PFNOPENPRINTER) (LPTSTR, LPHANDLE, void *);
typedef BOOL (* PFNCLOSEPRINTER) (HANDLE);
PFNENUMJOBS g_pfnEnumJobs = NULL;
PFNOPENPRINTER g_pfnOpenPrinter = NULL;
PFNCLOSEPRINTER g_pfnClosePrinter = NULL;
void PrintNotify_Init(HWND hwnd)
{
g_ts.pidlPrintersFolder = SHCloneSpecialIDList(hwnd, CSIDL_PRINTERS, FALSE);
if (g_ts.pidlPrintersFolder)
{
SHChangeNotifyEntry fsne;
HWND hwndSpool;
fsne.pidl = g_ts.pidlPrintersFolder;
fsne.fRecursive = TRUE;
TraceMsg(TF_TRAY, "PRINTNOTIFY - registering print notify icon");
g_ts.uPrintNotify = SHChangeNotifyRegister(hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel, SHCNE_CREATE | SHCNE_UPDATEITEM | SHCNE_DELETE, WMTRAY_PRINTCHANGE, 1, &fsne);
// Tell the spool subsystem to stop waiting for the printnotify icon..
// After this notification, the subsystem can start printing and our
// little printer icon will work. We need this so that deferred print jobs don't start printing before the icon is listening.
hwndSpool = FindWindow(TEXT("SpoolProcessClass"), NULL);
if (hwndSpool)
{
NMHDR nmhdr = {hwnd, 0, NM_ENDWAIT};
TraceMsg(TF_TRAY, "PRINTNOTIFY - Telling print subsystem we're here");
SendMessage(hwndSpool, WM_NOTIFY, (WPARAM)NM_ENDWAIT, (LPARAM)&nmhdr);
}
}
}
typedef BOOL (*ENUMPROP)(void *lpData, HANDLE hPrinter, DWORD dwLevel, LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum);
void *Printer_EnumProps(HANDLE hPrinter, DWORD dwLevel, DWORD *lpdwNum, ENUMPROP lpfnEnum, void *lpData)
{
DWORD dwSize, dwNeeded;
LPBYTE pEnum;
dwSize = 0;
SetLastError(0);
lpfnEnum(lpData, hPrinter, dwLevel, NULL, 0, &dwSize, lpdwNum);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
pEnum = NULL;
goto Error1;
}
ASSERT(dwSize < 0x100000L);
TryAgain:
pEnum = LocalAlloc(LPTR, dwSize);
if (!pEnum)
{
goto Error1;
}
SetLastError(0);
if (!lpfnEnum(lpData, hPrinter, dwLevel, pEnum, dwSize, &dwNeeded, lpdwNum))
{
LocalFree(pEnum);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
dwSize = dwNeeded;
goto TryAgain;
}
pEnum = NULL;
}
Error1:
return(pEnum);
}
BOOL _EnumJobsCB(void *lpData, HANDLE hPrinter, DWORD dwLevel, LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return(g_pfnEnumJobs(hPrinter, 0, 0xffff, dwLevel, pEnum, dwSize, lpdwNeeded, lpdwNum));
}
DWORD _EnumJobs(HANDLE hPrinter, DWORD dwLevel, void **ppJobs)
{
DWORD dwNum = 0L;
*ppJobs = Printer_EnumProps(hPrinter, dwLevel, &dwNum, _EnumJobsCB, NULL);
if (*ppJobs==NULL)
{
dwNum=0;
}
return(dwNum);
}
// Thread_PrinterPoll is the worker thread for PrintNotify_HandleFSNotify.
// It keeps track of print jobs, polls the pinter, updates the icon, etc.
typedef struct _PRINTERPOLLDATA
{
HWND hwnd;
HANDLE heWakeUp;
} PRINTERPOLLDATA, *PPRINTERPOLLDATA;
DWORD WINAPI Thread_PrinterPoll(PPRINTERPOLLDATA pData)
{
PRINTERPOLLDATA data;
HANDLE hmodWinspool;
TCHAR szUserName[40];
TCHAR szFormat[60];
DWORD dwSize;
DWORD_PTR aArgs[2];
NOTIFYICONDATA IconData = {0};
DWORD dwNotifyMessage = NIM_ADD;
UINT nIconShown = 0;
HICON hIconNormal = NULL;
HICON hIconError = NULL;
HANDLE haPrinterNames = NULL;
LPSHELLFOLDER psf = NULL;
DWORD dwTimeOut;
DWORD dwStatus;
int nFound;
MSG msg;
LPITEMIDLIST pidlFree = NULL;
static int s_cxSmIcon = 0;
static int s_cySmIcon = 0;
int cxSmIcon = 0;
int cySmIcon = 0;
// notify PrintNotify_StartThread that our message queue is up
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
data = *pData;
GlobalFree(pData);
SetEvent(data.heWakeUp);
// load winspool functions we use
hmodWinspool = LoadLibrary(TEXT("winspool.drv"));
if (!hmodWinspool)
{
goto Error1;
}
// These are globals just so I don't have to pass them around
#ifdef UNICODE
g_pfnEnumJobs = (PFNENUMJOBS) GetProcAddress(hmodWinspool, "EnumJobsW");
g_pfnOpenPrinter = (PFNOPENPRINTER) GetProcAddress(hmodWinspool, "OpenPrinterW");
#else
g_pfnEnumJobs = (PFNENUMJOBS) GetProcAddress(hmodWinspool, "EnumJobsA");
g_pfnOpenPrinter = (PFNOPENPRINTER) GetProcAddress(hmodWinspool, "OpenPrinterA");
#endif
g_pfnClosePrinter = (PFNCLOSEPRINTER)GetProcAddress(hmodWinspool, "ClosePrinter");
if (!g_pfnEnumJobs || !g_pfnOpenPrinter || !g_pfnClosePrinter)
{
ASSERT(FALSE);
goto Error2;
}
// Get fixed data
dwSize = ARRAYSIZE(szUserName);
if (!GetUserName(szUserName, &dwSize))
{
// non repro bug 6853/5592 is probably caused by GetUserName failing.
// since this bug gets reported occasionally, see if this fixes it.
// Note: we may want to change the tip message to not include this empty user name.
TraceMsg(TF_TRAY, "GetUserName failed!");
ASSERT(0); // why would this fail?
szUserName[0] = TEXT('\0');
}
LoadString(hinstCabinet, IDS_NUMPRINTJOBS, szFormat, ARRAYSIZE(szFormat));
aArgs[0] = (DWORD_PTR)-1;
aArgs[1] = (DWORD_PTR)szUserName;
// Set up initial data
IconData.cbSize = SIZEOF(IconData);
IconData.hWnd = data.hwnd;
//IconData.uID = 0;
IconData.uFlags = NIF_MESSAGE;
IconData.uCallbackMessage = WMTRAY_PRINTICONNOTIFY;
// IconData.hIcon filled in when message sent
// IconData.szTip filled in when message sent
dwNotifyMessage = NIM_ADD; // next operation for the icon
nIconShown = 0; // icon id of printer icon currently displayed
hIconNormal = NULL; // handle of ICO_PRINTER
hIconError = NULL; // handle of ICO_PRINTER_ERROR
haPrinterNames = NULL; // list of currently polled printers
psf = NULL; // IShellFolder of Printers Folder
dwTimeOut = POLLINTERVAL; // time before next poll
nFound = 0; // # jobs found after most recent poll
dwStatus = 0; // Status after most recent poll
// process messages
for ( ; TRUE ; )
{
int iPrinter, iJob;
JOB_INFO_1 *pJobs;
HANDLE hPrinter;
DWORD dwTime; // elapsed time for this wait
DWORD dw; // return value from this wait
dwTime = GetTickCount();
dw = MsgWaitForMultipleObjects(1, &(data.heWakeUp), FALSE, dwTimeOut, QS_ALLINPUT);
dwTime = GetTickCount() - dwTime;
switch (dw)
{
case WAIT_OBJECT_0:
case WAIT_FAILED:
// the tray is shutting down
TraceMsg(TF_TRAY, "PRINTNOTIFY - Thread_PrinterPoll shutting down");
goto exit;
case WAIT_TIMEOUT:
#ifndef WINNT
if (g_ts.uAutoHide & AH_HIDING)
{
// don't poll if the tray is hidden
dwTimeOut = MINPOLLINTERVAL;
break;
}
#endif
TraceMsg(TF_TRAY, "PRINTNOTIFY - Thread_PrinterPoll timeout polling");
// we need to poll -- jump into the message loop
pidlFree = NULL;
goto poll;
default:
// we have messages
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
switch (msg.message - WM_USER)
{
// NOTE: These can't (& don't) conflict w/ our SHCNE_ messages
case WM_LBUTTONDBLCLK:
case WM_RBUTTONUP:
{
int idCmd, idDefCmd = IDM_PRINTNOTIFY_FOLDER;
HMENU hmContext = LoadMenuPopup(MAKEINTRESOURCE(MENU_PRINTNOTIFYCONTEXT));
if (hmContext)
{
// Append each printer we're polling
if (haPrinterNames)
{
MENUITEMINFO mii;
int i;
TCHAR szTemplate[60];
// If error, we'll need szTemplate
if (dwStatus & JOB_STATUS_ERROR_BITS)
{
LoadString(hinstCabinet, IDS_PRINTER_ERROR, szTemplate, ARRAYSIZE(szTemplate));
}
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_TYPE | MIIM_ID;
mii.fType = MF_STRING;
InsertMenu(hmContext, (UINT)-1, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
for (i = DSA_GetItemCount(haPrinterNames) - 1 ; i >= 0 ; --i)
{
TCHAR szBuf[MAXPRINTERBUFFER+60];
LPTSTR pName;
LPPRINTERNAME pPrinter = DSA_GetItemPtr(haPrinterNames, i);
// give it an id
mii.wID = i+IDM_PRINTNOTIFY_FOLDER+1;
// indicate error state via name
pName = pPrinter->szPrinterName;
if (pPrinter->fInErrorState)
{
wsprintf(szBuf, szTemplate, pName);
pName = szBuf;
// default to first error state printer
if (idDefCmd == IDM_PRINTNOTIFY_FOLDER)
idDefCmd = mii.wID;
}
// Name of the printer
mii.dwTypeData = pName;
mii.cch = lstrlen(pName);
InsertMenuItem(hmContext, (UINT)-1, MF_BYPOSITION, &mii);
}
} // if (haPrinterNames)
// show the context menu
if (msg.message == WM_USER + WM_RBUTTONUP)
{
// We need an hwnd owned by this thread.
HWND hwndOwner = CreateWindow(TEXT("static"), NULL, WS_DISABLED, 0, 0, 0, 0, NULL, NULL, hinstCabinet, 0L);
DWORD dwPos = GetMessagePos();
SetMenuDefaultItem(hmContext, idDefCmd, MF_BYCOMMAND);
SetForegroundWindow(hwndOwner);
SetFocus(hwndOwner);
idCmd = TrackPopupMenu(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, hwndOwner, NULL);
DestroyWindow(hwndOwner);
}
else
{
idCmd = idDefCmd;
}
if (idCmd != 0)
{
int iStart, iStop;
if (idCmd == IDM_PRINTNOTIFY_FOLDER)
{
iStart = 0;
iStop = DSA_GetItemCount(haPrinterNames);
}
else
{
iStart = idCmd - IDM_PRINTNOTIFY_FOLDER - 1;
iStop = iStart + 1;
}
for ( ; iStart < iStop ; iStart++)
{
LPPRINTERNAME pPrinter = DSA_GetItemPtr(haPrinterNames, iStart);
IContextMenu *pcm;
// open selected queue view
TraceMsg(TF_TRAY, "PRINTNOTIFY - Open '%s'", pPrinter->szPrinterName);
psf->lpVtbl->GetUIObjectOf(psf, NULL, 1, &pPrinter->pidlPrinter, &IID_IContextMenu, NULL, &pcm);
if (pcm)
{
// We need to call QueryContextMenu() so that CDefFolderMenu
// can initialize its dispatch table correctly.
HMENU hmenuTmp = CreatePopupMenu();
if (hmenuTmp)
{
HRESULT hres;
hres = pcm->lpVtbl->QueryContextMenu(pcm, hmenuTmp, 0, 0, 0x7FFF, CMF_DEFAULTONLY);
if (SUCCEEDED(hres))
{
// BUGBUG:: compiler bug VC4 will not zero out remaining
// fields...
CMINVOKECOMMANDINFOEX ici;
ZeroMemory(&ici, SIZEOF(CMINVOKECOMMANDINFOEX));
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
// ici.fMask = 0;
ici.hwnd = NULL;
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(GetMenuDefaultItem(hmenuTmp, MF_BYCOMMAND, 0));
// ici.lpParameters = NULL;
// ici.lpDirectory = NULL;
ici.nShow = SW_NORMAL;
pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&ici);
}
DestroyMenu(hmenuTmp);
}
pcm->lpVtbl->Release(pcm);
}
}
}
DestroyMenu(hmContext);
} // if (hmenu)
break;
}
case SHCNE_CREATE:
case SHCNE_DELETE:
TraceMsg(TF_TRAY, "PRINTNOTIFY - Thread_PrinterPoll SHCNE_CREATE/DELETE");
// Bug 405510
// BUGBUG: Somebody is passing us messages with a bogus PIDL as the lParam.
// We don't know who it is, but we double-check if the PIDL is okay here.
// If we can find the culprit, then this check won't be necessary.
if (!IsValidPIDL((LPCITEMIDLIST)msg.lParam))
{
break;
}
PrintNotify_AddToQueue(&haPrinterNames, &psf, (LPCITEMIDLIST)msg.lParam);
// We need to free the pidl passed in.
pidlFree = (LPITEMIDLIST)msg.lParam;
poll:
dwTimeOut = POLLINTERVAL;
// just in case we failed AddToQueue
if (!haPrinterNames)
{
ASSERT(FALSE);
break;
}
nFound = 0;
dwStatus = 0;
for (iPrinter=DSA_GetItemCount(haPrinterNames)-1 ; iPrinter>=0 ; --iPrinter)
{
LPPRINTERNAME pPrinter;
BOOL fFound;
BOOL fNetDown;
pPrinter = DSA_GetItemPtr(haPrinterNames,iPrinter);
// if the tray is shutting down while we're in the
// middle of polling, we might want to exit this loop
if (!g_pfnOpenPrinter(pPrinter->szPrinterName, &hPrinter, NULL))
{
// Can't open the printer? Remove it so we don't keep trying
TraceMsg(TF_TRAY, "PRINTNOTIFY - can't open [%s]", pPrinter->szPrinterName);
goto remove_printer;
}
TraceMsg(TF_TRAY, "PRINTNOTIFY - polling [%s]", pPrinter->szPrinterName);
fFound = FALSE;
iJob = _EnumJobs(hPrinter, 1, &pJobs) - 1;
fNetDown = GetLastError() == ERROR_BAD_NET_RESP;
pPrinter->fInErrorState = FALSE;
for ( ; iJob>=0 ; --iJob)
{
// if GetUserName failed, match all jobs
#ifdef WINNT
// Exclude jobs that are status PRINTED.
if ((!lstrcmpi(pJobs[iJob].pUserName, szUserName) || !szUserName[0]) && !(pJobs[iJob].Status & JOB_STATUS_PRINTED))
#else
if (!lstrcmpi(pJobs[iJob].pUserName, szUserName) || !szUserName[0])
#endif
{
// We found a job owned by the user,
// so continue to show the icon
++nFound;
dwStatus |= pJobs[iJob].Status;
if (pJobs[iJob].Status & JOB_STATUS_USER_INTERVENTION)
pPrinter->fInErrorState = TRUE;
fFound = TRUE;
}
}
g_pfnClosePrinter(hPrinter);
if (pJobs)
{
LocalFree(pJobs);
}
// there may be local jobs (fFound), but if the net is down
// then we don't want to poll this printer again -- we have to
// wait for the net to timeout which is SLOW.
if (!fFound || fNetDown)
{
#ifdef DEBUG
if (fNetDown)
TraceMsg(TF_TRAY, "PRINTNOTIFY - net is down for [%s]", pPrinter->szPrinterName);
else
TraceMsg(TF_TRAY, "PRINTNOTIFY - no jobs for [%s]", pPrinter->szPrinterName);
#endif
remove_printer:
// no need to poll this printer any more
ILFree(pPrinter->pidlPrinter);
DSA_DeleteItem(haPrinterNames, iPrinter);
}
} // polling loop
#ifndef WINNT
update_icon:
#endif
if (nFound)
{
UINT nIcon = (dwStatus & JOB_STATUS_ERROR_BITS) ? ICO_PRINTER_ERROR : ICO_PRINTER;
// Keep track of small icon sizes so we can redraw the
// icon whenever this changes.
cxSmIcon = GetSystemMetrics(SM_CXSMICON);
cySmIcon = GetSystemMetrics(SM_CYSMICON);
if ((nIconShown != nIcon) || cxSmIcon != s_cxSmIcon || cySmIcon != s_cySmIcon)
{
nIconShown = nIcon;
s_cxSmIcon = GetSystemMetrics(SM_CXSMICON);
s_cySmIcon = GetSystemMetrics(SM_CYSMICON);
// show an error printer icon if the print job is in
// an error state which "requires user intervention"
if (dwStatus & JOB_STATUS_ERROR_BITS)
{
if (hIconError == NULL)
hIconError = (HICON)LoadImage(hinstCabinet, MAKEINTRESOURCE(nIcon), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
IconData.hIcon = hIconError;
}
else
{
if (hIconNormal == NULL)
hIconNormal = (HICON)LoadImage(hinstCabinet, MAKEINTRESOURCE(nIcon), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
IconData.hIcon = hIconNormal;
}
IconData.uFlags |= NIF_ICON;
}
}
else
{
if (pidlFree)
{
ILGlobalFree(pidlFree);
pidlFree = NULL;
}
goto exit;
}
if (aArgs[0] != (DWORD_PTR)nFound)
{
aArgs[0] = nFound;
if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY, szFormat, 0, 0, IconData.szTip, ARRAYSIZE(IconData.szTip), (va_list*)aArgs))
{
IconData.szTip[0] = TEXT('\0');
}
IconData.uFlags |= NIF_TIP;
}
// if the state has changed, update the tray notification area
if (IconData.uFlags)
{
TraceMsg(TF_TRAY, "PRINTNOTIFY - updating icon area");
Shell_NotifyIcon(dwNotifyMessage, &IconData);
dwNotifyMessage = NIM_MODIFY;
IconData.uFlags = 0;
}
break;
#ifndef WINNT
// WINNT is hacked only to send SHCNE_CREATE messages.
case SHCNE_UPDATEITEM:
TraceMsg(TF_TRAY, "PRINTNOTIFY - Thread_PrinterPoll SHCNE_UPDATEITEM");
// We need to free the pidl if one is passed in.
// (Currently one is not.)
pidlFree = (LPITEMIDLIST)msg.lParam;
// if the JOB_STATUS_ERROR_BITS status bit has
// changed, update the printer icon
if (((DWORD)msg.wParam ^ dwStatus) & JOB_STATUS_ERROR_BITS)
{
if ((DWORD)msg.wParam & JOB_STATUS_ERROR_BITS)
{
// A job just went into the error state, turn the
// icon on right away.
dwStatus |= JOB_STATUS_ERROR_BITS;
goto update_icon;
}
else
{
// Maybe the last job in an error state went
// out of the error state. Poll to find out.
// (But not more frequently than MINPOLLINTERVAL.)
if (dwTimeOut - dwTime < POLLINTERVAL - MINPOLLINTERVAL)
goto poll;
}
}
// Or if the icon size has changed.
if (s_cxSmIcon != cxSmIcon || s_cySmIcon != cySmIcon)
goto update_icon;
// don't wait as long next time
dwTimeOut -= dwTime;
if (dwTimeOut > POLLINTERVAL)
{
// oops, rollover; time to poll
goto poll;
}
break;
#endif
#ifdef DEBUG
default:
TraceMsg(TF_TRAY, "PRINTNOTIFY - Bogus PeekMessage 0x%X in Thread_PrinterPoll", msg.message);
break;
#endif
}
if (pidlFree)
{
ILGlobalFree(pidlFree);
pidlFree = NULL;
}
} // while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
} // switch (dw)
} // for ( ; TRUE ; )
exit:
Error2:
FreeLibrary(hmodWinspool);
Error1:
{
// Set g_ts.htPrinterPoll to null here and only here. we have a timing
// bug where someone sets this to null and we hang.
// Because g_ts.htPrinterPoll was null, we couldn't trace down what happened to the polling thread (it was lost).
HANDLE htPrinterPoll = (HANDLE)InterlockedExchangePointer((void **)&g_ts.htPrinterPoll, 0);
// assuming no race condition these should be true
ASSERT(htPrinterPoll);
ASSERT(g_ts.htPrinterPoll == NULL);
CloseHandle(htPrinterPoll);
}
if (psf)
{
psf->lpVtbl->Release(psf);
}
if (haPrinterNames)
{
int i;
for (i = DSA_GetItemCount(haPrinterNames) - 1 ; i >= 0 ; i--)
{
LPPRINTERNAME pPrinter = DSA_GetItemPtr(haPrinterNames, i);
ILFree(pPrinter->pidlPrinter);
}
DSA_Destroy(haPrinterNames);
}
if (nIconShown)
{
Shell_NotifyIcon(NIM_DELETE, &IconData);
}
if (hIconNormal)
{
DestroyIcon(hIconNormal);
}
if (hIconError)
{
DestroyIcon(hIconError);
}
return(0);
}
IShellFolder* BindToFolder(LPCITEMIDLIST pidl)
{
IShellFolder *psfDesktop;
if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
{
IShellFolder* psf;
psfDesktop->lpVtbl->BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, &psf);
psfDesktop->lpVtbl->Release(psfDesktop); // not really needed
return psf;
}
return NULL;
}
// This functions adds the printer name pointed to by pidl to the list of printers to poll by Thread_PrinterPoll
void PrintNotify_AddToQueue(LPHANDLE phaPrinterNames, LPSHELLFOLDER *ppsf, LPCITEMIDLIST pidlPrinter)
{
STRRET strret;
LPCTSTR pNewPrinter;
LPPRINTERNAME pPrinter;
int i;
LPSHELLFOLDER psf;
PRINTERNAME pn;
#ifdef UNICODE
LPCTSTR pszFree = NULL;
#endif
// We need the IShellFolder for the Printers Folder in order to get the display name of the printer
psf = *ppsf;
if (psf == NULL)
{
psf = BindToFolder(g_ts.pidlPrintersFolder);
if (!psf)
{
goto exit;
}
*ppsf = psf;
}
psf->lpVtbl->GetDisplayNameOf(psf, pidlPrinter, SHGDN_FORPARSING, &strret);// all this work to get the name of the printer:
// we know how we implemented the printers GetDisplayNameOf function
#ifdef UNICODE
// pszFree saves a copy that will be freed on exit.
ASSERT(strret.uType == STRRET_OLESTR);
pszFree = pNewPrinter = strret.pOleStr;
#else
ASSERT(strret.uType == STRRET_OFFSET);
pNewPrinter = STRRET_OFFPTR(pidlPrinter, &strret);
#endif
if (*phaPrinterNames == NULL)
{
*phaPrinterNames = DSA_Create(SIZEOF(PRINTERNAME), 4);
if (*phaPrinterNames == NULL)
goto exit;
}
for (i=DSA_GetItemCount(*phaPrinterNames)-1 ; i>=0 ; --i)
{
pPrinter = DSA_GetItemPtr(*phaPrinterNames, i);
if (!lstrcmp(pPrinter->szPrinterName, pNewPrinter))
{
// printer already in list, no need to add it
TraceMsg(TF_TRAY, "PRINTNOTIFY - [%s] already in poll list", pNewPrinter);
goto exit;
}
}
TraceMsg(TF_TRAY, "PRINTNOTIFY - adding [%s] to poll list", pNewPrinter);
lstrcpy(pn.szPrinterName, pNewPrinter);
pn.fInErrorState = FALSE;
pn.pidlPrinter = ILClone(pidlPrinter);
DSA_AppendItem(*phaPrinterNames, &pn);
exit:
#ifdef UNICODE
// Free the allocated string from GetDisplayNameOf in strret.
if (pszFree)
{
SHFree((void *)pszFree);
}
#endif
return;
}
BOOL PrintNotify_StartThread(HWND hwnd)
{
PPRINTERPOLLDATA pPrinterPollData;
BOOL fRet;
if (g_ts.htPrinterPoll)
{
return TRUE;
}
ENTERCRITICAL;
// We check this again so that the above check will go very quickly, and almost always do the return
fRet = g_ts.htPrinterPoll != NULL;
if (fRet)
{
goto Error_Exit;
}
// We keep one event around to communicate with the polling thread so
// we don't need to keep re-creating it whenever the user prints.
// On the down side: this thing never does get freed up... Oh well.
if (!g_ts.heWakeUp)
{
g_ts.heWakeUp = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!g_ts.heWakeUp)
goto Error_Exit;
}
pPrinterPollData = (PPRINTERPOLLDATA)GlobalAlloc(GPTR, SIZEOF(PRINTERPOLLDATA));
if (!pPrinterPollData)
{
goto Error_Exit;
}
pPrinterPollData->hwnd = hwnd;
pPrinterPollData->heWakeUp = g_ts.heWakeUp;
g_ts.htPrinterPoll = CreateThread(NULL, 0, Thread_PrinterPoll, pPrinterPollData, 0, &g_ts.idPrinterPoll);
fRet = g_ts.htPrinterPoll != NULL;
LEAVECRITICAL;
if (fRet)
{
// wait until the new thread's message queue is ready
// I had INFINITE here, but something funky happened once on a build
// machine which caused MSGSRV32 to hang waiting for this thread to unblock.
// If we timeout, the worst that will happen is our icon won't show and/or we'll keep destroying re-creating the polling thread until everything works.
WaitForSingleObject(g_ts.heWakeUp, ERRTIMEOUT);
TraceMsg(TF_TRAY, "PRINTNOTIFY - Thread_PrinterPoll started");
}
else
{
GlobalFree(pPrinterPollData);
}
goto Exit;
Error_Exit:
LEAVECRITICAL;
Exit:
return fRet;
}
void PrintNotify_HandleFSNotify(HWND hwnd, LPCITEMIDLIST *ppidl, LONG lEvent)
{
// Look at the pidl to determine if it's a Print Job notification.
// If it is, we may also need a copy of the relative Printer pidl.
// Pass the notification on to our polling thread so the tray isn't stuck waiting
// for a (net) print subsystem query (up to 15 seconds if net is funky).
LPITEMIDLIST pidl = ILClone(ppidl[0]);
if (pidl)
{
LPITEMIDLIST pidlPrintJob;
LPITEMIDLIST pidlPrinter;
LPITEMIDLIST pidlPrintersFolder;
USHORT cbPidlPrinter;
pidlPrintJob = ILFindLastID(pidl);
ILRemoveLastID(pidl);
pidlPrinter = ILFindLastID(pidl);
cbPidlPrinter = pidlPrinter->mkid.cb;
ILRemoveLastID(pidl);
pidlPrintersFolder = pidl;
if (ILIsEqual(pidlPrintersFolder, g_ts.pidlPrintersFolder))
{
if (PrintNotify_StartThread(hwnd))
{
LPCITEMIDLIST pidlPrinterName = NULL;
LPSHCNF_PRINTJOB_DATA pData;
// HACK: we know the format of this (internal) "temporary" pidl
// which exists only between SHChangeNotify and here
pData = (LPSHCNF_PRINTJOB_DATA)(pidlPrintJob->mkid.abID);
// PERFORMANCE: SHCNE_CREATE can just add one to the local count
// and SHCNE_DELETE will sub one from the local count and start
// polling this printer to get the net count.
// GOOD ENOUGH: poll on create and delete.
if (SHCNE_UPDATEITEM != lEvent)
{
pidlPrinter->mkid.cb = cbPidlPrinter;// this was whacked with one of the above ILRemoveLastID()s
pidlPrinterName = ILGlobalClone(pidlPrinter);// this is freed in the polling thread
}
// REVIEW: What if we PostThreadMessage to a dead thread?
// PrintNotify_StartThread can start the thread and give us the idPrinterPoll,
// but then under certain error conditions the new thread will terminate itself.
// This can happen after the above if and before this post.
PostThreadMessage(g_ts.idPrinterPoll, WM_USER+(UINT)lEvent, (WPARAM)pData->Status, (LPARAM)pidlPrinterName);
}
}
ILFree(pidl);
}
}
void PrintNotify_Exit(void)
{
// PrintNotify_Init only initializes if it can get the pidlPrintsFolder
if (g_ts.pidlPrintersFolder)
{
if (g_ts.uPrintNotify)
{
SHChangeNotifyDeregister(g_ts.uPrintNotify);
}
if (g_ts.htPrinterPoll)
{
// Signal the PrinterPoll thread to exit
if (g_ts.heWakeUp)
{
SetEvent(g_ts.heWakeUp);
}
WaitForSingleObject(g_ts.htPrinterPoll, ERRTIMEOUT);
}
// Free this last just in case the htPrinterPoll thread was active
ILFree((LPITEMIDLIST)g_ts.pidlPrintersFolder);
}
}
void PrintNotify_IconNotify(HWND hwnd, LPARAM uMsg)
{
switch (uMsg)
{
case WM_LBUTTONDBLCLK:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
// call off to worker thread to bring up the context menu --
// we need to access the list of polling printers for this
if (g_ts.htPrinterPoll && g_ts.idPrinterPoll)
{
PostThreadMessage(g_ts.idPrinterPoll, (UINT)(WM_USER+uMsg), 0, 0);
}
else if (uMsg == WM_LBUTTONDBLCLK)
{
ShowFolder(hwnd, CSIDL_PRINTERS, COF_USEOPENSETTINGS);
}
break;
}
}
// End Print Notify stuff
#ifndef WINNT // All NT drivers are required to support reschange
BOOL IsDisplayChangeSafe()
// make sure the display change is a safe thing to do.
// NOTE this code can easily be fooled if we are running on HW that will be
// gone after a undoc.
{
HDC hdc;
BOOL fSafe;
BOOL fVGA;
hdc = GetDC(NULL);
fVGA = GetDeviceCaps(hdc, PLANES) == 4 && GetDeviceCaps(hdc, BITSPIXEL) == 1 && GetDeviceCaps(hdc, HORZRES) == 640 && GetDeviceCaps(hdc, VERTRES) == 480;
fSafe = fVGA || (GetDeviceCaps(hdc, CAPS1) & C1_REINIT_ABLE);
ReleaseDC(NULL, hdc);
return fSafe;
}
#endif
int BlueScreenMesssgeBox(TCHAR *szTitle, TCHAR *szText, int flags)
{
#ifndef WINNT
BLUESCREENINFO bsi = {szText, szTitle, flags};
int cb;
HANDLE h;
//BUGBUG
// AnsiToOEM(szTitle, szTitle);
// AnsiToOEM(szText, szText);
h = CreateFile(SHELLFILENAME, GENERIC_WRITE, FILE_SHARE_WRITE,
0, OPEN_EXISTING, 0, 0);
if (h != INVALID_HANDLE_VALUE)
{
DeviceIoControl(h, WSHIOCTL_BLUESCREEN, &bsi, SIZEOF(bsi),
&flags, SIZEOF(flags), &cb, 0);
CloseHandle(h);
}
return flags;
#else
// BUGBUG - We need to figure out what to do for NT here
return 0;
#endif
}
const TCHAR c_szConfig[] = REGSTR_KEY_CONFIG;
const TCHAR c_szSlashDisplaySettings[] = TEXT("\\") REGSTR_PATH_DISPLAYSETTINGS;
const TCHAR c_szResolution[] = REGSTR_VAL_RESOLUTION;
// GetMinDisplayRes
// walk all the configs and find the minimum display resolution.
// when doing a hot undock we have no idea what config we are
// going to undock into.
// we want to put the display into a "common" mode that all configs
// can handle so we dont "fry" the display when we wake up in the new mode.
DWORD GetMinDisplayRes(void)
{
TCHAR ach[128];
UINT cb;
HKEY hkey;
HKEY hkeyT;
int i, n;
int xres=0;
int yres=0;
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szConfig, &hkey) == ERROR_SUCCESS)
{
for (n=0; RegEnumKey(hkey, n, ach, ARRAYSIZE(ach)) == ERROR_SUCCESS; n++)
{
lstrcat(ach, c_szSlashDisplaySettings); // 0000\Display\Settings
TraceMsg(TF_TRAY, "GetMinDisplayRes: found config %s", ach);
if (RegOpenKey(hkey, ach, &hkeyT) == ERROR_SUCCESS)
{
cb = SIZEOF(ach);
ach[0] = 0;
RegQueryValueEx(hkeyT, c_szResolution, 0, NULL, (LPBYTE) &ach[0], &cb);
TraceMsg(TF_TRAY, "GetMinDisplayRes: found res %s", ach);
if (ach[0])
{
i = StrToInt(ach);
if (i < xres || xres == 0)
xres = i;
for (i=1;ach[i] && ach[i-1]!=TEXT(','); i++)
;
i = StrToInt(ach + i);
if (i < yres || yres == 0)
yres = i;
}
else
{
xres = 640;
yres = 480;
}
RegCloseKey(hkeyT);
}
}
RegCloseKey(hkey);
}
TraceMsg(TF_TRAY, "GetMinDisplayRes: xres=%d yres=%d", xres, yres);
if (xres == 0 || yres == 0)
return MAKELONG(640, 480);
else
return MAKELONG(xres, yres);
}
// the user has done a un-doc or re-doc we may need to switch to a new display mode.
// if fCritical is set the mode switch is critical, show a error
// if it does not work.
void HandleDisplayChange(int x, int y, BOOL fCritical)
{
TCHAR ach[256];
DEVMODE dm;
LONG err;
HDC hdc;
// try to change into the mode specific to this config
// HKEY_CURRENT_CONFIG has already been updated by PnP
// so all we have to do is re-init the current display
// we cant default to current bpp because we may have changed configs.
// and the bpp may be different in the new config.
dm.dmSize = SIZEOF(dm);
dm.dmFields = DM_BITSPERPEL;
hdc = GetDC(NULL);
dm.dmBitsPerPel = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
ReleaseDC(NULL, hdc);
if (x + y)
{
dm.dmFields |= DM_PELSWIDTH|DM_PELSHEIGHT;
dm.dmPelsWidth = x;
dm.dmPelsHeight = y;
}
err = ChangeDisplaySettings(&dm, 0);
if (err != 0 && fCritical)
{
// if it fails make a panic atempt to try 640x480, if
// that fails also we should put up a big error message
// in text mode and tell the user he is screwed.
dm.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
dm.dmPelsWidth = 640;
dm.dmPelsHeight = 480;
err = ChangeDisplaySettings(&dm, 0);
if (err != 0)
{
// if 640x480 fails we should put up a big error message
// in text mode and tell the user he is screwed. and
// offer to reboot his machine.
MessageBeep(0);
LoadString(hinstCabinet, IDS_DISPLAY_ERROR, ach, ARRAYSIZE(ach));
BlueScreenMesssgeBox(NULL, ach, MB_OK);
}
}
}
void Tray_HandleGlobalHotkey(WPARAM wParam)
{
INSTRUMENT_HOTKEY(SHCNFI_GLOBALHOTKEY, wParam);
UEMFireEvent(&UEMIID_SHELL, UEME_UIHOTKEY, UEMF_XEVENT, -1, wParam); // NYI FEATURE_UASSIST
switch(wParam)
{
case GHID_RUN:
Tray_RunDlg();
break;
case GHID_MINIMIZEALL:
if (CanMinimizeAll(g_ts.hwndView))
MinimizeAll(g_ts.hwndView);
SetForegroundWindow(v_hwndDesktop);
break;
case GHID_UNMINIMIZEALL:
RestoreWindowPositions();
break;
case GHID_HELP:
Tray_Command(IDM_HELPSEARCH);
break;
case GHID_DESKTOP:
ToggleDesktop();
break;
case GHID_EXPLORER:
ShowFolder(g_ts.hwndMain, CSIDL_DRIVES, COF_CREATENEWWINDOW | COF_EXPLORE);
break;
case GHID_FINDFILES:
if (!SHRestricted(REST_NOFIND))
Tray_Command(FCIDM_FINDFILES);
break;
case GHID_FINDCOMPUTER:
if (!SHRestricted(REST_NOFIND))
Tray_Command(FCIDM_FINDCOMPUTER);
break;
case GHID_TASKTAB:
case GHID_TASKSHIFTTAB:
if (GetForegroundWindow() != v_hwndTray)
SetForegroundWindow(v_hwndTray);
SendMessage(g_ts.hwndView, TM_TASKTAB, wParam == GHID_TASKTAB ? 1 : -1, 0L);
break;
case GHID_SYSPROPERTIES:
#define IDS_SYSDMCPL 0x2334 // from shelldll
SHRunControlPanel(MAKEINTRESOURCE(IDS_SYSDMCPL), g_ts.hwndMain);
break;
}
}
void Tray_UnregisterGlobalHotkeys()
{
int i;
for (i = GHID_FIRST ; i < GHID_MAX; i++) {
UnregisterHotKey(v_hwndTray, i);
};
}
void Tray_RegisterGlobalHotkeys()
{
int i;
// Are the Windows keys restricted?
DWORD dwRestricted = SHRestricted(REST_NOWINKEYS);
for (i = GHID_FIRST ; i < GHID_MAX; i++)
{
// If the Windows Keys are Not restricted or it's not a Windows key
if (!( (HIWORD(GlobalKeylist[i - GHID_FIRST]) & MOD_WIN) && dwRestricted) )
{
// Then register it.
RegisterHotKey(v_hwndTray, i, HIWORD(GlobalKeylist[i - GHID_FIRST]), LOWORD(GlobalKeylist[i - GHID_FIRST]));
}
}
}
void RaiseDesktop()
{
if (v_hwndDesktop) {
if (CanMinimizeAll(g_ts.hwndView))
MinimizeAll(g_ts.hwndView);
PostMessage(v_hwndDesktop, DTM_RAISE, (WPARAM)v_hwndTray, DTRF_RAISE);
#ifdef CONFIG_DESKTOP
if (!g_hdlgDesktopConfig)
{
g_hdlgDesktopConfig = CreateDialog(hinstCabinet, MAKEINTRESOURCE(DLG_CONFIGDESKTOP), HWND_DESKTOP, (DLGPROC)ConfigDesktopDlgProc);
g_fShouldShowConfigDesktop = TRUE;
}
#endif
}
}
HWND g_hwndFocusBeforeRaise = NULL;
void LowerDesktop()
{
if (v_hwndDesktop && g_fDesktopRaised) {
PostMessage(v_hwndDesktop, DTM_RAISE, (WPARAM)v_hwndTray, DTRF_LOWER);
g_hwndFocusBeforeRaise = NULL;
g_fShouldShowConfigDesktop = FALSE;
KillConfigDesktopDlg();
}
}
void QueryDesktop()
{
if (v_hwndDesktop)
PostMessage(v_hwndDesktop, DTM_RAISE, (WPARAM)v_hwndTray, DTRF_QUERY);
}
void ToggleDesktop()
{
if (g_fDesktopRaised) {
HWND hwndLastActive;
HWND hwnd = g_hwndFocusBeforeRaise;
LowerDesktop();
if (!CanMinimizeAll(g_ts.hwndView))
RestoreWindowPositions();
if (!hwnd)
hwnd = v_hwndTray;
// use GetLastActivePopup (if it's a visible window) so we don't change
// what child had focus all the time
hwndLastActive = GetLastActivePopup(hwnd);
if (IsWindowVisible(hwndLastActive))
hwnd = hwndLastActive;
SetForegroundWindow(hwnd);
if (hwnd == v_hwndTray) {
Tray_SetFocus(g_ts.hwndStart);
}
} else {
g_hwndFocusBeforeRaise = GetForegroundWindow();
RaiseDesktop();
}
}
void Tray_OnDesktopState(LPARAM lParam)
{
g_fDesktopRaised = (!(lParam & DTRF_LOWER));
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
if (g_hwndDesktopTB)
SendMessage(g_hwndDesktopTB, TB_CHECKBUTTON, IDM_RAISEDESKTOP, g_fDesktopRaised);
if (!g_fDesktopRaised)
{
Tray_HandleFullScreenApp(g_ts.hwndRude);
}
else
{
// if the desktop is raised, we need to force the tray to be always on top
// until it's lowered again
Tray_ResetZorder();
}
DAD_ShowDragImage(TRUE); // unlock the drag sink if we are dragging.
}
#ifdef DESKBTN
void Tray_CreateDesktopButton()
{
HWND hwnd = g_ts.hwndMain;
TBBUTTON tb = { 0, IDM_RAISEDESKTOP, TBSTATE_ENABLED, BTNS_CHECK, {0,0}, 0, -1 };
if (!SHRestricted(REST_CLASSICSHELL))
g_hwndDesktopTB = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL,
TBSTYLE_FLAT| CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, // | TBSTYLE_TOOLTIPS
0, 0, 100, 30, hwnd, (HMENU)IDC_RAISEDESKTOP, hinstCabinet, NULL);
if (g_hwndDesktopTB) {
LPITEMIDLIST pidlDesktop;
g_DesktopTBProc = SubclassWindow(g_hwndDesktopTB, DesktopTBSubclass);
// this tells the toolbar what version we are
SendMessage(g_hwndDesktopTB, TB_BUTTONSTRUCTSIZE, SIZEOF(TBBUTTON), 0);
SendMessage(g_hwndDesktopTB, TB_SETBITMAPSIZE, 0, MAKELONG(16,16));
SendMessage(g_hwndDesktopTB, TB_SETBUTTONWIDTH, 0, MAKELONG(g_ts.sizeStart.cy, g_ts.sizeStart.cy));
SendMessage(g_hwndDesktopTB, TB_SETTOOLTIPS, (WPARAM)g_ts.hwndTrayTips, 0);
SendMessage(g_hwndDesktopTB, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
if (pidlDesktop) {
HIMAGELIST himl;
SHFILEINFO sfi;
Shell_GetImageLists(NULL, &himl);
SHGetFileInfo((LPCTSTR)pidlDesktop, 0, &sfi ,sizeof(sfi), SHGFI_SYSICONINDEX |SHGFI_PIDL);
tb.iBitmap = sfi.iIcon;
SendMessage(g_hwndDesktopTB, TB_SETIMAGELIST, 0, (LPARAM)himl);
SendMessage(g_hwndDesktopTB, TB_ADDBUTTONS, 1, (LPARAM)&tb);
ILFree(pidlDesktop);
}
}
}
#endif
// Process the message by propagating it to all of our child windows
struct _CabPM
{
UINT uMsg;
WPARAM wP;
LPARAM lP;
};
BOOL _PropagateProc(HWND hwnd, struct _CabPM *ppm)
{
if (SHIsChildOrSelf(g_ts.hwndRebar, hwnd) == S_OK) {
return TRUE;
}
SendMessage(hwnd, ppm->uMsg, ppm->wP, ppm->lP);
return TRUE;
}
void _PropagateMessage(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
struct _CabPM pm = {uMessage, wParam, lParam};
ASSERT(hwnd != g_ts.hwndRebar);
EnumChildWindows(hwnd, (WNDENUMPROC)_PropagateProc, (LPARAM)&pm);
}
BOOL CALLBACK Cabinet_RefreshEnum(HWND hwnd, LPARAM lParam)
{
if (Cabinet_IsFolderWindow(hwnd) || Cabinet_IsExplorerWindow(hwnd))
{
PostMessage(hwnd, WM_COMMAND, FCIDM_REFRESH, 0L);
}
return(TRUE);
}
void Cabinet_RefreshAll(void)
{
PostMessage(v_hwndDesktop, WM_COMMAND, FCIDM_REFRESH, 0L);
PostMessage(v_hwndTray, WM_COMMAND, FCIDM_REFRESH, 0L);
EnumWindows(Cabinet_RefreshEnum, 0);
}
// Called from SETTINGS.DLL when the tray property sheet needs
// to be activated. See SettingsUI_ThreadProc below.
VOID WINAPI SettingsUI_TrayPropSheetCallback(DWORD nStartPage)
{
DoTrayProperties(nStartPage);
}
DWORD SettingsUI_ThreadProc(void *pv)
{
// Open up the "Settings Wizards" UI.
HMODULE hmodSettings = LoadLibrary(TEXT("settings.dll"));
if (NULL != hmodSettings)
{
// Entry point in SETTINGS.DLL is ordinal 1.
// Don't want to export this entry point by name.
PSETTINGSUIENTRY pfDllEntry = (PSETTINGSUIENTRY)GetProcAddress(hmodSettings, (LPCSTR)1);
if (NULL != pfDllEntry)
{
// This call will open and run the UI.
// The thread's message loop is inside settings.dll.
// This call will not return until the settings UI has been closed.
(*pfDllEntry)(SettingsUI_TrayPropSheetCallback);
}
FreeLibrary(hmodSettings);
}
return 0;
}
HRESULT CDeskTray_QueryInterface(IDeskTray* this, REFIID riid, void ** ppvObj)
{
*ppvObj = NULL;
return E_NOTIMPL;
}
ULONG CDeskTray_AddRef(IDeskTray* this)
{
return 2;
}
ULONG CDeskTray_Release(IDeskTray* this)
{
return 1;
}
HRESULT CDeskTray_GetTrayWindow(IDeskTray* this, HWND* phwndTray)
{
ASSERT(v_hwndTray);
*phwndTray = v_hwndTray;
return S_OK;
}
HRESULT CDeskTray_SetDesktopWindow(IDeskTray* this, HWND hwndDesktop)
{
ASSERT(v_hwndDesktop == NULL);
v_hwndDesktop = hwndDesktop;
return S_OK;
}
//** CDeskTray::SetVar -- set an explorer variable (var#i := value)
// ENTRY/EXIT
// var id# of variable to be changed
// value value to be assigned
// NOTES
// WARNING: thread safety is up to caller!
// notes: currently only called in 1 place, but extra generality is cheap
// minimal cost
HRESULT CDeskTray_SetVar(IDeskTray* this, int var, DWORD value)
{
extern BOOL g_fExitExplorer;
TraceMsg(DM_TRACE, "c.cdt_sv: set var(%d):=%d", var, value);
switch (var) {
case SVTRAY_EXITEXPLORER:
TraceMsg(DM_TRACE, "c.cdt_sv: set g_fExitExplorer:=%d", value);
g_fExitExplorer = value;
WriteCleanShutdown(1);
break;
default:
ASSERT(0);
return S_FALSE;
}
return S_OK;
}
// This is a private interface EXPLORER.EXE exposes to SHDOCVW, which allows SHDOCVW (mostly desktop) to access tray. All member must be thread safe!
const IDeskTrayVtbl c_DeskTrayVtbl =
{
CDeskTray_QueryInterface, CDeskTray_AddRef, CDeskTray_Release,
CDeskTray_AppBarGetState,
CDeskTray_GetTrayWindow,
CDeskTray_SetDesktopWindow,
CDeskTray_SetVar,// thread safe iff var in question is!
// READ above warning before you are any member here
};
const struct {
IDeskTray dtray;
} c_CDeskTray = { (IDeskTrayVtbl*)&c_DeskTrayVtbl };
IDeskTray* const c_pdtray = (IDeskTray*)&c_CDeskTray.dtray;
// Review chrisny: this can be moved into an object easily to handle generic droptarget, dropcursor
// , autoscrool, etc. . .
void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject)
{
RECT rc;
POINT pt;
GetWindowRect(hwndTarget, &rc);
pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top;
DAD_DragEnterEx2(hwndTarget, pt, pdtObject);
}
void _DragMove(HWND hwndTarget, const POINTL ptStart)
{
RECT rc;
POINT pt;
GetWindowRect(hwndTarget, &rc);
pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top;
DAD_DragMove(pt);
}