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

8753 lines
269 KiB

//--------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1992
//
// File: desktop.c
//
// Contains: Shell desktop functions.
//
// History:
// 12-28-92 SatoNa Calls FileIconIndex().
//
//---------------------------------------------------------------------------
#include "cabinet.h"
#include "cabwnd.h"
#include "rcids.h"
#include <shellapi.h>
#include "trayclok.h"
#include <fsmenu.h>
#include <shguidp.h>
#include <trayp.h>
#include "traynot.h"
#include "help.h" // help ids
#if defined(FE_IME)
#include <immp.h>
#endif
#include <regstr.h>
#ifndef WINNT // BUGBUG - Fix this when NT gets POWERMGT features
#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
#define IsEjectAllowed() (g_ts.hBIOS != INVALID_HANDLE_VALUE)
#define ENTERCRITICAL MEnterCriticalSection(&g_csThreads)
#define LEAVECRITICAL MLeaveCriticalSection(&g_csThreads)
#define POLLINTERVAL (15*1000) // 15 seconds
#define MINPOLLINTERVAL (3*1000) // 3 seconds
#define ERRTIMEOUT (5*1000) // 5 seconds
#define SECOND 1000
#define RUNWAITSECS 5
#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 TM_RELAYPOSCHANGED (WM_USER + 0x150)
#define TM_INVALIDATEREBUILDMENU (WM_USER + 0x151)
#define TM_BRINGTOTOP (WM_USER + 0x152)
#define RDM_ACTIVATE (WM_USER + 0x101)
#if !defined(_PPC_) // PPC compiler generates random 4701s
#pragma warning(3 : 4700 4701 4702)
#endif
// DSA haPrinterNames structure:
typedef struct _PRINTERNAME {
TCHAR szPrinterName[MAXPRINTERBUFFER];
LPITEMIDLIST pidlPrinter; // relative pidl to printer
BOOL fInErrorState;
} PRINTERNAME, * LPPRINTERNAME;
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, UINT uMsg);
STDMETHODIMP CShellTray_AddViewPropertySheetPages(IShellBrowser * psb, DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam);
STDMETHODIMP ShellDesktop_BrowseObject(LPSHELLBROWSER psb, LPCITEMIDLIST pdil, UINT wFlags);
void Tray_ScreenSizeChange(HWND hWnd);
void Tray_SizeWindows();
void Tray_ContextMenu(PFileCabinet pfc, DWORD dwPos, BOOL fSetTime);
void StuckSizeChange();
void StartMenu_Build(PFileCabinet pfc);
void SetWindowStyleBit(HWND hwnd, DWORD dwBit, DWORD dwValue);
LRESULT Tray_HandleInitMenuPopup(HMENU hmenuPopup);
HMENU Menu_FindSubMenuByFirstID(HMENU hmenu, UINT id);
void CStartDropTarget_Register(LPFileCabinet pfc);
void CStartDropTarget_Revoke(LPFileCabinet pfc);
void DestroySavedWindowPositions();
void RebuildEntireMenu(PFileCabinet pfc);
void Cabinet_InitGlobalMetrics(WPARAM, LPTSTR);
void StartMenuFolder_ContextMenu(PFileCabinet pfc, DWORD dwPos);
void Tray_HandleSizing(PFileCabinet pfc, UINT code, LPRECT lprc, UINT uStuckPlace);
void MinimizeAll(HWND hwndView);
void RegisterGlobalHotkeys();
void TrayUnhide();
int HotkeyList_Restore(HWND hwnd);
LRESULT Tray_RegisterHotkey(HWND hwnd, int i);
void CheckWinIniForAssocs(void);
void SetAutoHideTimer();
#if (defined(DBCS) || defined(FE_IME))
// b#11258-win95d
#if !defined(WINNT)
DWORD WINAPI ImmGetAppIMECompatFlags(DWORD dwThreadID);
#endif
#endif
// appbar stuff
BOOL IAppBarSetAutoHideBar(HWND hwnd, BOOL fSet, UINT uEdge);
void IAppBarActivationChange(HWND hWnd, UINT uEdge);
HWND g_hwndAutoHide[ABE_MAX] = { NULL, NULL, NULL, NULL };
#define ClockCtl_HandleTrayHide(fHiding) 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);
BOOL IsDisplayChangeSafe(void);
DWORD GetMinDisplayRes(void);
// appbar stuff
void AppBarNotifyAll(UINT uMsg, HWND hwndExclude, LPARAM lParam);
HWND v_hwndTray = NULL;
PFileCabinet g_pfcTray = NULL; // we should move all the other globals into here
UINT g_cfIDList = 0;
// Button subclass.
WNDPROC g_ButtonProc = NULL;
// desktop and tray stuff
#define tray_cxScreen (g_cxScreen + g_cxEdge)
#define tray_cyScreen (g_cyScreen + g_cyEdge)
#define tray_xScreen (-g_cxEdge)
#define tray_yScreen (-g_cyEdge)
// timer IDs
#define IDT_AUTOHIDE 2
#define IDT_AUTOUNHIDE 3
#define IDT_FAVOURITE 4
#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 RECTWIDTH(rc) ((rc).right-(rc).left)
#define RECTHEIGHT(rc) ((rc).bottom-(rc).top)
extern HWND g_hwndDde;
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
{
SMCT_NONE = 0x0,
SMCT_PRIMARY = 0x1,
SMCT_WININIASSOCS = 0x2,
SMCT_BUILDLISTOFPATHS = 0x3,
SMCT_INITPROGRAMS = 0x4,
SMCT_INITRECENT = 0x5,
SMCT_PARTFILLPROGRAMS = 0x6,
SMCT_FILLRECENT = 0x7,
SMCT_FILLPROGRAMS = 0x8,
SMCT_FILLFAVOURITE = 0x9,
SMCT_DESKTOPHOTKEYS = 0xa,
SMCT_DONE = 0xb,
SMCT_FILLRECENTONLY = 0xc,
SMCT_FILLPROGRAMSONLY = 0xd,
SMCT_DESKTOPHOTKEYSONLY = 0xe,
SMCT_STOP = 0xf,
SMCT_RESTART = 0x10
} STARTMENUCONTROLTHREAD;
enum
{
GHID_RUN = 0,
GHID_MINIMIZEALL,
GHID_UNMINIMIZEALL,
GHID_HELP,
GHID_EXPLORER,
GHID_FINDFILES,
GHID_FINDCOMPUTER,
GHID_TASKTAB,
GHID_TASKSHIFTTAB,
GHID_SYSPROPERTIES,
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)
};
void DoExitWindows(HWND hwnd);
void TrayCommand(PFileCabinet pfc, UINT idCmd);
BOOL SetAutoHideState(BOOL fAutoHide);
LRESULT CALLBACK TrayWndProc(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_TOPMOST 0x0002
#define TVSD_SMSMALLICONS 0x0004
#define TVSD_HIDECLOCK 0x0008
#define DM_IANELHK DM_TRACE
// Define a 16/32 bit independant format for saving the tray information
// into
typedef struct _TVSD // Tray View Size (or save) Data
{
DWORD dwSize;
// Screen size in X direction.
LONG cxScreen;
LONG cyScreen;
// Only save Widths and heights for the four edges not whole values
LONG dxLeft;
LONG dxRight;
LONG dyTop;
LONG dyBottom;
// Additional data for AutoHide
DWORD uAutoHide;
RECTL rclAutoHide;
// More stuff.
DWORD uStuckPlace;
DWORD dwFlags;
} TVSD;
#define IDC_FOLDERTABS 1
BOOL CreateClockWindow(HWND hwnd)
{
g_ts.hwndNotify = TrayNotifyCreate(hwnd, IDC_CLOCK, hinstCabinet);
return (BOOL)g_ts.hwndNotify;
}
BOOL InitTrayClass(HINSTANCE hInstance)
{
WNDCLASS wc;
wc.lpszClassName = c_szTrayClass;
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = TrayWndProc;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(PFileCabinet);
return RegisterClass(&wc);
}
#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[CCHSZNORMAL];
SIZE size;
HDC hdcStart;
HDC hdcScreen;
HFONT hfontStart = NULL;
HFONT hfontStartOld = NULL;
NONCLIENTMETRICS ncm;
RECT rcStart;
// 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))
{
ncm.lfCaptionFont.lfWeight = FW_BOLD;
hfontStart = CreateFontIndirect(&ncm.lfCaptionFont);
hfontStartOld = SelectObject(hdcStart , hfontStart);
}
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));
GetTextExtentPoint(hdcStart, szStart, lstrlen(szStart), &size);
// 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);
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);
// 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;
}
VOID _GetSaveStateAndInitRects()
{
TVSD tvsd;
DWORD cbData = SIZEOF(tvsd);
if (Reg_GetStruct(g_hkeyExplorer, c_szCabinetStuckRects, c_szSettings, &tvsd, &cbData) &&
(cbData == SIZEOF(tvsd)) && (tvsd.dwSize == SIZEOF(tvsd)))
{
// Now lets validate the information that was returned
if ((tvsd.dyTop < 0) || tvsd.dyTop > g_cyScreen / 2)
tvsd.dyTop = g_ts.sizeStart.cy + (g_cyDlgFrame + g_cxBorder) * 2;
if ((tvsd.dyBottom < 0) || tvsd.dyBottom > g_cyScreen / 2)
tvsd.dyBottom = g_ts.sizeStart.cy + (g_cyDlgFrame + g_cxBorder) * 2;
if ((tvsd.dxLeft < 0) || tvsd.dxLeft > g_cxScreen / 2)
tvsd.dxLeft = g_ts.sizeStart.cx + (g_cxDlgFrame + g_cyBorder) * 2;
if ((tvsd.dxRight < 0) || tvsd.dxRight > g_cxScreen / 2)
tvsd.dxRight = g_ts.sizeStart.cx + (g_cxDlgFrame + g_cyBorder) * 2;
// Restore the autohide rectangles and settings also.
g_ts.uAutoHide = (UINT)tvsd.uAutoHide;
g_ts.rc.left = (int)tvsd.rclAutoHide.left;
g_ts.rc.right = (int)tvsd.rclAutoHide.right;
g_ts.rc.top = (int)tvsd.rclAutoHide.top;
g_ts.rc.bottom = (int)tvsd.rclAutoHide.bottom;
g_ts.uStuckPlace = (UINT)tvsd.uStuckPlace;
g_ts.fAlwaysOnTop = tvsd.dwFlags & TVSD_TOPMOST ? 1 : 0;
g_ts.fSMSmallIcons = tvsd.dwFlags & TVSD_SMSMALLICONS ? 1 : 0;
g_ts.fHideClock = tvsd.dwFlags & TVSD_HIDECLOCK ? 1 : 0;
}
else
{
// setup for default rects, when it failed.
tvsd.cxScreen = g_cxScreen;
tvsd.cyScreen = g_cyScreen;
// Now get the sizes for the four edges.
tvsd.dyTop = tvsd.dyBottom = g_ts.sizeStart.cy +
(g_cyDlgFrame + g_cxBorder) * 2;
tvsd.dxLeft = tvsd.dxRight = g_ts.sizeStart.cx +
(g_cxDlgFrame + g_cyBorder) * 2;
// this is just a sanity check. if there were
// no settings, then auto-hide is off, and
// this rect will be initialized if/when autohide is turned on
// (unless we have a bug....)
g_ts.rc.left = 0;
g_ts.rc.right = g_cxScreen;
g_ts.rc.bottom = g_cyScreen;
g_ts.rc.top = g_cyScreen - 20;
// the ever changing default
g_ts.uStuckPlace = STICK_BOTTOM;
g_ts.fAlwaysOnTop = TRUE;
g_ts.fHideClock = FALSE;
}
// Now set the rects to match the sizes that are in our save record
GetWindowRect(GetDesktopWindow(), &g_ts.arStuckRects[STICK_TOP]);
InflateRect(&g_ts.arStuckRects[STICK_TOP], g_cxEdge, g_cyEdge);
g_ts.arStuckRects[STICK_BOTTOM] = g_ts.arStuckRects[STICK_TOP];
g_ts.arStuckRects[STICK_LEFT] = g_ts.arStuckRects[STICK_TOP];
g_ts.arStuckRects[STICK_RIGHT] = g_ts.arStuckRects[STICK_TOP];
g_ts.arStuckRects[STICK_TOP].bottom = (int)tray_yScreen + tvsd.dyTop;
g_ts.arStuckRects[STICK_BOTTOM].top = tray_cyScreen - (int)tvsd.dyBottom;
g_ts.arStuckRects[STICK_LEFT].right = (int)tray_xScreen + tvsd.dxLeft;
g_ts.arStuckRects[STICK_RIGHT].left = tray_cxScreen - (int)tvsd.dxRight;
// See if the screen resolution changed
if ((g_cxScreen != tvsd.cxScreen) || (g_cyScreen != tvsd.cyScreen))
Tray_ScreenSizeChange(NULL);
}
/*------------------------------------------------------------------
** align toolbar so that buttons are flush with client area
** and make toolbar's buttons to be MENU style
**------------------------------------------------------------------*/
void AlignStartButton(HWND hwndStart)
{
if (hwndStart)
{
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;
}
SetWindowPos(hwndStart, NULL, 0, 0, g_ts.sizeStart.cx, g_ts.sizeStart.cy, SWP_NOZORDER | SWP_NOACTIVATE);
}
}
//----------------------------------------------------------------------------
// Allow us to do stuff on a "button-down".
LRESULT CALLBACK StartButtonSubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// PFileCabinet pfc;
LRESULT lRet;
static UINT uDown = 0;
Assert(g_ButtonProc)
// Is the button going down?
if (uMsg == BM_SETSTATE)
{
if (wParam) {
// DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
// Yep, Is it already down?
if (!uDown)
{
// Nope.
uDown = 1;
// Show the button down.
lRet = CallWindowProc(g_ButtonProc, hWnd, uMsg, wParam, lParam);
// Notify the parent.
SendMessage(GetParent(hWnd), WM_COMMAND,
(WPARAM)LOWORD(GetDlgCtrlID(hWnd)), (LPARAM)hWnd);
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?
if (uDown == 1)
{
// Yep, do nothing.
uDown = 2;
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
else
{
// Nope, Forward it on.
uDown = 0;
return CallWindowProc(g_ButtonProc, hWnd, uMsg, wParam, lParam);
}
}
}
else
{
if (uMsg == 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);
}
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_MakeStartButton(HWND hwndTray)
{
HWND hwnd;
HBITMAP hbm;
// BUGBUG: BS_CENTER | VS_VCENTER required, user bug?
hwnd = CreateWindowEx(0, c_szButton, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
BS_PUSHBUTTON | BS_BITMAP | BS_LEFT | BS_VCENTER,
0, 0, 0, 0, hwndTray, (HMENU)IDC_START, hinstCabinet, NULL);
if (hwnd) {
// Subclass it.
g_ts.hwndStart = hwnd;
g_ButtonProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)(FARPROC)StartButtonSubclassWndProc);
hbm = CreateStartBitmap(hwndTray);
if (hbm) {
SendMessage(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (DWORD)hbm);
AlignStartButton(hwnd);
return hwnd;
}
DestroyWindow(hwnd);
}
return NULL;
}
void Tray_GetWindowSizes(PRECT prcClient, PRECT prcView, PRECT prcClock)
{
int xFSView, yFSView, cxFSView, cyFSView;
int xClock, yClock, cxClock, cyClock;
DWORD dwClockMinSize;
// 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;
dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE,
g_ts.sizeStart.cx, 0);
cxClock = LOWORD(dwClockMinSize);
cyClock = HIWORD(dwClockMinSize);
#ifdef ALLOW_CLOCK_ON_RIGHT
//
// we're now disallowing this case because the clock would
// jump to the right when a notify hits and makes the clock
// too wide to fit under the clock
//
if ((g_ts.sizeStart.cy + cyClock + g_cyTabSpace) <= prcClient->bottom)
{
// put it below
xClock = 0;
if (cxClock < g_ts.sizeStart.cx)
cxClock = g_ts.sizeStart.cx;
yClock = g_ts.sizeStart.cy + g_cyTabSpace;
xFSView = cxClock + g_cxFrame;
cxFSView = prcClient->right - xFSView;
}
else
{
#endif
// Recalc the min size for horizontal arrangement
dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE,
0x7fff, 0);
cxClock = LOWORD(dwClockMinSize);
cyClock = HIWORD(dwClockMinSize);
// xClock = prcClient->right - cxClock - g_cxFrame; // A little gap...
xClock = prcClient->right - cxClock;
#ifdef VCENTER_CLOCK
yClock = (prcClient->bottom-cyClock)/2; // Center it vertically.
#else
yClock = 0;
#endif
xFSView = g_ts.sizeStart.cx + g_cxFrame;
cxFSView = xClock - xFSView - g_cxFrame;
#ifdef ALLOW_CLOCK_ON_RIGHT
}
#endif
}
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, 0);
cxClock = LOWORD(dwClockMinSize);
cyClock = HIWORD(dwClockMinSize);
if ((g_ts.sizeStart.cx + cxClock + 4 * g_cyTabSpace) < prcClient->right)
{
int cyMax = g_ts.sizeStart.cy;;
// Can fit on the same row!
xClock = prcClient->right - cxClock - (2 * g_cxEdge); // 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, 0);
cxClock = min(LOWORD(dwClockMinSize), prcClient->right);
cyClock = HIWORD(dwClockMinSize);
xClock = (prcClient->right - cxClock) / 2;
yClock = prcClient->bottom - cyClock - g_cyTabSpace;
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;
}
void _TrayRestoreWindowPos(HWND hWnd)
{
WINDOWPLACEMENT wp;
//first restore the stuck postitions
_GetSaveStateAndInitRects();
wp.length = SIZEOF(wp);
g_ts.uMoveStuckPlace = g_ts.uStuckPlace;
wp.rcNormalPosition = g_ts.arStuckRects[g_ts.uStuckPlace];
wp.showCmd = SW_HIDE;
SendMessage(g_ts.hwndNotify, TNM_HIDECLOCK, 0, g_ts.fHideClock);
SetWindowPlacement(hWnd, &wp);
}
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL Reg_GetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID pData, DWORD *pcbData)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
DWORD dwType;
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKeyEx(hkey, pszSubKey, 0L, KEY_READ, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, (LPVOID)pszValue, 0, &dwType, pData, pcbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL Reg_SetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID 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;
}
/*-------------------------------------------------------------------
** the screen size has changed, so the docked rectangles need to be
** adjusted to the new screen. since only the bottom and right change,
** only need to increment/decrement certain edges.
**-------------------------------------------------------------------*/
void ResizeStuckRects(RECT *arStuckRects)
{
int xChange, yChange;
xChange = tray_cxScreen - arStuckRects[STICK_RIGHT].right;
yChange = tray_cyScreen - arStuckRects[STICK_BOTTOM].bottom;
arStuckRects[STICK_TOP].right = tray_cxScreen;
arStuckRects[STICK_LEFT].bottom = tray_cyScreen;
arStuckRects[STICK_BOTTOM].top += yChange;
arStuckRects[STICK_BOTTOM].right = tray_cxScreen;
arStuckRects[STICK_BOTTOM].bottom = tray_cyScreen;
arStuckRects[STICK_RIGHT].left += xChange;
arStuckRects[STICK_RIGHT].right = tray_cxScreen;
arStuckRects[STICK_RIGHT].bottom = tray_cyScreen;
}
//----------------------------------------------------------------------------
STDAPI Tasks_CreateInstance(REFIID riid, void **ppv);
//---------------------------------------------------------------------------
// Create the tray IShellView instance
HWND Tray_CreateShellView(PFileCabinet pfc)
{
if (FAILED(Tasks_CreateInstance(&IID_IShellView, &pfc->psv)))
return NULL; // This is very bad
if (FAILED(pfc->psv->lpVtbl->CreateViewWindow(pfc->psv, NULL, NULL, &pfc->sb, NULL, &pfc->hwndView)))
{
Assert(pfc->hwndView == NULL); // the failure case shouls set this
// pfc->hwndView = NULL;
}
return pfc->hwndView;
}
// tray IShellBrowser vtbl
IShellBrowserVtbl s_TraySBVtbl = {
// *** IUnknown methods ***
CFileCabinet_QueryInterface,
CFileCabinet_AddRef,
CFileCabinet_Release,
// *** IOleWindow methods ***
CFileCabinet_GetWindow,
ShellDesktop_ContextSensitiveHelp,
// *** IShellBrowser methods ***
ShellDesktop_InsertMenus,
ShellDesktop_SetMenu,
ShellDesktop_RemoveMenus,
ShellDesktop_SetStatusText,
CFileCabinet_EnableModeless,
CFileCabinet_TranslateAccelerator,
// *** IShellBrowser methods ***
ShellDesktop_BrowseObject,
CDeskTray_GetViewStateStream,
ShellDesktop_GetControlWindow,
ShellDesktop_SendControlMsg,
CFileCabinet_QueryActiveShellView,
ShellDesktop_OnViewWindowActive,
ShellDesktop_SetToolbarItems,
};
void 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(c_szBIOSDevice,
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;
}
return;
#endif
}
/* Check if system is currently in a docking station.
*
* Returns: TRUE if docked, FALSE if undocked or can't tell.
*
*/
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;
// 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(FALSE);
#endif
}
#undef HEAP_SHARED
const TCHAR c_szREGSTR_ROOT_APM[] = REGSTR_KEY_ENUM TEXT("\\") REGSTR_KEY_ROOTENUM TEXT("\\") REGSTR_KEY_APM TEXT("\\") REGSTR_DEFAULT_INSTANCE;
const TCHAR c_szREGSTR_BIOS_APM[] = REGSTR_KEY_ENUM TEXT("\\") REGSTR_KEY_BIOSENUM TEXT("\\") REGSTR_KEY_APM;
const TCHAR c_szREGSTR_VAL_APMMENUSUSPEND[] = REGSTR_VAL_APMMENUSUSPEND;
/* Open the registry APM device key
*/
BOOL OpenAPMKey(HKEY *phKey)
{
HKEY hBiosSys;
BOOL rc = FALSE;
TCHAR szInst[MAX_PATH+1];
DWORD cchInst = ARRAYSIZE(szInst);
// Open HKLM\Enum\Root\*PNP0C05\0000 - This is the APM key for
// non-PnP BIOS machines.
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szREGSTR_ROOT_APM, phKey) == ERROR_SUCCESS)
return TRUE;
// Open HKLM\Enum\BIOS\*PNP0C05, Enum the 1st subkey, open that. Example:
// HKLM\Enum\BIOS\*PNP0C05\03.
if (RegOpenKey(HKEY_LOCAL_MACHINE,c_szREGSTR_BIOS_APM,&hBiosSys) == ERROR_SUCCESS)
{
if (RegEnumKey(hBiosSys, 0, szInst, cchInst) == ERROR_SUCCESS &&
RegOpenKey(hBiosSys, szInst, phKey) == ERROR_SUCCESS)
rc = TRUE;
RegCloseKey(hBiosSys);
}
return rc;
}
/* Determine if "Suspend" should appear on the Tray menu.
*
* Returns: TRUE if Suspend should appear on menu, FALSE if not.
* Sets g_ts.fSuspendUndocked if Suspend should be disabled
* when system is docked.
*/
BOOL IsSuspendAllowed(void)
{
HKEY hkey;
DWORD dwType;
BOOL fRet = TRUE;
BYTE bMenuSuspend = APMMENUSUSPEND_ENABLED;
DWORD cbSize = SIZEOF(bMenuSuspend);
#ifdef WINNT // BUGBUG - Fix this when NT gets APM.
return FALSE;
#else
/* Check the Registry APM key for an APMMenuSuspend value.
* APMMenuSuspend may have the following values: APMMENUSUSPEND_DISABLED,
* APMMENUSUSPEND_ENABLED, or APMMENUSUSPEND_UNDOCKED.
*
* An APMMenuSuspend value of APMMENUSUSPEND_DISABLED means the
* tray should never show the Suspend menu item on its menu.
*
* APMMENUSUSPEND_ENABLED means the Suspend menu item should be shown
* if the machine has APM support enabled (VPOWERD is loaded). This is
* the default.
*
* APMMENUSUSPEND_UNDOCKED means the Suspend menu item should be shown,
* but only enabled when the machine is not in a docking station.
*
*/
if (OpenAPMKey(&hkey))
{
if (RegQueryValueEx(hkey, (LPTSTR)c_szREGSTR_VAL_APMMENUSUSPEND, 0,
&dwType, &bMenuSuspend, &cbSize) == ERROR_SUCCESS)
{
bMenuSuspend &= ~(APMMENUSUSPEND_NOCHANGE); // don't care about nochange flag
if (bMenuSuspend == APMMENUSUSPEND_UNDOCKED)
g_ts.fSuspendUndocked = TRUE;
else
{
g_ts.fSuspendUndocked = FALSE;
if (bMenuSuspend == APMMENUSUSPEND_DISABLED)
fRet = FALSE;
}
}
RegCloseKey(hkey);
}
if (fRet) {
DWORD dwPmLevel, cbOut;
BOOL fIoSuccess;
// Disable Suspend menu item if 1) only wanted when undocked and
// system is currently docked, 2) power mgnt level < advanced
if (g_ts.fSuspendUndocked && g_ts.DockedState == DOCKSTATE_DOCKED)
fRet = FALSE;
else
{
#ifdef WINNT // BUGBUG - Fix this when NT does APM
fRet = FALSE;
#else
fIoSuccess = DeviceIoControl(g_ts.hVPowerD,
VPOWERD_IOCTL_GET_PM_LEVEL, NULL, 0,
&dwPmLevel, SIZEOF(dwPmLevel), &cbOut,
NULL);
fRet = (fIoSuccess && (dwPmLevel==PMLEVEL_ADVANCED));
#endif
}
}
return fRet;
#endif
}
void Tray_VerifySize(PFileCabinet pfc, BOOL fWinIni)
{
RECT rc;
RECT rcView;
GetWindowRect(pfc->hwndMain, &rc);
Tray_HandleSizing(pfc, 0, &rc, 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)) {
// stash the old view size;
GetClientRect(pfc->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;
}
// now snap this size.
Tray_HandleSizing(pfc, 0, &rc, g_ts.uStuckPlace);
}
}
g_ts.fSelfSizing = TRUE;
SetWindowPos(pfc->hwndMain, NULL, rc.left, rc.top, RECTWIDTH(rc),RECTHEIGHT(rc), SWP_NOZORDER|SWP_NOACTIVATE);
StuckSizeChange();
g_ts.fSelfSizing = FALSE;
}
//----------------------------------------------------------------------------
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 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 RegisterDesktopNotify(PFileCabinet pfc)
{
LPITEMIDLIST pidl;
DebugMsg(DM_TRACE, TEXT("c.rdn: Notify for desktop."));
if (!g_ts.uDesktopNotify)
{
pidl = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
if (pidl)
{
g_ts.uDesktopNotify = _RegisterNotify(pfc->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(pfc->hwndMain, WMTRAY_DESKTOPCHANGE, pidl, FALSE);
ILFree(pidl);
}
}
}
LRESULT Tray_OnCreate(HWND hWnd, LPCREATESTRUCT lpcs)
{
PFileCabinet pfc;
MINIMIZEDMETRICS mm;
v_hwndTray = hWnd;
mm.cbSize = SIZEOF(mm);
SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
mm.iArrange |= ARW_HIDE;
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
UpdateDockingFlags();
g_ts.DockedState = GetDockedState();
// Try to open a handle to VPOWERD if the Suspend menu item is allowed.
// Failure to open VPOWERD will cause the Suspend item to be deleted
// from the menu later.
#ifndef WINNT // BUGBUG - Fix this when NT gets APM
g_ts.hVPowerD = CreateFile(c_szPOWERDevice,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
#endif
pfc = CreateSimpleFileCabinet(hWnd, &s_TraySBVtbl);
if (pfc)
{
SetPFC(hWnd, pfc);
g_pfcTray = pfc;
pfc->hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_TRAY));
pfc->pidl = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, FALSE);
// get a pointer to the tray's information packet
pfc->hwndToolbar = Tray_MakeStartButton(hWnd);
if (pfc->hwndToolbar)
{
// Clock
if (CreateClockWindow(hWnd))
{
//
// We need to set the tray position, before creating
// the view window, because it will call back our
// GetWindowRect member functions.
//
_TrayRestoreWindowPos(hWnd);
g_ts.hwndTrayTips = CreateWindow(c_szSToolTipsClass, c_szNULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hinstCabinet,
NULL);
SetWindowPos(g_ts.hwndTrayTips, HWND_TOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
if (g_ts.hwndTrayTips) {
HWND hwndClock;
TOOLINFO ti;
ti.cbSize = SIZEOF(ti);
ti.uFlags = TTF_IDISHWND;
ti.hwnd = hWnd;
ti.uId = (UINT)pfc->hwndToolbar;
ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_STARTBUTTONTIP);
ti.hinst = hinstCabinet;
SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
if (NULL != (hwndClock = (HWND)SendMessage(g_ts.hwndNotify, TNM_GETCLOCK, 0, 0L))) {
ti.uFlags = 0;
ti.uId = (UINT)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);
}
}
if (Tray_CreateShellView(pfc))
{
// we need to be unhidden to verify our size
TrayUnhide();
Tray_VerifySize(pfc, FALSE);
Tray_SizeWindows(); // size after all windows created
#ifdef WINNT
{
// On NT, load different start menu background bitmaps
// for workstation vs. server installations
NT_PRODUCT_TYPE type;
RtlGetNtProductType(&type);
if (type == NtProductWinNt)
{
g_ts.hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(IDB_STARTBKG));
}
else
{
g_ts.hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(IDB_SERVERSTARTBKG));
}
}
#else
g_ts.hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(IDB_STARTBKG));
#endif
if (g_ts.hbmpStartBkg)
{
UpdateWindow(hWnd);
StartMenu_Build(pfc);
CStartDropTarget_Register(pfc);
PrintNotify_Init(hWnd);
if (CheckAssociations())
CheckWinIniForAssocs();
RegisterDesktopNotify(pfc);
return 1; // success
}
else
{
DebugMsg(DM_ERROR, TEXT("c.t_oc: Unable to load start menu background."));
}
}
}
}
}
return -1; // failure
}
BOOL IsHwndObscured(HWND hwnd)
{
if (IsWindowVisible(hwnd)) {
RECT rc;
HWND hwndHit;
POINT pt;
GetWindowRect(hwnd, &rc);
if (rc.left < 0 && 0 < rc.right)
rc.left = 0;
if (rc.top < 0 && 0 < rc.bottom)
rc.top = 0;
pt.x = rc.left;
pt.y = rc.top;
hwndHit = WindowFromPoint(pt);
if (hwndHit == hwnd ||
IsChild(hwnd, hwndHit) ||
IsChild(hwndHit, hwnd)) {
DebugMsg(DM_TRACE, TEXT("hwnd (%d) is NOT obscured"), hwnd);
return FALSE;
}
}
DebugMsg(DM_TRACE, TEXT("hwnd (%d) is obscured"), hwnd);
return TRUE;
}
void DeskTray_DestroyShellView(PFileCabinet pfc)
{
if (pfc->psv)
{
pfc->psv->lpVtbl->DestroyViewWindow(pfc->psv);
pfc->psv->lpVtbl->Release(pfc->psv);
pfc->hwndView = NULL;
pfc->psv = NULL;
}
}
void Tray_HandleFullScreenApp(BOOL fFullScreen, HWND hwnd)
{
// in autohide mode, only drop if the window activated has no sysmenu
if (hwnd) {
if (fFullScreen && (g_ts.uAutoHide & AH_ON) &&
GetWindowLong(hwnd, GWL_STYLE) & WS_SYSMENU) {
//safe to bail here. fFullScreen is true so we're guaranteed to
// get another notification later with fFullScreen = FALSE
fFullScreen = FALSE;
}
}
// only do this if the bit has changed and we're marked for always on top
if (v_hwndTray) {
if (g_ts.fAlwaysOnTop &&
(fFullScreen ? TRUE : FALSE) != (g_ts.fRudeApp ? TRUE : FALSE)) {
SetWindowPos(v_hwndTray,
fFullScreen ? HWND_BOTTOM : HWND_TOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
AppBarNotifyAll(ABN_FULLSCREENAPP, NULL, fFullScreen);
g_ts.fRudeApp = fFullScreen ? TRUE : FALSE;
if (fFullScreen && !IsHwndObscured(v_hwndTray)) {
// they lied.
Tray_HandleFullScreenApp(FALSE, hwnd);
return;
}
}
// Notify clock we are hiding to avoid doing all the paints etc
if (!(g_ts.uAutoHide & AH_ON)) {
BOOL fStopClock;
// before we're willing to stop the clock, make sure we're really obscured
fStopClock = g_ts.fRudeApp &&
IsHwndObscured((HWND)SendMessage(g_ts.hwndNotify, TNM_GETCLOCK, 0, 0));
ClockCtl_HandleTrayHide(fStopClock);
}
}
}
/*------------------------------------------------------------------
** Create the tray, toolbar, and ListView windows, get the borders together,
** size to the appropriate shape, and show it.
**------------------------------------------------------------------*/
BOOL InitTray(HINSTANCE hInst)
{
// initalize globals that need to be non-zero
g_ts.wThreadCmd = SMCT_DONE;
g_ts.hBIOS = g_ts.hVPowerD = 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;
}
v_hwndTray = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW,
c_szTrayClass, NULL,
WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_THICKFRAME,
0, 0, 100, 100, NULL, NULL, hInst, NULL);
if (v_hwndTray) {
ShowWindow(v_hwndTray, SW_SHOW);
// no obey the always on top flag
SetWindowPos(v_hwndTray,
g_ts.fAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
UpdateWindow(v_hwndTray);
StuckSizeChange();
// this kick starts autohide
if (g_ts.uAutoHide & AH_ON) {
// register it
if (!IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace)) {
SetAutoHideState(FALSE);
ShellMessageBox(hinstCabinet, v_hwndTray, MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
MAKEINTRESOURCE(IDS_TASKBAR),MB_OK|MB_ICONSTOP);
} else {
if (!(g_ts.uAutoHide & AH_HIDING)) {
SetAutoHideTimer();
}
}
}
// this needs to be done here because some of the hotkeys rely on
// the tray
RegisterGlobalHotkeys();
// Init the user defined hotkeys too.
HotkeyList_Restore(v_hwndTray);
return TRUE;
} else
return FALSE;
}
//----------------------------------------------------------------------------
BOOL WINAPI Reg_GetString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPTSTR psz, DWORD cb)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
DWORD dwType;
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, (LPVOID)pszValue, 0, &dwType, (LPBYTE) psz, &cb) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
//----------------------------------------------------------------------------
BOOL WINAPI Reg_SetString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPCTSTR psz)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
if (RegCreateKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS)
{
if (RegSetValueEx(hkeyNew, pszValue, 0, REG_SZ, (LPBYTE) psz, (lstrlen(psz)+1)*sizeof(TCHAR)) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
TCHAR c_szRegPathIniExtensions[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Extensions");
//----------------------------------------------------------------------------
// Returns TRUE if there is a proper shell\open\command for the given
// extension that matches the given command line.
// NB This is for MGX Designer which registers an extension and commands for
// printing but relies on win.ini extensions for Open. We need to detect that
// there's no open command and add the appropriate entries to the registry.
// If the given extension maps to a type name we return that in pszTypeName
// otherwise it will be null.
// FMH This also affects Asymetric Compel which makes a new .CPL association.
// We need to merge it up into our Control Panel .CPL association. We depend
// on Control Panels NOT having a proper Open so users can see both verb sets.
// NB pszLine is the original line from win.ini eg foo.exe /bar ^.fred, see
// comments below...
BOOL Reg_ShellOpenForExtension(LPCTSTR pszExt, LPTSTR pszCmdLine,
LPTSTR pszTypeName, int cchTypeName, LPCTSTR pszLine)
{
TCHAR sz[MAX_PATH];
TCHAR szExt[MAX_PATH];
LONG cb;
if (pszTypeName)
pszTypeName[0] = TEXT('\0');
// Is the extension registed at all?
cb = SIZEOF(sz);
sz[0] = TEXT('\0');
if (RegQueryValue(HKEY_CLASSES_ROOT, pszExt, sz, &cb) == ERROR_SUCCESS)
{
// Is there a file type?
if (*sz)
{
// Yep, check there.
// DebugMsg(DM_TRACE, "c.r_rofe: Extension has a file type name %s.", sz);
lstrcpy(szExt, sz);
if (pszTypeName)
lstrcpyn(pszTypeName, sz, cchTypeName);
}
else
{
// No, check old style associations.
// DebugMsg(DM_TRACE, "c.r_rofe: Extension has no file type name.", pszExt);
lstrcpy(szExt, pszExt);
}
// See if there's an open command.
lstrcat(szExt, TEXT("\\"));
lstrcat(szExt, c_szShellOpenCommand);
cb = SIZEOF(sz);
if (RegQueryValue(HKEY_CLASSES_ROOT, szExt, sz, &cb) == ERROR_SUCCESS)
{
// DebugMsg(DM_TRACE, "c.r_rofe: Extension %s already registed with an open command.", pszExt);
// NB We want to compare the paths only, not the %1 stuff.
if (PathIsRelative(pszCmdLine))
{
int cch;
// If a relative path was passed in, we may have a fully qualifed
// one that is now in the registry... In that case we should
// say that it matches...
LPTSTR pszT = PathGetArgs(sz);
if (pszT)
{
*(pszT-1) = TEXT('\0');
}
PathUnquoteSpaces(sz);
PathRemoveBlanks(pszCmdLine);
cch = lstrlen(sz) - lstrlen(pszCmdLine);
if ((cch >= 0) && (lstrcmpi(sz+cch, pszCmdLine) == 0))
{
// DebugMsg(DM_TRACE, "c.r_rofe: Open commands match.");
return TRUE;
}
lstrcat(pszCmdLine, c_szSpace); // Append blank back on...
}
else
{
// If absolute path we can cheat for matches
*(sz+lstrlen(pszCmdLine)) = TEXT('\0');
if (lstrcmpi(sz, pszCmdLine) == 0)
{
// DebugMsg(DM_TRACE, "c.r_rofe: Open commands match.");
return TRUE;
}
}
// DebugMsg(DM_TRACE, "c.r_rofe: Open commands don't match.");
// Open commands don't match, check to see if it's because the ini
// changed (return FALSE so the change is reflected in the registry) or
// if the registry changed (return TRUE so we keep the registry the way
// it is.
if (Reg_GetString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, pszExt, sz, SIZEOF(sz)))
{
if (lstrcmpi(sz, pszLine) == 0)
return TRUE;
}
return FALSE;
}
else
{
// DebugMsg(DM_TRACE, "c.r_rofe: Extension %s already registed but with no open command.", pszExt);
return FALSE;
}
}
// DebugMsg(DM_TRACE, "c.r_rofe: No open command for %s.", pszExt);
return FALSE;
}
//----------------------------------------------------------------------------
const TCHAR c_szDotDGW[] = TEXT(".dgw");
const TCHAR c_szDesignWDotExeSpace[] = TEXT("designw.exe ");
const TCHAR c_szHDesign[] = TEXT("HDesign");
//----------------------------------------------------------------------------
BOOL _PathIsExe(LPCTSTR pszPath)
{
TCHAR szPath[MAX_PATH];
lstrcpy(szPath, pszPath);
PathRemoveBlanks(szPath);
return PathIsExe(szPath);
}
//----------------------------------------------------------------------------
// Return TRUE for exe, com, bat, pif and lnk.
BOOL ReservedExtension(LPCTSTR pszExt)
{
TCHAR szExt[5]; // Dot+ext+null.
lstrcpyn(szExt, pszExt, ARRAYSIZE(szExt));
PathRemoveBlanks(szExt);
if (PathIsExe(szExt) || (lstrcmpi(szExt, c_szDotLnk) == 0))
{
return TRUE;
}
return FALSE;
}
//----------------------------------------------------------------------------
// This function will read in the extensions section of win.ini to see if
// there are any old style associations that we have not accounted for.
// NB Some apps screw up if their extensions magically disappear from the
// extensions section so DON'T DELETE the old entries from win.ini.
#define CWIFA_SIZE 4096
void CheckWinIniForAssocs(void)
{
LPTSTR pszBuf;
int cbRet;
LPTSTR pszLine;
TCHAR szExtension[MAX_PATH];
TCHAR szTypeName[MAX_PATH];
TCHAR szCmdLine[MAX_PATH];
LPTSTR pszExt;
LPTSTR pszT;
BOOL fAssocsMade = FALSE;
szExtension[0]=TEXT('.');
szExtension[1]=TEXT('\0');
// BUGBUG - BobDay - This code doesn't handle larger section
pszBuf = (LPTSTR)LocalAlloc(LPTR, CWIFA_SIZE*SIZEOF(TCHAR));
if (!pszBuf)
return; // Could not allocate the memory
cbRet = (int)GetProfileSection(c_szExtensions, pszBuf, CWIFA_SIZE);
//
// We now walk through the list to find any items that is not
// in the registry.
//
for (pszLine = pszBuf; *pszLine; pszLine += lstrlen(pszLine)+1)
{
// Get the extension for this file into a buffer.
pszExt = StrChr(pszLine, TEXT('='));
if (pszExt == NULL)
continue; // skip this line
szExtension[0]=TEXT('.');
// lstrcpyn will put the null terminator for us.
// We should now have something like .xls in szExtension.
lstrcpyn(szExtension+1, pszLine, (int)(pszExt-pszLine)+1);
// Ignore extensions bigger than dot + 3 chars.
if (lstrlen(szExtension) > 4)
{
DebugMsg(DM_ERROR, TEXT("c.cwia: Invalid extension, skipped."));
continue;
}
pszLine = pszExt+1; // Points to after the =;
while (*pszLine == TEXT(' '))
pszLine++; // skip blanks
// Now find the ^ in the command line.
pszExt = StrChr(pszLine, TEXT('^'));
if (pszExt == NULL)
continue; // dont process
// Now setup the command line
// WARNING: This assumes only 1 ^ and it assumes the extension...
lstrcpyn(szCmdLine, pszLine, (int)(pszExt-pszLine)+1);
// Don't bother moving over invalid entries (like the busted .hlp
// entry VB 3.0 creates).
if (!_PathIsExe(szCmdLine))
{
DebugMsg(DM_ERROR, TEXT("c.cwia: Invalid app, skipped."));
continue;
}
if (ReservedExtension(szExtension))
{
DebugMsg(DM_ERROR, TEXT("c.cwia: Invalid extension (%s), skipped."), szExtension);
continue;
}
// Now see if there is already a mapping for this extension.
if (Reg_ShellOpenForExtension(szExtension, szCmdLine, szTypeName, ARRAYSIZE(szTypeName), pszLine))
{
// Yep, Setup the initial list of ini extensions in the registry if they are
// not there already.
if (!Reg_GetString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, szTypeName, SIZEOF(szTypeName)))
{
Reg_SetString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, pszLine);
}
continue;
}
// No mapping.
// HACK for Expert Home Design. They put an association in win.ini
// (which we propagate as typeless) but then register a type and a
// print command the first time they run - stomping on our propagated
// Open command. The fix is to put their open command under the proper
// type instead of leaving it typeless.
if (lstrcmpi(szExtension, c_szDotDGW) == 0)
{
if (lstrcmpi(PathFindFileName(szCmdLine), c_szDesignWDotExeSpace) == 0)
{
// Put in a ProgID for them.
RegSetValue(HKEY_CLASSES_ROOT, szExtension, REG_SZ, c_szHDesign, 0L);
// Force Open command under their ProgID.
DebugMsg(DM_TRACE, TEXT("c.cwifa: Expert Home Design special case hit."));
lstrcpy(szTypeName, c_szHDesign);
}
}
//
// HACK for Windows OrgChart which does not register OLE1 class
// if ".WOC" is registered in the registry.
//
if (lstrcmpi(szExtension, TEXT(".WOC")) == 0)
{
if (lstrcmpi(PathFindFileName(szCmdLine), TEXT("WINORG.EXE ")) == 0)
{
DebugMsg(DM_ERROR, TEXT("c.cwia: HACK: Found WINORG (%s, %s), skipped."), szExtension, pszLine);
continue;
}
}
// Record that we're about to move things over in the registry so we won't keep
// doing it all the time.
Reg_SetString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, pszLine);
lstrcat(szCmdLine, TEXT("%1"));
// see if there are anything else to copy out...
pszExt++; // get beyond the ^
pszT = szExtension;
while (*pszExt && (CharLower((LPTSTR)(DWORD)*pszExt) == CharLower((LPTSTR)(DWORD)*pszT)))
{
// Look for the next character...
pszExt++;
pszT++;
}
if (*pszExt)
lstrcat(szCmdLine, pszExt); // add the rest onto the command line
// Now lets make the actual association.
// We need to add on the right stuff onto the key...
if (*szTypeName)
lstrcpy(szExtension, szTypeName);
lstrcat(szExtension, TEXT("\\"));
lstrcat(szExtension, c_szShellOpenCommand);
RegSetValue(HKEY_CLASSES_ROOT, szExtension, REG_SZ, szCmdLine, 0L);
// DebugMsg(DM_TRACE, "c.cwifa: %s %s", szExtension, szCmdLine);
fAssocsMade = TRUE;
}
// If we made any associations we should let the cabinet know.
//
// Now call off to the notify function.
if (fAssocsMade)
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
// And cleanup our allocation
LocalFree((HLOCAL)pszBuf);
}
//---------------------------------------------------------------------------
void _InitAllSubMenuPopups(HMENU hmenu)
{
HMENU hmenuSub;
int i;
// DebugMsg(DM_IANELHK, "c.iasmp: Initing %x.", hmenu);
if (!hmenu)
return;
if (g_ts.fThreadTerminate)
{
return;
}
FileMenu_InitMenuPopup(hmenu);
for (i = GetMenuItemCount(hmenu) - 1; i >= 0 ; i--)
{
if (g_ts.fThreadTerminate)
{
break;
}
if (NULL != (hmenuSub = GetSubMenu(hmenu, i)))
{
FileMenu_InitMenuPopup(hmenuSub);
_InitAllSubMenuPopups(hmenuSub);
}
}
}
//---------------------------------------------------------------------------
void WINAPI FillFileMenus(int wID, BOOL fRecurse)
{
HMENU hmenu = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, wID);
int i;
if (!hmenu)
return;
if (g_ts.fThreadTerminate)
{
DebugMsg(DM_IANELHK, TEXT("c.ffm:...Aborted."));
return;
}
FileMenu_InitMenuPopup(hmenu);
for (i = GetMenuItemCount(hmenu) - 1; i >= 0 ; i--)
{
HMENU hmenuSub;
if (g_ts.fThreadTerminate)
break;
if (NULL != (hmenuSub = GetSubMenu(hmenu, i)))
{
if (fRecurse)
_InitAllSubMenuPopups(hmenuSub);
else
FileMenu_InitMenuPopup(hmenuSub);
}
}
// DebugMsg(DM_IANELHK, "c.ffm:...Done");
}
//---------------------------------------------------------------------------
ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl, BOOL fRecursive)
{
SHChangeNotifyEntry fsne;
ULONG lReturn;
// DebugMsg(DM_TRACE, "c.rn: Notify for %s.", (LPSTR)lpszPath);
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-enumuerate 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 that support IShellLink, not just .lnk files
//
WORD _GetHotkeyFromPidls(LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
WORD wHotkey = 0;
LPITEMIDLIST pidl = ILCombine(pidlFolder, pidlItem);
if (pidl)
{
TCHAR szPath[MAX_PATH];
if (SHGetPathFromIDList(pidl, szPath))
{
IShellLink *psl;
CLSID clsid;
// BUGBUG: we really should call GetClassFile() but this could
// slow this down a lot... so chicken out and just look in the registry
if (FAILED(_CLSIDFromExtension(PathFindExtension(szPath), &clsid)))
clsid = CLSID_ShellLink; // assume it's a shell link
if (SUCCEEDED(ICoCreateInstance(&clsid, &IID_IShellLink, &psl)))
{
IPersistFile *ppf;
if (SUCCEEDED(psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf)))
{
WCHAR wszPath[MAX_PATH];
StrToOleStr(wszPath, szPath);
ppf->lpVtbl->Load(ppf, wszPath, 0);
ppf->lpVtbl->Release(ppf);
}
psl->lpVtbl->GetHotkey(psl, &wHotkey);
#ifdef DEBUG
// if (wHotkey)
// DebugMsg(DM_TRACE, "c.x: %s has hotkey %x.", szPath, wHotkey);
#endif
psl->lpVtbl->Release(psl);
}
}
ILFree(pidl);
}
return wHotkey;
}
//----------------------------------------------------------------------------
UINT HotkeyList_GetFreeItemIndex(void)
{
int i, cItems;
PHOTKEYITEM phki;
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, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem, BOOL fClone)
{
LPITEMIDLIST pidl1, pidl2;
if (wHotkey)
{
HOTKEYITEM hki;
int i = HotkeyList_GetFreeItemIndex();
Assert(g_ts.hdsaHKI);
// 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 = pidl1;
hki.pidlItem = 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(g_ts.hdsaHKI);
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 i, cItems;
PHOTKEYITEM phki;
LPITEMIDLIST pidl;
int cCached = 0;
int cbData = 0;
TCHAR szValue[32];
LPBYTE pData;
Assert(g_ts.hdsaHKI);
DebugMsg(DM_TRACE, TEXT("c.hl_s: Saving global hotkeys."));
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;
DebugMsg(DM_TRACE, TEXT("c.hl.r: 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.
SHRegDeleteKey(g_hkeyExplorer, c_szHotkeys);
}
return i;
}
//----------------------------------------------------------------------------
// We get called back for each item added to a filemenu.
void CALLBACK FileMenu_AddItemCallback(LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
WORD wHotkey = _GetHotkeyFromPidls(pidlFolder, pidlItem);
if (wHotkey)
{
int i = HotkeyList_Add(wHotkey, pidlFolder, pidlItem, TRUE);
if (i != -1)
{
// Register in the context of the tray's thread.
PostMessage(v_hwndTray, WMTRAY_REGISTERHOTKEY, i, 0);
}
}
}
//----------------------------------------------------------------------------
// NB Again, this takes window hotkey not a Global one.
// NB This doesn't delete cached hotkeys.
int HotkeyList_Remove(WORD wHotkey)
{
int i, cItems;
PHOTKEYITEM phki;
WORD wGHotkey;
// 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;
// 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;
}
//----------------------------------------------------------------------------
// We get called back when a submenu is delete.
void CALLBACK FileMenu_InvalidateCallback(LPITEMIDLIST pidlFolder)
{
int i, cItems;
PHOTKEYITEM phki;
// DebugMsg(DM_IANELHK, "c.gm_ic: Invalidating items for %x", pidlFolder);
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
if (phki && (phki->pidlFolder == pidlFolder))
{
// DebugMsg(DM_IANELHK, "c.gm_ic: Invalidating %d", i);
phki->wGHotkey = 0;
phki->pidlFolder = NULL;
phki->pidlItem = NULL;
PostMessage(v_hwndTray, WMTRAY_UNREGISTERHOTKEY, i, 0);
}
}
}
//----------------------------------------------------------------------------
void CALLBACK FileMenu_Callback(LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
// A null item means that this is a delete of all the filemenu items in the
// given folder.
if (pidlItem)
FileMenu_AddItemCallback(pidlFolder, pidlItem);
else
FileMenu_InvalidateCallback(pidlFolder);
}
//----------------------------------------------------------------------------
// 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;
}
//----------------------------------------------------------------------------
// Stick the contents of the "favourite" folder at the top of the start menu.
void StartMenu_InsertFavouriteItems(PFileCabinet pfc, HMENU hmenu)
{
LPITEMIDLIST pidlFav;
UINT cItems;
UINT fFilter = SHCONTF_NONFOLDERS;
Assert(pfc);
// create it so that we have soemthingto register notifies on
pidlFav = SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE);
if (pidlFav)
{
if (!SHRestricted(REST_NOSTARTMENUSUBFOLDERS))
fFilter |= SHCONTF_FOLDERS;
cItems = FileMenu_InsertUsingPidl(hmenu, IDM_STARTMENU, pidlFav,
FMF_NOEMPTYITEM | FMF_NOPROGRAMS, fFilter, FileMenu_Callback);
if (!g_ts.uFavNotify)
g_ts.uFavNotify = RegisterNotify(pfc->hwndMain, WMTRAY_FAVCHANGE, pidlFav);
// If there are any items, insert a sep.
if (cItems)
FileMenu_AppendItem(hmenu, (LPTSTR)FMAI_SEPARATOR, 0, -1, NULL, 0);
ILFree(pidlFav);
}
if (!SHRestricted(REST_NOCOMMONGROUPS)) {
// Favorites from the common profile
// create it so that we have soemthingto register notifies on
pidlFav = SHCloneSpecialIDList(NULL, CSIDL_COMMON_STARTMENU, TRUE);
if (pidlFav)
{
cItems = FileMenu_AppendFilesForPidl(hmenu, pidlFav, FALSE);
if (!g_ts.uCommonFavNotify)
g_ts.uCommonFavNotify = RegisterNotify(pfc->hwndMain, WMTRAY_COMMONFAVCHANGE, pidlFav);
// If there are any items, insert a sep.
if (cItems)
FileMenu_AppendItem(hmenu, (LPTSTR)FMAI_SEPARATOR, 0, -1, NULL, 0);
ILFree(pidlFav);
}
}
}
//----------------------------------------------------------------------------
BOOL EnumFolder_GetHotkeys(LPSHELLFOLDER psf, HWND hwndOwner,
LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
WORD wHotkey;
DebugMsg(DM_TRACE, TEXT("c.ef_gh: ..."));
if (g_ts.fThreadTerminate)
return FALSE;
wHotkey = _GetHotkeyFromPidls(pidlFolder, pidlItem);
if (wHotkey)
{
int i = HotkeyList_Add(wHotkey, pidlFolder, pidlItem, TRUE);
if (i != -1)
{
// Register in the context of the tray's thread.
PostMessage(v_hwndTray, WMTRAY_REGISTERHOTKEY, i, 0);
}
}
return TRUE;
}
//----------------------------------------------------------------------------
void GetDesktopHotkeys(void)
{
LPITEMIDLIST pidlDesktop = NULL;
LPITEMIDLIST pidlCommonDesktop = NULL;
int i, cItems;
PHOTKEYITEM phki;
DebugMsg(DM_TRACE, TEXT("c.gdh: Global hotkeys from desktop."));
pidlDesktop = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
pidlCommonDesktop = SHCloneSpecialIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
if (pidlDesktop && pidlCommonDesktop)
{
// Clean up all existing (non-cached) desktop hotkeys.
cItems = DSA_GetItemCount(g_ts.hdsaHKI);
for (i=0; i<cItems; i++)
{
phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
if (phki && phki->wGHotkey)
{
if (!(phki->wFlags & HKIF_CACHED) && (phki->wFlags & HKIF_FREEPIDLS)
&& phki->pidlFolder &&
(ILIsEqual(phki->pidlFolder, pidlDesktop) ||
ILIsEqual(phki->pidlFolder, pidlCommonDesktop)))
{
DebugMsg(DM_IANELHK, TEXT("c.gdh: Invalidating %d"), i);
if (phki->pidlFolder)
ILFree(phki->pidlFolder);
if (phki->pidlItem)
ILFree(phki->pidlItem);
phki->wGHotkey = 0;
phki->pidlFolder = NULL;
phki->pidlItem = NULL;
PostMessage(v_hwndTray, WMTRAY_UNREGISTERHOTKEY, i, 0);
}
}
}
// Add them back.
EnumFolder(NULL, pidlDesktop, SHCONTF_NONFOLDERS, (PFNENUMFOLDERCALLBACK)EnumFolder_GetHotkeys);
if (!SHRestricted(REST_NOCOMMONGROUPS)) {
EnumFolder(NULL, pidlCommonDesktop, SHCONTF_NONFOLDERS, (PFNENUMFOLDERCALLBACK)EnumFolder_GetHotkeys);
}
}
if (pidlDesktop)
ILFree(pidlDesktop);
if (pidlCommonDesktop)
ILFree(pidlCommonDesktop);
DebugMsg(DM_TRACE, TEXT("c.gdh: done."));
}
//----------------------------------------------------------------------------
#define BTF_DONELISTOFPATHS 0x0001
//----------------------------------------------------------------------------
DWORD WINAPI StartMenu_BkgThread(LPVOID lpv)
{
static UINT btflags = 0;
// Handle faults a bit better.
_try
{
while (!g_ts.fThreadTerminate)
{
switch (g_ts.wThreadCmd)
{
case SMCT_PRIMARY:
case SMCT_WININIASSOCS:
break;
case SMCT_BUILDLISTOFPATHS:
if (!(btflags & BTF_DONELISTOFPATHS))
{
RLBuildListOfPaths();
btflags |= BTF_DONELISTOFPATHS;
}
break;
case SMCT_INITPROGRAMS:
Tray_HandleInitMenuPopup(Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_PROGRAMSINIT));
break;
case SMCT_INITRECENT:
Tray_HandleInitMenuPopup(Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_RECENTINIT));
break;
case SMCT_PARTFILLPROGRAMS:
FillFileMenus(IDM_PROGRAMS, FALSE);
break;
case SMCT_FILLRECENT:
FillFileMenus(IDM_RECENT, FALSE);
break;
case SMCT_FILLPROGRAMS:
FillFileMenus(IDM_PROGRAMS, TRUE);
break;
case SMCT_DESKTOPHOTKEYS:
GetDesktopHotkeys();
break;
case SMCT_FILLFAVOURITE:
_InitAllSubMenuPopups(g_ts.hmenuStart);
break;
case SMCT_DONE:
goto Done;
case SMCT_FILLRECENTONLY:
FillFileMenus(IDM_RECENT, TRUE);
goto Done;
case SMCT_FILLPROGRAMSONLY:
FillFileMenus(IDM_PROGRAMS, TRUE);
goto Done;
case SMCT_DESKTOPHOTKEYSONLY:
GetDesktopHotkeys();
goto Done;
default:
DebugMsg(DM_ERROR, TEXT("c.sm_bt: Invalid thread command %d."), g_ts.wThreadCmd);
goto Done;
}
// If we're interupted we will need to repeat the command last command otherwise
// move on to the next command.
// NB Commands up to BUILDLISTOFPATHS aren't interuptable so don't need restarting.
if (!g_ts.fThreadTerminate || (g_ts.wThreadCmd <= SMCT_BUILDLISTOFPATHS))
g_ts.wThreadCmd++;
}
}
_except (SetErrorMode(SEM_NOGPFAULTERRORBOX),
UnhandledExceptionFilter(GetExceptionInformation()))
{
// Put up a nice error box.
ShellMessageBox(hinstCabinet, NULL, MAKEINTRESOURCE(IDS_EXCEPTIONMSG),
MAKEINTRESOURCE(IDS_CABINET), MB_ICONEXCLAMATION|MB_SETFOREGROUND);
}
Done:
if (g_ts.fThreadTerminate)
{
DebugMsg(DM_IANELHK, TEXT("c.sm_bt: Thread %d forced to terminate (%d)."), g_ts.hThread, g_ts.wThreadCmd);
}
else
{
g_ts.wThreadCmd = SMCT_DONE;
// DebugMsg(DM_IANELHK, "c.sm_bt: Thread %d completed normally.", g_ts.hThread);
}
Assert(g_ts.hThread);
CloseHandle(g_ts.hThread);
g_ts.hThread = NULL;
return 0;
}
//----------------------------------------------------------------------------
void Thread_CreateLowPri(void)
{
DWORD idThread;
LPFileCabinet pfc;
Assert(!g_ts.hThread);
pfc = g_pfcTray;
if (pfc && (pfc->bMainMenuInit))
{
DebugMsg(DM_IANELHK, TEXT("c.t_clp: Can't create thread, StartMenu is up or being modified."));
return;
}
g_ts.hThread = CreateThread(NULL, 0, StartMenu_BkgThread, NULL, 0, &idThread);
if (g_ts.hThread)
{
// DebugMsg(DM_IANELHK, "c.t_clp: Thread %d created.", g_ts.hThread);
SetThreadPriority(g_ts.hThread, THREAD_PRIORITY_BELOW_NORMAL);
}
else
{
DebugMsg(DM_ERROR, TEXT("c.t_clp: Failed to create thread."));
}
}
//----------------------------------------------------------------------------
BOOL WINAPI StartMenu_ControlBkgThread(STARTMENUCONTROLTHREAD smct)
{
BOOL fRet = FALSE;
switch (smct)
{
case SMCT_STOP:
DebugMsg(DM_IANELHK, TEXT("c.sm_cbt: Stop thread."));
if (!g_ts.hThread)
{
DebugMsg(DM_IANELHK, TEXT("c.sm_cbt: Thread not running."));
fRet = TRUE;
}
else
{
DebugMsg(DM_IANELHK, TEXT("c.sm_cbt: Waiting for %x to complete."), g_ts.hThread);
// Speed up stopping the filemenu.
FileMenu_AbortInitMenu();
g_ts.fThreadTerminate = TRUE;
if (WaitForSingleObject(g_ts.hThread, 1000) == WAIT_TIMEOUT)
{
DECLAREWAITCURSOR;
SetWaitCursor();
DebugMsg(DM_ERROR, TEXT("c.: Still waiting for %x."), g_ts.hThread);
if (MsgWaitForMultipleObjectsLoop(g_ts.hThread, 10*1000) == WAIT_TIMEOUT)
{
DebugMsg(DM_ERROR, TEXT("c.sm_cbt: Background thread timed-out."));
}
else
{
DebugMsg(DM_ERROR, TEXT("c.sm_cbt: Background thread finally completed."));
}
ResetWaitCursor();
}
else
{
DebugMsg(DM_ERROR, TEXT("c.sm_cbt: Background thread completed."));
}
g_ts.fThreadTerminate = FALSE;
// CloseHandle(g_ts.hThread);
// Assert that the terminated thread reset g_ts.hThread.
Assert(!g_ts.hThread);
}
fRet = TRUE;
break;
case SMCT_RESTART:
// DebugMsg(DM_IANELHK, "c.cm_st: Restart thread.");
if (g_ts.wThreadCmd == SMCT_DONE)
{
// DebugMsg(DM_IANELHK, "c.cm_st: Nothing to restart.");
fRet = TRUE;
}
else
{
DebugMsg(DM_IANELHK, TEXT("c.cm_st: Restarting with %d."), g_ts.wThreadCmd);
if (!g_ts.hThread)
Thread_CreateLowPri();
else
DebugMsg(DM_ERROR, TEXT("c.dm_cbt: Thread already running."));
}
break;
case SMCT_PRIMARY:
case SMCT_FILLPROGRAMSONLY:
case SMCT_FILLRECENTONLY:
#ifdef DEBUG
DebugMsg(DM_IANELHK, TEXT("c.cm_st: Handle %s stuff."),
SMCT_FILLRECENTONLY == smct ? TEXT("recent") :
SMCT_FILLPROGRAMSONLY == smct ? TEXT("programs") :
TEXT("primary")
);
#endif
g_ts.wThreadCmd = (WORD) smct;
if (!g_ts.hThread)
Thread_CreateLowPri();
break;
case SMCT_DESKTOPHOTKEYSONLY:
g_ts.wThreadCmd = (WORD) smct;
if (!g_ts.hThread)
Thread_CreateLowPri();
break;
}
return fRet;
}
//---------------------------------------------------------------------------
// Add all the items on menuSrc onto the end of menuDst where menu dst is
// a filemenu and menusrc is an ordinary menu. The initial image index to
// use for all non-seperator items is given by iDefImage. If it's -1 then
// no items will get images.
BOOL StartMenu_CatMenu(HMENU hmenuDst, HMENU hmenuSrc, int iDefImage, UINT cyItem)
{
int i, iMax, iImage;
TCHAR szText[CCHSZNORMAL];
HMENU hmenuSubCopy;
// Recursion would be bad.
if (hmenuDst == hmenuSrc)
return FALSE;
iMax = GetMenuItemCount(hmenuSrc);
if (iMax != -1)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
for (i=0; i<iMax; i++)
{
mii.dwTypeData = szText;
mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_STATE | MIIM_DATA;
mii.cch = ARRAYSIZE(szText);
mii.hSubMenu = NULL;
mii.fType = MFT_SEPARATOR; // to avoid ramdom result.
mii.dwItemData = 0;
GetMenuItemInfo(hmenuSrc, i, TRUE, &mii);
// Give the settings sub menu's the right icons.
if ((i == 0) && (iDefImage == -1) && (mii.wID == IDM_CONTROLS))
iDefImage = II_STCPANEL;
if (mii.wID == IDM_FILERUN)
iDefImage = II_STRUN;
// NB Take out the icons and remove these id's when we're sure
// the Start Menu is stable.
// Skip programs(start menu) icon.
if (iDefImage == II_STSPROGS)
iDefImage++;
// Skip fonts icon.
if (iDefImage == II_STFONTS)
iDefImage++;
iImage = iDefImage;
// Make sure the find menu items have a default image.
if ((mii.wID >= TRAY_IDM_FINDFIRST) && (mii.wID <= TRAY_IDM_FINDLAST) && !mii.dwItemData)
mii.dwItemData = II_STFIND;
if (mii.fType & MFT_SEPARATOR)
{
FileMenu_AppendItem(hmenuDst, (LPTSTR)FMAI_SEPARATOR, mii.wID, -1, NULL, 0);
}
else if (mii.hSubMenu)
{
// Submenu.
hmenuSubCopy = FileMenu_Create((COLORREF)-1, 0, NULL, 0, 0);
// NB Only allow small submenu's for now.
StartMenu_CatMenu(hmenuSubCopy, mii.hSubMenu, -1, 0);
FileMenu_AppendItem(hmenuDst, szText, mii.wID, iImage, hmenuSubCopy, cyItem);
if (iDefImage != -1)
iDefImage++;
}
else
{
if ((iDefImage == -1) && (mii.dwItemData != 0))
{
FileMenu_AppendItem(hmenuDst, szText, mii.wID, mii.dwItemData, NULL, cyItem);
}
else
{
FileMenu_AppendItem(hmenuDst, szText, mii.wID, iImage, NULL, cyItem);
}
if (iDefImage != -1)
iDefImage++;
}
}
return TRUE;
}
return FALSE;
}
//----------------------------------------------------------------------------
// Remove restricted items and stuff that is specific to a particular hardware
// platform.
void StartMenu_RemoveUnavailableItems(void)
{
HMENU hmenu;
UINT iSep2ItemsMissing = 0;
// APM/Dockable stuff.
if (!IsEjectAllowed() || g_ts.DockedState == DOCKSTATE_UNDOCKED)
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_EJECTPC);
iSep2ItemsMissing++;
}
if (g_ts.hVPowerD == INVALID_HANDLE_VALUE || !IsSuspendAllowed())
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_SUSPEND);
iSep2ItemsMissing++;
}
// Restictions
if (SHRestricted(REST_NORUN))
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_FILERUN);
}
if (SHRestricted(REST_NOCLOSE))
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_EXITWIN);
iSep2ItemsMissing++;
}
if (iSep2ItemsMissing == 3)
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_SEP2);
}
// Setting stuff.
if (SHRestricted(REST_NOSETTASKBAR) || SHRestricted(REST_NOSETFOLDERS))
{
hmenu = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_CONTROLS);
if (hmenu)
{
// Both?
if (SHRestricted(REST_NOSETTASKBAR) && SHRestricted(REST_NOSETFOLDERS))
{
// Yep, toast the whole settings menu.
FileMenu_DeleteMenuItemByFirstID(g_ts.hmenuStart, IDM_CONTROLS);
}
else
{
// Nope, just one.
if (SHRestricted(REST_NOSETFOLDERS))
{
FileMenu_DeleteItemByCmd(hmenu, IDM_CONTROLS);
// FileMenu_DeleteItemByCmd(hmenu, IDM_PROGRAMSFOLDER);
FileMenu_DeleteItemByCmd(hmenu, IDM_PRINTERS);
// FileMenu_DeleteItemByCmd(hmenu, IDM_FONTS);
}
else if (SHRestricted(REST_NOSETTASKBAR))
{
FileMenu_DeleteItemByCmd(hmenu, IDM_TRAYPROPERTIES);
}
// Delete the seperator.
FileMenu_DeleteSeparator(hmenu);
}
}
else
{
DebugMsg(DM_ERROR, TEXT("c.fm_rui: Settings menu couldn't be found. Restricted items could not be removed."));
}
}
// Find menu.
if (SHRestricted(REST_NOFIND))
{
FileMenu_DeleteItemByCmd(g_ts.hmenuStart, IDM_MENU_FIND);
}
}
//----------------------------------------------------------------------------
// Make sure the menu fits on the screen. Remove items from the top if it
// doesn't.
void StartMenu_LimitHeight(void)
{
int i, j, cItems;
int cyMenu, cyMenuMax;
Assert(g_ts.hmenuStart);
cItems = GetMenuItemCount(g_ts.hmenuStart);
Assert(cItems);
cyMenu = 0;
cyMenuMax = GetSystemMetrics(SM_CYSCREEN);
// Stop when the menu gets to big.
for (i=cItems; i>0; i--)
{
cyMenu += HIWORD(FileMenu_GetItemExtent(g_ts.hmenuStart, i-1));
if (cyMenu > cyMenuMax)
break;
}
// Delete overflow items.
for (j=i; j>0; j--)
{
FileMenu_DeleteItemByIndex(g_ts.hmenuStart, j-1);
}
}
//----------------------------------------------------------------------------
void StartMenu_Build(PFileCabinet pfc)
{
HMENU hMenu, hMenuSub;
// DWORD idThread;
// Keep these menu's around - it's no longer safe to just delete
// them willy-nilly.
Assert(!g_ts.hmenuStart);
Assert(!g_ts.hThread);
hMenu = LoadMenu(hinstCabinet, MAKEINTRESOURCE(MENU_START));
if (hMenu)
{
hMenuSub = GetSubMenu(hMenu, 0);
if (hMenuSub)
{
// initialize the find extensions
// check to see if it's the find menu
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_SUBMENU|MIIM_ID;
if (GetMenuItemInfo(hMenuSub, IDM_MENU_FIND, FALSE, &mii))
{
DebugMsg(DM_TRACE, TEXT("InitMenuPopup of Find commands"));
pfc->pcmFind = SHFind_InitMenuPopup(mii.hSubMenu, pfc->hwndMain, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST);
} else {
Assert(0);
}
if (g_ts.fSMSmallIcons)
g_ts.hmenuStart = FileMenu_Create((COLORREF)-1, 0, NULL, 0, FMF_NOBREAK);
else
g_ts.hmenuStart = FileMenu_Create(RGB(0,0,0), 21, g_ts.hbmpStartBkg, 0, FMF_LARGEICONS|FMF_NOBREAK);
if (g_ts.hmenuStart)
{
// List for global hotkeys.
HotkeyList_Create();
// Startmenu items.
StartMenu_InsertFavouriteItems(pfc, g_ts.hmenuStart);
// Default stuff.
StartMenu_CatMenu(g_ts.hmenuStart, hMenuSub, II_STPROGS, g_ts.fSMSmallIcons ? 0 : g_cyIcon);
// Remove stuff.
StartMenu_RemoveUnavailableItems();
// Limit it's height.
StartMenu_LimitHeight();
// Build programs menu's in the background.
StartMenu_ControlBkgThread(SMCT_PRIMARY);
}
}
DestroyMenu(hMenu);
}
}
//----------------------------------------------------------------------------
void StartMenu_Destroy(LPFileCabinet pfc)
{
StartMenu_ControlBkgThread(SMCT_STOP);
FileMenu_Destroy(g_ts.hmenuStart);
if (pfc && pfc->pcmFind)
{
pfc->pcmFind->lpVtbl->Release(pfc->pcmFind);
pfc->pcmFind = NULL;
}
g_ts.hmenuStart = NULL;
}
//----------------------------------------------------------------------------
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);
}
int Tray_TrackMenu(PFileCabinet pfc, HMENU hmenu, BOOL fFileMenu)
{
TPMPARAMS tpm;
int iret;
tpm.cbSize = SIZEOF(tpm);
GetClientRect(pfc->hwndToolbar, &tpm.rcExclude);
MapWindowPoints(pfc->hwndToolbar, NULL, (LPPOINT)&tpm.rcExclude, 2);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
if (fFileMenu)
{
iret = FileMenu_TrackPopupMenuEx(hmenu, TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_RETURNCMD,
tpm.rcExclude.left, tpm.rcExclude.bottom, v_hwndTray, &tpm);
}
else
{
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(PFileCabinet pfc)
{
HMENU hmenuMain = NULL;
HMENU hMenu = NULL;
int idCmd;
// DWORD idThread;
SetActiveWindow(v_hwndTray);
#ifndef WINNT // BUGBUG - Fix this when NT gets docking ability
if (g_ts.hBIOS!=INVALID_HANDLE_VALUE)
{
FileMenu_EnableItemByCmd(g_ts.hmenuStart, IDM_EJECTPC,
g_ts.fDockingFlags&DOCKFLAG_WARMEJECTABLENOW);
}
#endif
// Keep the button down.
StartMenu_ControlBkgThread(SMCT_STOP);
if (g_ts.fFavInvalid || StartMenu_FontChange())
RebuildEntireMenu(pfc);
pfc->bMainMenuInit = TRUE;
idCmd = Tray_TrackMenu(pfc, g_ts.hmenuStart, TRUE);
pfc->bMainMenuInit = FALSE;
// This forces the button back up.
_ForceStartButtonUp();
// Restart the menu thread if needed.
StartMenu_ControlBkgThread(SMCT_RESTART);
if (idCmd) {
TrayCommand(pfc, idCmd); // execute if a menu was selected
}
}
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(LPRECT lprc)
{
int i;
if (g_ts.hdpaAppBars) {
i = DPA_GetPtrCount(g_ts.hdpaAppBars);
// can't use subtract rect because of fully inclusion limitation
while (i--) {
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
AppBarSubtractRect(pab, lprc);
}
}
}
void GetWorkArea(LPRECT lprcWAreaNew, LPRECT lprcFull, LPRECT lprcTray)
{
RECT rcWork;
// Calc size for new work areas. Only limit the screen if the tray is
// always on top and autohide is off.
if (!(g_ts.uAutoHide & AH_ON) && g_ts.fAlwaysOnTop)
{
SubtractRect(&rcWork, lprcFull, lprcTray);
} else {
rcWork = *lprcFull;
}
AppBarSubtractRects(&rcWork);
*lprcWAreaNew = rcWork;
}
/*------------------------------------------------------------------
** tell USER to limit the usable screen size because the tray is
** stuck to one of the sides.
**------------------------------------------------------------------*/
void LimitScreenSize(PRECT prTray)
{
RECT rcFull;
RECT rcWork;
RECT rcWAreaOld;
rcFull.top = rcFull.left = 0;
rcFull.right = g_cxScreen;
rcFull.bottom = g_cyScreen;
GetWorkArea(&rcWork, &rcFull, prTray);
// Did anything change?
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWAreaOld, 0);
if (!EqualRect(&rcWork, &rcWAreaOld))
{
DebugMsg(DM_TRACE, TEXT("c.lss: Changing desktop work area."));
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork, SPIF_SENDWININICHANGE);
}
}
/*------------------------------------------------------------------
** the tray has either just been stuck to an edge or has resized while
** in the stuck position. so adjust the world to this real estate
** guzzling action. Other windows in the system are moved out of the way.
**------------------------------------------------------------------*/
void StuckSizeChange()
{
GetWindowRect(v_hwndTray, &g_ts.arStuckRects[g_ts.uStuckPlace]);
LimitScreenSize(&g_ts.arStuckRects[g_ts.uStuckPlace]);
SendMessage(v_hwndDesktop, DTM_SIZEDESKTOP, 0, 0);
}
/*------------------------------------------------------------------
** 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;
rc = g_ts.arStuckRects[g_ts.uMoveStuckPlace];
lpwp->x = rc.left;
lpwp->y = rc.top;
lpwp->cx = rc.right - rc.left;
lpwp->cy = rc.bottom - rc.top;
// the size has changed
lpwp->flags &= ~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);
}
// remember the new state
g_ts.uStuckPlace = g_ts.uMoveStuckPlace;
if (g_ts.fSysSizing)
g_ts.rc = g_ts.arStuckRects[g_ts.uStuckPlace];
if (g_ts.uAutoHide & AH_ON) {
if (!IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace)) {
SetAutoHideState(FALSE);
ShellMessageBox(hinstCabinet, v_hwndTray, MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
MAKEINTRESOURCE(IDS_TASKBAR),MB_OK|MB_ICONSTOP);
}
}
}
void Tray_WhichStuckRect(int left, int top, int right, int bottom)
{
int i;
for (i = STICK_BOTTOM ; i >= STICK_LEFT ; i--) {
if (g_ts.arStuckRects[i].left == left &&
g_ts.arStuckRects[i].top == top &&
g_ts.arStuckRects[i].right == right &&
g_ts.arStuckRects[i].bottom == bottom) {
g_ts.uMoveStuckPlace = i;
}
}
}
void Tray_CalcStuckPlace(POINT pt)
{
int i;
int dx, dy;
int horiz, vert;
// BUGBUG go in backward order to encourage good positioning
// BUGBUG ideally, we want to have the order: b,t,l,r.
for(i = STICK_BOTTOM; i >= STICK_LEFT; i--)
{
if (PtInRect(&(g_ts.arStuckRects[i]), pt))
{
g_ts.uMoveStuckPlace = i;
break;
}
}
// not in the rect, see which side we're closest to.
if (pt.x < (g_cxScreen/2)) {
dx = pt.x;
horiz = STICK_LEFT;
} else {
dx = g_cxScreen - pt.x;
horiz = STICK_RIGHT;
}
if (pt.y < (g_cyScreen/2)) {
dy = pt.y;
vert = STICK_TOP;
} else {
dy = g_cyScreen - pt.y;
vert = STICK_BOTTOM;
}
// if ((dx / g_cxScreen) < (dy / g_cyScreen))
// same as above
if ((g_cxScreen * dy ) > (g_cyScreen * dx))
g_ts.uMoveStuckPlace = horiz;
else
g_ts.uMoveStuckPlace = vert;
}
/*------------------------------------------------------------------
** processing of the WM_MOVING message.
**
** if the cursor is in one of the docking positions, make the
** fuzzy rect look like the docked position. if not, the fuzzy
** rectangle is the size of the undocked tray.
**
** g_ts.uMoveStuckPlace is set here for general use.
** return: fill in rectangle with new size.
**------------------------------------------------------------------*/
void Tray_HandleMoving(LPRECT lprc)
{
POINT ptCursor;
GetCursorPos(&ptCursor);
Tray_CalcStuckPlace(ptCursor);
*lprc = g_ts.arStuckRects[g_ts.uMoveStuckPlace];
}
//------------------------------------------------------------------
// Size the icon area to fill as much of the tray window as it can.
//------------------------------------------------------------------
void Tray_SizeWindows()
{
RECT rcView, rcClock, rcClient;
PFileCabinet pfc = g_pfcTray;
if (!g_pfcTray || !pfc->hwndView || !pfc->hwndMain || !g_ts.hwndNotify)
return;
if (g_ts.uAutoHide & AH_HIDING) {
g_ts.fShouldResize = TRUE;
return;
}
GetClientRect(pfc->hwndMain, &rcClient);
Tray_GetWindowSizes(&rcClient, &rcView, &rcClock);
SetWindowPos(g_ts.hwndStart, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(pfc->hwndToolbar, NULL, 0, 0,
(rcClient.right < g_ts.sizeStart.cx) ? rcClient.right : g_ts.sizeStart.cx,
g_ts.sizeStart.cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
// position the view
SetWindowPos(pfc->hwndView, 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);
{
TOOLINFO ti;
HWND hwndClock = (HWND)SendMessage(g_ts.hwndNotify, TNM_GETCLOCK, 0, 0L);
ti.cbSize = SIZEOF(ti);
ti.uFlags = 0;
ti.hwnd = pfc->hwndMain;
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.uId = (UINT)hwndClock;
GetWindowRect(hwndClock, &ti.rect);
MapWindowPoints(HWND_DESKTOP, pfc->hwndMain, (LPPOINT)&ti.rect, 2);
SendMessage(g_ts.hwndTrayTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
}
//InvalidateRect(pfc->hwndView, NULL, TRUE);
}
void Tray_NegotiateSizing(PFileCabinet pfc, UINT code, PRECT prcClient)
{
RECT rcView, rcClock;
int iHeight;
// 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);
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
SendMessage(pfc->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;
// try again until it doesn't change
Tray_NegotiateSizing(pfc, code, prcClient);
}
}
int Window_GetClientGapHeight(HWND hwnd)
{
RECT rc;
SetRectEmpty(&rc);
AdjustWindowRectEx(&rc, GetWindowLong(hwnd, GWL_STYLE), FALSE, GetWindowLong(hwnd, GWL_EXSTYLE));
return RECTHEIGHT(rc);
}
/*-------------------------------------------------------------
** process the WM_SIZING message.
**
** nothing exciting. just don't let a docked tray get sized
** too big.
**-------------------------------------------------------------*/
void Tray_HandleSizing(PFileCabinet pfc, UINT code, LPRECT lprc, UINT uStuckPlace)
{
RECT rc;
int iHeight;
int iYExtra; // the amount of Y difference between the window and client height;
iYExtra = Window_GetClientGapHeight(pfc->hwndMain);
if (g_cyTrayBorders == -1)
g_cyTrayBorders = iYExtra; // on initial boot
// first do original size check.. don't let it be more than half the
// screen if it's docked.
switch (uStuckPlace)
{
case STICK_TOP:
lprc->top = tray_yScreen;
lprc->right = tray_cxScreen;
lprc->left = tray_xScreen;
if (lprc->bottom > g_cyScreen/2)
lprc->bottom = g_cyScreen/2;
if (RECTHEIGHT(*lprc) <= (g_cyTrayBorders)) {
// the minmaxsize will prevent us from getting too small
// allow
//lprc->bottom = lprc->top + g_cyFrame;
g_cyTrayBorders = iYExtra;
return;
}
break;
case STICK_BOTTOM:
lprc->right = tray_cxScreen;
lprc->left = tray_xScreen;
if (lprc->top > tray_cyScreen) {
lprc->top = tray_cyScreen - (lprc->bottom - lprc->top);
}
lprc->bottom = tray_cyScreen;
if (lprc->top < g_cyScreen/2)
lprc->top = g_cyScreen/2;
if (RECTHEIGHT(*lprc) <= (g_cyTrayBorders)) {
// the minmaxsize will prevent us from getting too small
// allow
//lprc->top = lprc->bottom - g_cyFrame;
g_cyTrayBorders = iYExtra;
return;
}
break;
// in these two cases, we return, not break because we don't want
// to arbitrate size if we're stuck to the right or left sides
case STICK_RIGHT:
lprc->top = tray_yScreen;
lprc->bottom = tray_cyScreen;
if (lprc->left > tray_cxScreen) {
lprc->top = tray_cxScreen - (lprc->right - lprc->left);
}
lprc->right = tray_cxScreen;
if (lprc->left < g_cxScreen/2)
lprc->left = g_cxScreen/2;
return;
case STICK_LEFT:
lprc->top = tray_yScreen;
lprc->bottom = tray_cyScreen;
lprc->left = tray_xScreen;
if (lprc->right > g_cxScreen/2)
lprc->right = g_cxScreen/2;
return;
}
/////
// next we go and negotiate the size with our children.
// Take into account changes in the border by keeping track of it.
// if (!g_cyTrayBorders)
// g_cyTrayBorders = Window_GetClientGapHeight(pfc->hwndMain);
// it doesn't matter which we take off from,
rc = *lprc;
rc.bottom -= g_cyTrayBorders;
Tray_NegotiateSizing(pfc, code, &rc);
// was there a change? If so, adjust the lprc
iHeight = RECTHEIGHT(rc);
if ((iHeight+iYExtra) != RECTHEIGHT(*lprc)) {
switch(uStuckPlace)
{
case STICK_TOP:
lprc->bottom = lprc->top + iHeight + iYExtra;
break;
case STICK_BOTTOM:
lprc->top = lprc->bottom - (iHeight + iYExtra);
break;
}
}
// Remember the current client gap.
g_cyTrayBorders = iYExtra;
}
/*-------------------------------------------------------------------
** 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.
**-------------------------------------------------------------------*/
void Tray_ScreenSizeChange(HWND hWnd)
{
// screen size changed, so we need to adjust globals
g_cxScreen = GetSystemMetrics(SM_CXSCREEN);
g_cyScreen = GetSystemMetrics(SM_CYSCREEN);
ResizeStuckRects(g_ts.arStuckRects);
// recompute the autohide rect
if (g_ts.uAutoHide & AH_ON) {
int x,y;
x = RECTWIDTH(g_ts.rc);
y = RECTHEIGHT(g_ts.rc);
g_ts.rc = g_ts.arStuckRects[g_ts.uStuckPlace];
switch(g_ts.uStuckPlace) {
case STICK_TOP:
g_ts.rc.bottom = g_ts.rc.top + y;
break;
case STICK_BOTTOM:
g_ts.rc.top = g_ts.rc.bottom - y;
break;
case STICK_RIGHT:
g_ts.rc.left = g_ts.rc.right - x;
break;
case STICK_LEFT:
g_ts.rc.right = g_ts.rc.left + x;
break;
}
}
if (hWnd) {
// fake up position globals to set "new" docking position
// since WINDOWPOSCHANGING will be used, fake up gmove_ data
// to make it look like things moved.
// "current" position is faked to be some other docked state
g_ts.uMoveStuckPlace = g_ts.uStuckPlace;
g_ts.uStuckPlace = (g_ts.uStuckPlace + 1) % 4;
//// set a bogus windowpos and actually repaint with the right
//// shape/size in handling the WINDOWPOSCHANGING message
SetWindowPos(hWnd, NULL,
g_ts.arStuckRects[g_ts.uMoveStuckPlace].left,
g_ts.arStuckRects[g_ts.uMoveStuckPlace].top,
g_ts.arStuckRects[g_ts.uMoveStuckPlace].right -
g_ts.arStuckRects[g_ts.uMoveStuckPlace].left,
g_ts.arStuckRects[g_ts.uMoveStuckPlace].bottom -
g_ts.arStuckRects[g_ts.uMoveStuckPlace].top,
SWP_NOZORDER | SWP_NOACTIVATE);
// we went to the stuck rects which means we hid if AH_ON is set
if (g_ts.uAutoHide & AH_ON)
g_ts.uAutoHide |= AH_HIDING;
}
// Space for StartMenu items may be different now.
// We'll need to rebuild the whole menu before showing it again.
g_ts.fFavInvalid = TRUE;
// if there's a rude app up and we're obscured, don't set the timer to rebuild
if (!(g_ts.fAlwaysOnTop && g_ts.fRudeApp && IsHwndObscured(v_hwndTray)))
{
// Use the "Update Favourite" timer to do it as
// soon as possible.
SetTimer(v_hwndTray, IDT_FAVOURITE, 2*1000, NULL);
}
Tray_SizeWindows();
}
void _UpdateAlwaysOnTop(BOOL fAlwaysOnTop)
{
//
// The user clicked on the AlwaysOnTop menu item, we should now toggle
// the state and update the window occordingly...
//
g_ts.fAlwaysOnTop = fAlwaysOnTop;
SetWindowPos(v_hwndTray,
g_ts.fAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
// Make sure the screen limits are update to the new state.
StuckSizeChange();
AppBarNotifyAll(ABN_STATECHANGE, NULL, 0);
}
void ViewOptionsUpdateDisplay(HWND hDlg);
void _ApplyViewOptionsFromDialog(LPPROPSHEETPAGE psp, HWND hDlg)
{
// We need to get the Cabinet structure from the property sheet info.
LPSHELLBROWSER psb = (LPSHELLBROWSER)(psp->lParam);
PFileCabinet pfc = IToClassN(CFileCabinet, sb, psb);
BOOL fSmallPrev;
BOOL fSmallNew;
BOOL fAutoHide;
// First check for Always on Top
_UpdateAlwaysOnTop(IsDlgButtonChecked(hDlg, IDC_TRAYOPTONTOP));
//
// And change the Autohide state
fAutoHide = IsDlgButtonChecked(hDlg, IDC_TRAYOPTAUTOHIDE);
if (!SetAutoHideState(fAutoHide) && fAutoHide) {
// we tried and failed.
if (!(g_ts.uAutoHide & AH_ON)) {
CheckDlgButton(hDlg, IDC_TRAYOPTAUTOHIDE, FALSE);
ViewOptionsUpdateDisplay(hDlg);
}
}
// show/hide the clock
g_ts.fHideClock = !IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWCLOCK);
SendMessage(g_ts.hwndNotify, TNM_HIDECLOCK, 0, g_ts.fHideClock);
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;
g_ts.fFavInvalid = TRUE;
SetTimer(v_hwndTray, IDT_FAVOURITE, 2*1000, NULL);
}
}
//---------------------------------------------------------------------------
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 f;
// 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.
SetWindowPos(GetDlgItem(hDlg, IDC_VOBASE), HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
ShowWindow(GetDlgItem(hDlg, IDC_VOLARGEMENU),
!IsDlgButtonChecked(hDlg, IDC_SMSMALLICONS) ? SW_SHOWNA : SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAY),
!(f = IsDlgButtonChecked(hDlg, IDC_TRAYOPTAUTOHIDE)) ? SW_SHOWNA : SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDC_VOTRAYNOCLOCK),
!f && !IsDlgButtonChecked(hDlg, IDC_TRAYOPTSHOWCLOCK) ? SW_SHOWNA : SW_HIDE);
ShowWindow(hwnd = GetDlgItem(hDlg, IDC_VOWINDOW),
(FALSE != (fShown = !IsDlgButtonChecked(hDlg, IDC_TRAYOPTONTOP))) ? SW_SHOWNA : SW_HIDE);
if (fShown)
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
#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 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, hwnd2;
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(hwnd2 = GetDlgItem(hDlg, IDC_VOTRAY), NULL,
ptBase.x + XVOTRAY, ptBase.y + YVOTRAY, CXVOTRAY, CYVOTRAY, SWP_NOACTIVATE);
SetWindowPos(hwnd = GetDlgItem(hDlg, IDC_VOTRAYNOCLOCK), HWND_TOP,
ptBase.x + XVOTRAYNOCLOCK, ptBase.y + YVOTRAYNOCLOCK, CXVOTRAYNOCLOCK, CYVOTRAYNOCLOCK, SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_VOWINDOW), NULL,
ptBase.x + XVOWINDOW, 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_TRAYOPTONTOP, IDH_TRAY_TASKBAR_ONTOP,
IDC_TRAYOPTAUTOHIDE, IDH_TRAY_TASKBAR_AUTOHIDE,
IDC_SMSMALLICONS, IDH_STARTMENU_SMALLICONS,
IDC_TRAYOPTSHOWCLOCK, IDH_TRAY_SHOW_CLOCK,
0, 0
};
BOOL CALLBACK TrayViewOptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
switch (uMsg) {
case WM_COMMAND:
ViewOptionsUpdateDisplay(hDlg);
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
break;
case WM_INITDIALOG:
SetWindowLong(hDlg, DWL_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);
ViewOptionsSizeControls(hDlg);
ViewOptionsInitBitmaps(hDlg);
ViewOptionsUpdateDisplay(hDlg);
break;
case WM_SYSCOLORCHANGE:
ViewOptionsInitBitmaps(hDlg);
return TRUE;
case WM_DESTROY:
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, (DWORD)(LPTSTR) aTaskOptionsHelpIDs);
break;
case WM_CONTEXTMENU:
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (DWORD)(LPVOID)aTaskOptionsHelpIDs);
break;
}
return FALSE;
}
// this will open the special view info stream for the "tray" incarnation
// of tray, rather than the cabinet's incarnation of the tray directory
STDMETHODIMP CDeskTray_GetViewStateStream(IShellBrowser * psb, DWORD grfMode, LPSTREAM *pStrm)
{
CFileCabinet * this = IToClassN(CFileCabinet, sb, psb);
// Now lets try to save away the other information associated with view.
// We need to figure out where we will store the information.
// If it is a local drive, we will save it in the ini file associated
// with that directory. If it is remote and we are opening to write
// we will look in our cache. If it is remote and we are opening for
// reading, we will first see if it is in our cache and if not we
// will read it from the remote one.
// And call off to get the stream associated with the path.
*pStrm = Cabinet_GetViewStreamForPidl(this->pidl, grfMode, c_szViewStreamInfo);
return *pStrm ? S_OK : E_OUTOFMEMORY;
}
BOOL CALLBACK InitStartMenuDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
/*-------------------------------------------------------------
** Method to add view property sheet pages to the tray property
** sheet
**-------------------------------------------------------------*/
STDMETHODIMP CShellTray_AddViewPropertySheetPages(IShellBrowser * psb,
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 = (LPARAM)psb;
hpage = CreatePropertySheetPage(&psp);
if (hpage)
lpfn(hpage, lParam);
psp.pszTemplate = MAKEINTRESOURCE(DLG_STARTMENU_CONFIG);
psp.pfnDlgProc = InitStartMenuDlgProc;
psp.lParam = (LPARAM)psb;
hpage = CreatePropertySheetPage(&psp);
if (hpage)
lpfn(hpage, lParam);
return(S_OK);
}
void _SaveTrayAndDesktop(void)
{
WINDOWPLACEMENT wp;
TVSD tvsd;
FOLDERSETTINGS fs;
PFileCabinet pfc;
DebugMsg(DM_TRACE, TEXT("Saving desktop state"));
if (SHRestricted(REST_NOSAVESET))
{
return;
}
Cabinet_SaveState(v_hwndDesktop, NULL, TRUE, FALSE, FALSE);
pfc = GetPFC(v_hwndDesktop);
pfc->psv->lpVtbl->GetCurrentInfo(pfc->psv, &fs);
Reg_SetStruct(g_hkeyExplorer, c_szCabinetDeskView, c_szSettings, &fs, SIZEOF(fs));
// And now the tray.
DebugMsg(DM_TRACE, TEXT("Saving tray state"));
HotkeyList_Save();
wp.length = SIZEOF(wp);
GetWindowPlacement(v_hwndTray, &wp);
Cabinet_SaveState(v_hwndTray, &wp, TRUE, FALSE, FALSE);
// These have now been combined into one structure - Review how to put into stream
tvsd.cxScreen = g_cxScreen;
tvsd.cyScreen = g_cyScreen;
// Now get the sizes for the four edges.
tvsd.dyTop = g_ts.arStuckRects[STICK_TOP].bottom - tray_yScreen;
tvsd.dyBottom = tray_cyScreen - g_ts.arStuckRects[STICK_BOTTOM].top;
tvsd.dxLeft = g_ts.arStuckRects[STICK_LEFT].right - tray_xScreen;
tvsd.dxRight = tray_cxScreen - g_ts.arStuckRects[STICK_RIGHT].left;
// And also stuff for autohide
tvsd.uAutoHide = g_ts.uAutoHide;
tvsd.rclAutoHide.left = g_ts.rc.left;
tvsd.rclAutoHide.right = g_ts.rc.right;
tvsd.rclAutoHide.top = g_ts.rc.top;
tvsd.rclAutoHide.bottom = g_ts.rc.bottom;
// The rest.
tvsd.uStuckPlace = g_ts.uStuckPlace;
tvsd.dwFlags = g_ts.fAlwaysOnTop ? TVSD_TOPMOST : 0;
if (g_ts.fSMSmallIcons) tvsd.dwFlags |= TVSD_SMSMALLICONS;
if (g_ts.fHideClock) tvsd.dwFlags |= TVSD_HIDECLOCK;
// Sanity check.
tvsd.dwSize = SIZEOF(tvsd);
// Save for now in Stuck rects.
Reg_SetStruct(g_hkeyExplorer, c_szCabinetStuckRects, c_szSettings, &tvsd, SIZEOF(tvsd));
}
void SlideWindow(HWND hwnd, RECT *prc)
{
RECT rcOld;
RECT rcNew;
int x,y,dx,dy,dt,t,t0;
BOOL fShow;
HANDLE me;
int priority;
rcNew = *prc;
if (g_fDragFullWindows && g_dtSlideShow>0 && g_dtSlideHide>0) {
GetWindowRect(hwnd, &rcOld);
fShow = (rcNew.bottom-rcNew.top) > (rcOld.bottom-rcOld.top) ||
(rcNew.right-rcNew.left) > (rcOld.right-rcOld.left);
dx = (rcNew.right-rcOld.right) + (rcNew.left - rcOld.left);
dy = (rcNew.bottom-rcOld.bottom) + (rcNew.top - rcOld.top);
Assert((rcOld.right-rcNew.right)==0 || (rcOld.left-rcNew.left)==0);
Assert((rcOld.bottom-rcNew.bottom)==0 || (rcOld.top-rcNew.top)==0);
Assert(dx == 0 || dy == 0);
if (fShow)
{
rcOld = rcNew;
OffsetRect(&rcOld, -dx, -dy);
SetWindowPos(hwnd, NULL, rcOld.left, rcOld.top,
rcOld.right - rcOld.left, rcOld.bottom - rcOld.top,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME);
dt = g_dtSlideShow;
}
else
{
dt = g_dtSlideHide;
}
me = GetCurrentThread();
priority = GetThreadPriority(me);
SetThreadPriority(me, THREAD_PRIORITY_HIGHEST);
t0 = GetTickCount();
while ((t = GetTickCount()) < t0 + dt)
{
x = rcOld.left + (dx) * (t - t0) / dt;
y = rcOld.top + (dy) * (t - t0) / dt;
SetWindowPos(hwnd, NULL, x, y, 0, 0,
SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
if (fShow)
UpdateWindow(hwnd);
}
SetThreadPriority(me, priority);
}
SetWindowPos(hwnd, NULL, rcNew.left, rcNew.top,
rcNew.right - rcNew.left, rcNew.bottom - rcNew.top,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_DRAWFRAME);
StuckSizeChange();
AppBarNotifyAll(ABN_POSCHANGED, NULL, 0);
}
void DoTrayUnhide()
{
g_ts.fSelfSizing = TRUE;
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
SlideWindow(v_hwndTray, &g_ts.rc);
////MoveWindow(v_hwndTray, g_ts.rc.left, g_ts.rc.top, g_ts.rc.right - g_ts.rc.left, g_ts.rc.bottom - g_ts.rc.top, TRUE);
UpdateWindow(v_hwndTray);
DAD_ShowDragImage(TRUE); // restore the lock state.
g_ts.fSelfSizing = FALSE;
ClockCtl_HandleTrayHide(FALSE);
}
void TrayHide()
{
RECT rcNew, rcOld;
GetWindowRect(v_hwndTray, &rcOld);
rcNew = rcOld;
// bugbug, what we really want to do is to force a bigger size,
// not just punt out on saving this new size.
if ((STUCK_HORIZONTAL(g_ts.uStuckPlace) &&
(RECTHEIGHT(rcOld) > g_cyFrame)) ||
(!STUCK_HORIZONTAL(g_ts.uStuckPlace) &&
(RECTWIDTH(rcOld) > g_cxFrame)))
g_ts.rc = rcOld;
switch (g_ts.uStuckPlace) {
case STICK_LEFT:
rcNew.right = rcNew.left + g_cxFrame;
break;
case STICK_RIGHT:
rcNew.left = rcNew.right - g_cxFrame;
break;
case STICK_TOP:
rcNew.bottom = rcNew.top + g_cyFrame;
break;
case STICK_BOTTOM:
rcNew.top = rcNew.bottom - g_cyFrame;
break;
}
g_ts.uAutoHide = AH_ON | AH_HIDING;
g_ts.fSelfSizing = TRUE;
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
SlideWindow(v_hwndTray, &rcNew);
////MoveWindow(v_hwndTray, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, TRUE);
DAD_ShowDragImage(TRUE); // restore the lock state.
ClockCtl_HandleTrayHide(TRUE);
g_ts.fSelfSizing = FALSE;
}
BOOL SetAutoHideState (BOOL fAutoHide)
{
if (!IAppBarSetAutoHideBar(v_hwndTray, fAutoHide, g_ts.uStuckPlace)) {
// unable to set it because there's another one there already.
ShellMessageBox(hinstCabinet, v_hwndTray, MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
MAKEINTRESOURCE(IDS_TASKBAR),MB_OK|MB_ICONSTOP);
return FALSE;
}
if (fAutoHide && !(g_ts.uAutoHide & AH_ON)) {
TrayHide();
} else if (!fAutoHide & (g_ts.uAutoHide & AH_ON)) {
g_ts.uAutoHide = 0;
KillTimer(v_hwndTray, IDT_AUTOHIDE);
DoTrayUnhide();
LimitScreenSize(&g_ts.arStuckRects[g_ts.uStuckPlace]);
} else {
return TRUE;
}
AppBarNotifyAll(ABN_STATECHANGE, NULL, 0);
return 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 SetAutoHideTimer()
{
if (g_ts.uAutoHide & AH_ON) {
SetTimer(v_hwndTray, IDT_AUTOHIDE, 500, NULL);
}
}
void Tray_HandleExitMenuLoop()
{
// when we leave the menu stuff, start checking again.
SetAutoHideTimer();
}
void TrayUnhide()
{
// handle autohide
if ((g_ts.uAutoHide & AH_ON) &&
(g_ts.uAutoHide & AH_HIDING)) {
DoTrayUnhide();
g_ts.uAutoHide &= ~AH_HIDING;
SetAutoHideTimer();
if (g_ts.fShouldResize) {
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 RebuildEntireMenu(PFileCabinet pfc)
{
DebugMsg(DM_IANELHK, TEXT("c.rfm: Cleaning up Frequent menu."));
// Delete all the start menu items.
pfc->bMainMenuInit = TRUE;
UnregisterNotify(g_ts.uFavNotify);
g_ts.uFavNotify = 0;
UnregisterNotify(g_ts.uCommonFavNotify);
g_ts.uCommonFavNotify = 0;
StartMenu_Destroy(pfc);
StartMenu_Build(pfc);
pfc->bMainMenuInit = FALSE;
DebugMsg(DM_IANELHK, TEXT("c.rfm: Done."));
g_ts.fFavInvalid = FALSE;
StartMenu_ControlBkgThread(SMCT_RESTART);
}
//----------------------------------------------------------------------------
void StartButton_ResetAndSize(PFileCabinet pfc)
{
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(pfc->hwndMain);
if (hbmNew)
{
SendMessage(g_ts.hwndStart, BM_SETIMAGE, IMAGE_BITMAP, (DWORD)hbmNew);
DeleteObject(hbmOld);
}
}
AlignStartButton(pfc->hwndToolbar);
Tray_SizeWindows();
}
//---------------------------------------------------------------------------
void Tray_OnDelayWinIniChange(PFileCabinet pfc)
{
DebugMsg(DM_TRACE, TEXT("c.t_odwic: Handling win ini change."));
TrayUnhide();
Tray_VerifySize(pfc, TRUE);
StartButton_ResetAndSize(pfc);
// We'll need to rebuild the whole menu before showing it again.
g_ts.fFavInvalid = TRUE;
// Use the "Update Favourite" timer to do it as
// soon as possible.
SetTimer(v_hwndTray, IDT_FAVOURITE, 2*1000, NULL);
}
//---------------------------------------------------------------------------
void Tray_HandleTimer(PFileCabinet pfc, WPARAM wTimerID)
{
POINT pt;
RECT rc;
switch (wTimerID) {
case IDT_PROGRAMS:
case IDT_RECENT:
if (!pfc->bMainMenuInit)
{
StartMenu_ControlBkgThread(SMCT_STOP);
KillTimer(v_hwndTray, wTimerID);
StartMenu_ControlBkgThread((wTimerID == IDT_PROGRAMS) ?
SMCT_FILLPROGRAMSONLY : SMCT_FILLRECENTONLY);
}
break;
case IDT_REBUILDMENU:
if (!pfc->bMainMenuInit)
{
DebugMsg(DM_TRACE, TEXT("c.ht: rebuilding start menu."));
StartMenu_ControlBkgThread(SMCT_STOP);
KillTimer(v_hwndTray, wTimerID);
RebuildEntireMenu(pfc);
} else {
DebugMsg(DM_TRACE, TEXT("c.ht: waiting to rebuild start menu."));
}
break;
case IDT_FAVOURITE:
if (!pfc->bMainMenuInit)
{
StartMenu_ControlBkgThread(SMCT_STOP);
RebuildEntireMenu(pfc);
}
if (!g_ts.fFavInvalid)
KillTimer(v_hwndTray, wTimerID);
break;
#ifdef DELAYWININICHANGE
case IDT_DELAYWININICHANGE:
KillTimer(pfc->hwndMain, wTimerID);
Tray_OnDelayWinIniChange(pfc);
break;
#endif
case IDT_DESKTOP:
DebugMsg(DM_TRACE, TEXT("c.t_ht: IDT_DESKTOP"));
StartMenu_ControlBkgThread(SMCT_STOP);
KillTimer(pfc->hwndMain, wTimerID);
StartMenu_ControlBkgThread(SMCT_DESKTOPHOTKEYSONLY);
break;
}
if (g_ts.uAutoHide & AH_ON) {
if (g_ts.fSysSizing)
return;
switch (wTimerID) {
case IDT_AUTOHIDE:
// handle autohide
if (!g_ts.fSysSizing && !(g_ts.uAutoHide & AH_HIDING)) {
HWND hwndA = GetActiveWindow();
GetCursorPos(&pt);
rc = g_ts.rc;
InflateRect(&rc, g_cxEdge * 4, g_cyEdge*4);
if (!PtInRect(&rc, pt) &&
hwndA != v_hwndTray &&
!SendMessage(pfc->hwndView, TM_SYSMENUCOUNT, 0, 0L) &&
(hwndA == NULL || GetWindowOwner(hwndA) != v_hwndTray)) {
KillTimer(v_hwndTray, wTimerID);
TrayHide();
}
}
break;
case IDT_AUTOUNHIDE:
// handle 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))
TrayUnhide();
}
break;
}
}
}
//---------------------------------------------------------------------------
BOOL Menu_GetItemData(HMENU hmenu, UINT iItem)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_DATA | MIIM_STATE;
mii.cch = 0; // just in case
if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
{
return mii.dwItemData;
}
return 0;
}
//---------------------------------------------------------------------------
LRESULT Tray_HandleInitMenuPopup(HMENU hmenuPopup)
{
LPITEMIDLIST pidl, pidlAlt = NULL;
UINT wID;
int iSpecialID;
UINT uMsg = 0;
ULONG *puNotify = NULL;
LPFileCabinet pfc;
// DebugMsg(DM_TRACE, "hmenupopup = %d, id = %d", hmenuPopup, GetMenuItemID(hmenuPopup, 0));
if (!hmenuPopup)
return 1;
wID = GetMenuItemID(hmenuPopup, 0);
switch (wID)
{
case IDM_RECENT:
WaitForRecent();
case IDM_PROGRAMS:
if (FileMenu_InitMenuPopup(hmenuPopup))
break;
case IDM_RECENTINIT:
case IDM_PROGRAMSINIT:
if (wID == IDM_PROGRAMSINIT)
{
iSpecialID = CSIDL_PROGRAMS;
wID = IDM_PROGRAMS;
if (!g_ts.uProgNotify) {
uMsg = WMTRAY_PROGCHANGE;
puNotify = &g_ts.uProgNotify;
}
pidlAlt = SHCloneSpecialIDList(NULL, CSIDL_COMMON_PROGRAMS, FALSE);
}
else
{
wID = IDM_RECENT;
iSpecialID = CSIDL_RECENT;
if (!g_ts.uRecentNotify) {
uMsg = WMTRAY_RECCHANGE;
puNotify = &g_ts.uRecentNotify;
}
}
pidl = SHCloneSpecialIDList(NULL, iSpecialID, FALSE);
if (!pidl)
goto ExitAndFree;
// Make sure we get notified of changes.
if (puNotify)
{
pfc = g_pfcTray;
Assert(pfc);
if (pfc)
*puNotify = RegisterNotify(pfc->hwndMain, uMsg, pidl);
}
// Remove our fake initialisation menu item.
// NB It's possible to have a filemenu here so we have to be
// careful to check before we delete the initialisation item.
// We should have a FileMenu_IsFileMenu() API but this is
// good enough for now.
if (!Menu_GetItemData(hmenuPopup, 0))
{
DeleteMenu(hmenuPopup, 0, MF_BYPOSITION);
}
// Build the proper menu.
FileMenu_DeleteAllItems(hmenuPopup);
FileMenu_AddFilesForPidl(hmenuPopup, 0, wID, pidl, FMF_NONE, g_CabState.fMenuEnumFilter, FileMenu_Callback);
if (pidlAlt) {
if (!SHRestricted(REST_NOCOMMONGROUPS)) {
FileMenu_AppendFilesForPidl(hmenuPopup, pidlAlt, TRUE);
if (wID == IDM_PROGRAMS) {
if (!g_ts.uCommonProgNotify) {
if (g_pfcTray) {
g_ts.uCommonProgNotify = RegisterNotify(g_pfcTray->hwndMain,
WMTRAY_COMMONPROGCHANGE,
pidlAlt);
}
}
}
}
}
ExitAndFree:
if (pidlAlt)
ILFree(pidlAlt);
if (pidl)
ILFree(pidl);
break;
default:
{
FileMenu_InitMenuPopup(hmenuPopup);
break;
}
}
return 1;
}
//---------------------------------------------------------------------------
LRESULT Tray_HandleMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmi)
{
return FileMenu_MeasureItem(hwnd, lpmi);
}
//---------------------------------------------------------------------------
LRESULT Tray_HandleDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdi)
{
return FileMenu_DrawItem(hwnd, lpdi);
}
#if 0
const TCHAR rszRecent[] = TEXT("IDM_RECENT");
const TCHAR rszFind[] = TEXT("IDM_FIND");
const TCHAR rszHelp[] = TEXT("IDM_HELPSEARCH");
const TCHAR rszPrograms[] = TEXT("IDM_PROGRAMS");
const TCHAR rszControls[] = TEXT("IDM_CONTROLS");
const TCHAR rszExit[] = TEXT("IDM_EXITWIN");
const TCHAR rszPrinters[] = TEXT("IDM_PRINTERS");
const TCHAR rszStart[] = TEXT("IDM_STARTMENU");
const TCHAR rszMyComp[] = TEXT("IDM_MYCOMPUTER");
const TCHAR rszProgInit[] = TEXT("IDM_PROGRAMSINIT");
const TCHAR rszRecentInit[] = TEXT("IDM_RECENTINIT");
const TCHAR rszUnknown[] = TEXT("[UNKNOWN]");
LPCTSTR MenuName( UINT id )
{
switch( id )
{
case IDM_RECENT:
return rszRecent;
case IDM_FIND:
return rszFind;
case IDM_HELPSEARCH:
return rszHelp;
case IDM_PROGRAMS:
return rszPrograms;
case IDM_CONTROLS:
return rszControls;
case IDM_EXITWIN:
return rszExit;
case IDM_PRINTERS:
return rszPrinters;
case IDM_STARTMENU:
return rszStart;
case IDM_MYCOMPUTER:
return rszMyComp;
case IDM_PROGRAMSINIT:
return rszProgInit;
case IDM_RECENTINIT:
return rszRecentInit;
}
return rszUnknown;
}
#endif
//---------------------------------------------------------------------------
// 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;
}
//---------------------------------------------------------------------------
#define CMD_ID_FIRST 1
#define CMD_ID_LAST 0x7fff
//---------------------------------------------------------------------------
BOOL _ExecItemByPidls(HWND hwnd, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
{
IShellFolder *psf;
LPCONTEXTMENU pcm;
HMENU hmenu;
UINT idCmd;
BOOL fRes = FALSE;
TCHAR szPath[MAX_PATH];
if (pidlFolder && pidlItem)
{
if (SUCCEEDED(s_pshfRoot->lpVtbl->BindToObject(s_pshfRoot,
pidlFolder, NULL, &IID_IShellFolder, &psf)))
{
if (SUCCEEDED(psf->lpVtbl->GetUIObjectOf(psf, v_hwndTray, 1, &pidlItem, &IID_IContextMenu, 0, &pcm)))
{
if (pcm)
{
hmenu = CreatePopupMenu();
if (hmenu)
{
if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST,
CMF_DEFAULTONLY))) {
if (hmenu)
{
idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
if (idCmd)
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
CMIC_MASK_ASYNCOK,
v_hwndTray,
(LPSTR)MAKEINTRESOURCE(idCmd - CMD_ID_FIRST),
NULL, NULL,
SW_NORMAL,
};
pcm->lpVtbl->InvokeCommand(pcm,
(LPCMINVOKECOMMANDINFO)&ici);
fRes = TRUE;
}
DestroyMenu(hmenu);
}
}
}
// Release our use of the context menu
pcm->lpVtbl->Release(pcm);
}
}
psf->lpVtbl->Release(psf);
} else {
SHGetPathFromIDList(pidlFolder, szPath);
ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_CANTFINDSPECIALDIR),
NULL, MB_ICONEXCLAMATION, szPath);
}
}
return fRes;
}
//---------------------------------------------------------------------------
LRESULT FileMenu_HandleCommand(HWND hwnd, WPARAM wparam)
{
LPITEMIDLIST pidlFolder = NULL;
LPITEMIDLIST pidlItem = NULL;
BOOL fRes;
HMENU hmenu;
DECLAREWAITCURSOR;
SetWaitCursor();
hmenu = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, wparam);
FileMenu_GetLastSelectedItemPidls(hmenu, &pidlFolder, &pidlItem);
fRes = _ExecItemByPidls(hwnd, pidlFolder, pidlItem);
ILFree(pidlFolder);
ILFree(pidlItem);
ResetWaitCursor();
#ifdef DEBUG
if (!fRes)
{
DebugMsg(DM_ERROR, TEXT("t.fm_hc: Can't exec command."));
}
#endif
return fRes;
}
//---------------------------------------------------------------------------
LRESULT Tray_HandleDestroy(PFileCabinet pfc)
{
HMENU hmenuSub;
MINIMIZEDMETRICS mm;
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(pfc);
// HotkeyList_Save();
if (g_ts.pPositions)
DestroySavedWindowPositions();
if (g_ts.hBIOS!=INVALID_HANDLE_VALUE)
CloseHandle(g_ts.hBIOS);
if (g_ts.hVPowerD!=INVALID_HANDLE_VALUE)
CloseHandle(g_ts.hVPowerD);
{
// Cleanup notify handlers.
UnregisterNotify(g_ts.uProgNotify);
UnregisterNotify(g_ts.uFavNotify);
UnregisterNotify(g_ts.uRecentNotify);
UnregisterNotify(g_ts.uDesktopNotify);
UnregisterNotify(g_ts.uCommonDesktopNotify);
UnregisterNotify(g_ts.uCommonProgNotify);
UnregisterNotify(g_ts.uCommonFavNotify);
// Cleanup programs
hmenuSub = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_PROGRAMS);
if (hmenuSub)
{
FileMenu_DeleteAllItems(hmenuSub);
}
else
{
DebugMsg(DM_TRACE, TEXT("c.t_hd: Can't find Programs menu."));
}
// Cleanup recent docs.
hmenuSub = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_RECENT);
if (hmenuSub)
{
FileMenu_DeleteAllItems(hmenuSub);
}
else
{
DebugMsg(DM_TRACE, TEXT("c.t_hd: Can't find RecentDocs."));
}
}
if (pfc) {
if (pfc->pcmFind)
pfc->pcmFind->lpVtbl->Release(pfc->pcmFind);
DeskTray_DestroyShellView(pfc);
}
if (g_ts.hwndTrayTips) {
DestroyWindow(g_ts.hwndTrayTips);
g_ts.hwndTrayTips = NULL;
}
// REVIEW
PostQuitMessage(0);
if (g_ts.hbmpStartBkg)
DeleteBitmap(g_ts.hbmpStartBkg);
v_hwndTray = NULL;
g_ts.hwndStart = NULL;
return 0;
}
void InvalidateRebuildMenu(WORD wID, HMENU hmenuSub)
{
if (hmenuSub)
{
PFileCabinet pfc = g_pfcTray;
if (pfc->bMainMenuInit) {
// HACK Don't destroy the menu if we're showing it - just nuke the whole thing.
SetTimer(v_hwndTray, IDT_REBUILDMENU, 1000, NULL);
} else {
StartMenu_ControlBkgThread(SMCT_STOP);
FileMenu_Invalidate(hmenuSub);
// set a timer to fill this in later...
// do it later in case of multiple changes and also
// because we want to return immediately and do the
// updates without blocking someone else (or the filesys notification)
// NB Use a lomger timeout if we're doing DDE. ExcelNT has a 5
// second timeout on DDE. If we're doing updates then sometimes we
// don't get back in time.
if (g_hwndDde)
SetTimer(v_hwndTray, wID, 10 * 1000, NULL);
else
SetTimer(v_hwndTray, wID, 3 * 1000, NULL);
}
}
}
void TrayProgramsInvalidateMenu(HMENU hmenuSub, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlPrograms)
{
LPITEMIDLIST pidlChild;
HMENU hmenu;
pidlChild = ILFindChild(pidlPrograms, pidl);
if (pidlChild) {
pidlChild = ILClone(pidlChild);
ILRemoveLastID(pidlChild);
hmenu = FileMenu_FindSubMenuByPidl(hmenuSub, pidlChild);
// doing this will catch the case of "everything else"
// and just nuke the whole menu tree if we couldn't find a menu
// for this pidl
if (!hmenu)
hmenu = hmenuSub;
InvalidateRebuildMenu(IDM_PROGRAMS, hmenu);
ILFree(pidlChild);
}
}
void TrayRevertMenuToInitial(HMENU hmenuSub, UINT idMenu, UINT idTimer)
{
ULONG *puNotify = (idMenu == IDM_PROGRAMSINIT ? &g_ts.uProgNotify : &g_ts.uRecentNotify);
if (idMenu == IDM_PROGRAMSINIT)
puNotify = &g_ts.uProgNotify;
else
puNotify = &g_ts.uRecentNotify;
UnregisterNotify(*puNotify);
*puNotify = 0;
FileMenu_DeleteAllItems(hmenuSub);
InsertMenu(hmenuSub, 0, MF_BYPOSITION, idMenu, c_szNULL);
SetTimer(v_hwndTray, idTimer, 10 * 1000, NULL);
}
void TrayUpdateProgramsMenu(LPARAM lEvent, LPCITEMIDLIST *ppidl, BOOL bCommon)
{
LPITEMIDLIST pidlPrograms;
HMENU hmenuSub;
DebugMsg(DM_IANELHK, TEXT("c.t_hfsn: Cleaning up Programs or Recent menu."));
hmenuSub = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_PROGRAMS);
if (hmenuSub) {
pidlPrograms = SHCloneSpecialIDList(NULL,
bCommon ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS,
FALSE);
if (!pidlPrograms)
return;
if (ILIsEqual(pidlPrograms, ppidl[0]) ||
((lEvent & SHCNE_RENAMEFOLDER) && ILIsEqual(pidlPrograms, ppidl[1]))) {
DebugMsg(DM_TRACE, TEXT("Deleting all programs menus"));
TrayRevertMenuToInitial(hmenuSub, IDM_PROGRAMSINIT, IDM_PROGRAMS);
} else {
TrayProgramsInvalidateMenu(hmenuSub, ppidl[0], pidlPrograms);
if (lEvent & (SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER)) {
TrayProgramsInvalidateMenu(hmenuSub, ppidl[1], pidlPrograms);
}
}
ILFree(pidlPrograms);
}
}
//---------------------------------------------------------------------------
LRESULT Tray_HandleFSNotify(UINT uMsg, PFileCabinet pfc, LPITEMIDLIST * ppidl, LPARAM lEvent)
{
HMENU hmenuSub;
WORD wID = 0;
DebugMsg(DM_TRACE, TEXT("Tray_HandleFSNotify : enter!!"));
// Roach the start menu if the icon cache is getting re-ordered.
if ((lEvent == SHCNE_UPDATEIMAGE) && ppidl && ppidl[0])
{
if (*(int UNALIGNED *)((BYTE *)ppidl[0] + 2) == -1)
{
DebugMsg(DM_TRACE, TEXT("c.t_hfsn: Update image of -1."));
g_ts.fFavInvalid = TRUE;
SetTimer(v_hwndTray, IDT_FAVOURITE, 1000, NULL);
}
return 1;
}
switch (uMsg) {
case WMTRAY_PRINTCHANGE:
PrintNotify_HandleFSNotify(pfc->hwndMain, ppidl, lEvent);
break;
case WMTRAY_FAVCHANGE:
case WMTRAY_COMMONFAVCHANGE:
{
LPITEMIDLIST pidlProgs;
pidlProgs = SHCloneSpecialIDList(NULL,
((uMsg == WMTRAY_FAVCHANGE) ? CSIDL_PROGRAMS : CSIDL_COMMON_PROGRAMS),
TRUE);
// If the Programs folder is under the StartMenu (usual case) then we'll
// get a FAVCHANGE everytime something in the Programs folder changes.
// FAVCHANGES are kinda piggy so filter out spurious ones here.
if (pidlProgs && ppidl)
{
if ((ppidl[0] && !ILIsParent(pidlProgs, ppidl[0], FALSE))
|| (ppidl[1] && !ILIsParent(pidlProgs, ppidl[1], FALSE)))
{
DebugMsg(DM_TRACE, TEXT("WMTRAY_FAVCHANGE"));
g_ts.fFavInvalid = TRUE;
// the timer is to collect up the changes...
SetTimer(v_hwndTray, IDT_FAVOURITE, 1000, NULL);
}
ILFree(pidlProgs);
}
break;
}
case WMTRAY_RECCHANGE:
hmenuSub = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, IDM_RECENT);
if (hmenuSub) {
TrayRevertMenuToInitial(hmenuSub, IDM_RECENTINIT, IDM_RECENT);
}
break;
case WMTRAY_PROGCHANGE:
TrayUpdateProgramsMenu(lEvent, ppidl, FALSE);
break;
case WMTRAY_COMMONPROGCHANGE:
TrayUpdateProgramsMenu(lEvent, ppidl, TRUE);
break;
case WMTRAY_DESKTOPCHANGE:
DebugMsg(DM_TRACE, TEXT("c.t_hfs: Desktop change."));
SetTimer(v_hwndTray, IDT_DESKTOP, 2000, NULL);
break;
default:
Assert(0);
break;
}
return 1;
}
void Tray_ActAsSwitcher(PFileCabinet pfc)
{
if (g_ts.uModalMode) {
if (g_ts.uModalMode != MM_SHUTDOWN) {
SwitchToThisWindow(GetLastActivePopup(pfc->hwndMain), TRUE);
}
MessageBeep(0);
} else {
HWND hwndForeground;
#ifdef DEBUG
static int s_iRecurse = 0;
s_iRecurse++;
Assert(s_iRecurse < 100);
DebugMsg(DM_TRACE, TEXT("s_iRecurse = %d"), s_iRecurse);
#endif
hwndForeground = GetForegroundWindow();
// only do the button once we're the foreground dude.
if ((hwndForeground == v_hwndTray) && (GetActiveWindow() == v_hwndTray)) {
// This pushes the start button and causes the start menu to popup.
SendMessage(GetDlgItem(v_hwndTray, IDC_START), BM_SETSTATE, TRUE, 0);
#ifdef DEBUG
s_iRecurse = 0;
#endif
} else {
// until then, try to come forward.
Tray_HandleFullScreenApp(FALSE, NULL);
if (hwndForeground == v_hwndDesktop) {
SetFocus(g_ts.hwndStart);
if (GetFocus() != g_ts.hwndStart)
return;
}
SwitchToThisWindow(pfc->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 RebuildMenu(WORD wID)
{
HMENU hmenuSub;
hmenuSub = Menu_FindSubMenuByFirstID(g_ts.hmenuStart, wID);
InvalidateRebuildMenu(wID, hmenuSub);
}
void Tray_SetFSMenuFilter(UINT fFilter)
{
if (fFilter != g_CabState.fMenuEnumFilter) {
g_CabState.fMenuEnumFilter = fFilter;
RebuildMenu(IDM_PROGRAMS);
}
}
//----------------------------------------------------------------------------
void Tray_OnWinIniChange(PFileCabinet pfc, HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// 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, c_szMetrics) == 0))))
{
#ifdef DEBUG
if (wParam == SPI_SETNONCLIENTMETRICS)
DebugMsg(DM_TRACE, TEXT("c.t_owic: Non-client metrics (probably) changed."));
else
DebugMsg(DM_TRACE, TEXT("c.t_owic: Window metrics changed."));
#endif
Cabinet_InitGlobalMetrics(wParam, (LPTSTR)lParam);
#ifdef DELAYWININICHANGE
SetTimer(pfc->hwndMain, IDT_DELAYWININICHANGE, 2*1000, NULL);
#else
Tray_OnDelayWinIniChange(pfc);
#endif
}
// Handle old extensions.
if (!lParam || (lParam && (lstrcmpi((LPTSTR)lParam, c_szExtensions) == 0)))
{
DebugMsg(DM_TRACE, TEXT("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);
}
}
//---------------------------------------------------------------------------
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);
}
// else
// {
// GetWindowText(hwnd, sz, ARRAYSIZE(sz));
// DebugMsg(DM_IANELHK, "c.hkl_hiu: %s (%x) has no kotkey assigned.", sz, hwnd);
// }
#endif
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
}
return NULL;
}
//---------------------------------------------------------------------------
void HotkeyList_HandleHotkey(PFileCabinet pfc, int nID)
{
PHOTKEYITEM phki;
BOOL fRes;
HWND hwnd;
DebugMsg(DM_TRACE, TEXT("c.hkl_hh: Handling hotkey (%d)."), nID);
// Find it in the list.
Assert(g_ts.hdsaHKI);
phki = DSA_GetItemPtr(g_ts.hdsaHKI, nID);
if (phki && phki->wGHotkey)
{
DebugMsg(DM_TRACE, TEXT("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)
{
DebugMsg(DM_TRACE, TEXT("c.hkl_hh: Hotkey is already in use."));
// Activate it.
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
}
else
{
DECLAREWAITCURSOR;
// Exec the item.
SetWaitCursor();
DebugMsg(DM_TRACE, TEXT("c.hkl_hh: Hotkey is not in use, execing item."));
Assert(phki->pidlFolder && phki->pidlItem);
fRes = _ExecItemByPidls(pfc->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)
{
DebugMsg(DM_TRACE, TEXT("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))
{
DebugMsg(DM_TRACE, TEXT("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;
DebugMsg(DM_TRACE, TEXT("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)
{
LPRECT lprc;
lprc = (LPRECT)SHLockShared(ptabd->hSharedRect, ptabd->dwProcId);
if (lprc)
{
*lprc = g_ts.arStuckRects[g_ts.uStuckPlace];
SHUnlockShared(lprc);
}
}
void NukeAppBar(int i)
{
Free(DPA_GetPtr(g_ts.hdpaAppBars, i));
DPA_DeletePtr(g_ts.hdpaAppBars, i);
}
void AppBarRemove(PTRAYAPPBARDATA ptabd)
{
if (g_ts.hdpaAppBars) {
int i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--) {
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
if (ptabd->abd.hWnd == pab->hwnd) {
NukeAppBar(i);
}
}
}
}
PAPPBAR FindAppBar(HWND hwnd)
{
int i;
if (g_ts.hdpaAppBars) {
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(UINT uMsg, HWND hwndExclude, LPARAM lParam)
{
if (g_ts.hdpaAppBars) {
int i = DPA_GetPtrCount(g_ts.hdpaAppBars);
while (i--) {
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
if (hwndExclude != pab->hwnd) {
if (!IsWindow(pab->hwnd))
NukeAppBar(i);
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_InsertPtr(g_ts.hdpaAppBars, 0x7fff, pab) != -1);
}
UINT AppBarGetState()
{
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) {
LPRECT lprc;
lprc = (LPRECT)SHLockShared(ptabd->hSharedRect, ptabd->dwProcId);
if (lprc)
{
*lprc = ptabd->abd.rc;
// always subtract off the tray
if (!g_ts.uAutoHide) {
APPBAR ab;
ab.uEdge = g_ts.uStuckPlace;
ab.rc = g_ts.arStuckRects[g_ts.uStuckPlace];
AppBarSubtractRect(&ab, lprc);
}
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.
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))
)
{
AppBarSubtractRect(pab, lprc);
}
}
SHUnlockShared(lprc);
}
}
}
void AppBarSetPos(PTRAYAPPBARDATA ptabd)
{
PAPPBAR pab = FindAppBar(ptabd->abd.hWnd);
if (pab) {
LPRECT lprc;
AppBarQueryPos(ptabd);
lprc = (LPRECT)SHLockShared(ptabd->hSharedRect, ptabd->dwProcId);
if (lprc)
{
if (!EqualRect(&pab->rc, lprc)) {
pab->rc = *lprc;
pab->uEdge = ptabd->abd.uEdge;
PostMessage(v_hwndTray, TM_RELAYPOSCHANGED, (WPARAM)ptabd->abd.hWnd, 0);
StuckSizeChange();
SendMessage(v_hwndDesktop, DTM_SIZEDESKTOP, 0, 0);
}
SHUnlockShared(lprc);
}
}
}
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 fSet, UINT uEdge)
{
HWND hwndAutoHide = g_hwndAutoHide[uEdge];
if (!IsWindow(hwndAutoHide)) {
g_hwndAutoHide[uEdge] = NULL;
}
// register
if (fSet) {
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, ptabd->abd.lParam, uEdge);
}
}
void IAppBarActivationChange(HWND hWnd, UINT uEdge)
{
HWND hwndAutoHide = AppBarGetAutoHideBar(uEdge);
if (hwndAutoHide &&
(hwndAutoHide != hWnd)) {
// this needs to be done on a postmessage so that USER will have a chance to
PostMessage(v_hwndTray, TM_BRINGTOTOP, (WPARAM)hwndAutoHide, uEdge);
}
}
void AppBarActivationChange(PTRAYAPPBARDATA ptabd)
{
PAPPBAR pab = FindAppBar(ptabd->abd.hWnd);
if (pab) {
IAppBarActivationChange(ptabd->abd.hWnd, pab->uEdge);
}
}
UINT TrayAppBarMessage(PFileCabinet pfc, 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);
AppBarNotifyAll(ABN_POSCHANGED, NULL, 0);
StuckSizeChange();
break;
case ABM_QUERYPOS:
AppBarQueryPos(ptabd);
break;
case ABM_SETPOS:
AppBarSetPos(ptabd);
break;
case ABM_GETSTATE:
return AppBarGetState();
case ABM_GETTASKBARPOS:
AppBarGetTaskBarPos(ptabd);
break;
case ABM_WINDOWPOSCHANGED:
case ABM_ACTIVATE:
AppBarActivationChange(ptabd);
break;
case ABM_GETAUTOHIDEBAR:
return (UINT)AppBarGetAutoHideBar(ptabd->abd.uEdge);
case ABM_SETAUTOHIDEBAR:
return AppBarSetAutoHideBar(ptabd);
default:
return FALSE;
}
return TRUE;
}
UINT TrayLoadInProc(PFileCabinet pfc, PCOPYDATASTRUCT pcds)
{
LPUNKNOWN punk;
HRESULT 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 = LOWORD(lParam);
pt.y = HIWORD(lParam);
GetWindowRect(hwnd, &r1);
return PtInRect(&r1, pt);
}
void Tray_HandleWindowPosChanging(PFileCabinet pfc, LPWINDOWPOS lpwp)
{
//DebugMsg(DM_TRACE, "Tray -- WM_WINDOWPOSCHANGING %x, %d %d %d %d",
//lpwp->flags, lpwp->x, lpwp->y, lpwp->cx, lpwp->cy);
//the user could have hit esc, in which case, what we had
// in our g_ts.uMoveStuckPlace is bogus
Tray_WhichStuckRect(lpwp->x, lpwp->y, lpwp->x + lpwp->cx, lpwp->y + lpwp->cy);
if (g_ts.uMoveStuckPlace != (UINT)-1) {
if (pfc->hwndView)
Tray_HandleSizing(pfc, 0, &g_ts.arStuckRects[g_ts.uMoveStuckPlace], g_ts.uMoveStuckPlace);
Tray_DoneMoving(lpwp);
g_ts.uMoveStuckPlace = (UINT)-1;
} else if (!g_ts.fSysSizing && !g_ts.fSelfSizing) {
// if someone's trying to force us to move
lpwp->flags |= (SWP_NOMOVE | SWP_NOSIZE);
}
}
//---------------------------------------------------------------------------
LRESULT CALLBACK TrayWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT r1;
POINT pt;
DWORD dw;
PFileCabinet pfc = g_pfcTray;
switch (uMsg) {
case WMTRAY_PROGCHANGE:
case WMTRAY_RECCHANGE:
case WMTRAY_FAVCHANGE:
case WMTRAY_PRINTCHANGE:
case WMTRAY_DESKTOPCHANGE:
case WMTRAY_COMMONPROGCHANGE:
case WMTRAY_COMMONFAVCHANGE:
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
LRESULT lResult = 0;
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
lResult = Tray_HandleFSNotify(uMsg, pfc, ppidl, lEvent);
SHChangeNotification_Unlock(pshcnl);
}
return lResult;
}
case WMTRAY_PRINTICONNOTIFY:
PrintNotify_IconNotify(hWnd, (UINT)lParam);
break;
case WMTRAY_REGISTERHOTKEY:
return Tray_RegisterHotkey(hWnd, wParam);
case WMTRAY_UNREGISTERHOTKEY:
return Tray_UnregisterHotkey(hWnd, 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 WM_MENUCHAR:
return FileMenu_HandleMenuChar((HMENU)lParam, (TCHAR)LOWORD(wParam));
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:
dw = TrayNotify(g_ts.hwndNotify, (HWND)wParam, (PCOPYDATASTRUCT)lParam);
Tray_SizeWindows();
return(dw);
case TCDM_APPBAR:
return TrayAppBarMessage(pfc, (PCOPYDATASTRUCT)lParam);
case TCDM_LOADINPROC:
return TrayLoadInProc(pfc, (PCOPYDATASTRUCT)lParam);
}
}
return FALSE;
case WM_NCLBUTTONDBLCLK:
if (IsPosInHwnd(lParam, g_ts.hwndNotify)) {
TrayCommand(pfc, IDM_SETTIME);
}
break;
case WM_NCMOUSEMOVE:
if (IsPosInHwnd(lParam, g_ts.hwndNotify)) {
MSG msg;
msg.lParam = lParam;
msg.wParam = wParam;
msg.message = WM_NCMOUSEMOVE;
msg.hwnd = hWnd;
SendMessage(g_ts.hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msg);
}
goto DoDefault;
case WM_CREATE:
Tray_OnCreate(hWnd, (LPCREATESTRUCT)lParam);
break;
case WM_DESTROY:
return Tray_HandleDestroy(pfc);
case WM_ENDSESSION:
// save our settings if someone else is shutting things down
if (wParam) {
DebugMsg(DM_TRACE, TEXT("Tray WM_ENDSESSION"));
_SaveTrayAndDesktop();
ShowWindow(v_hwndTray, SW_HIDE);
ShowWindow(v_hwndDesktop, SW_HIDE);
RegisterShellHook(pfc->hwndView, FALSE);
}
break;
//
// always repaint the screen when the machine wakes up from
// a suspend. NOTE we dont need this for a standby suspend.
//
// a critical resume does not generate a WM_POWERBROADCAST
// to windows for some reason, but it does generate a old
// WM_POWER message.
//
case WM_POWER:
if (wParam != PWR_CRITICALRESUME)
goto DoDefault;
#ifndef WINNT // BUGBUG - Fix this when NT gets APM
wParam = PBT_APMRESUMECRITICAL;
#endif
// fall through to WM_POWERBROADCAST
case WM_POWERBROADCAST:
#ifndef WINNT // BUGBUG - Fix this when NT gets APM
switch (wParam)
{
case PBT_APMRESUMECRITICAL:
case PBT_APMRESUMESUSPEND:
ChangeDisplaySettings(NULL, CDS_RESET);
break;
}
#endif
goto DoDefault;
case WM_DEVICECHANGE:
if (wParam == DBT_CONFIGCHANGED)
{
UpdateDockingFlags();
g_ts.DockedState = GetDockedState();
if (IsEjectAllowed())
{
HandleDisplayChange(0,0,TRUE);
// Rebuild the entire start menu next chance we get.
g_ts.fFavInvalid = TRUE;
SetTimer(v_hwndTray, IDT_FAVOURITE, 2*1000, NULL);
}
}
else if (wParam == DBT_QUERYCHANGECONFIG)
{
if (IsEjectAllowed() && !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:
switch (((NMHDR *)lParam)->code) {
case SEN_DDEEXECUTE:
return (DDEHandleViewFolderNotify(pfc, (LPNMVIEWFOLDER)lParam));
case NM_STARTWAIT:
case NM_ENDWAIT:
Cabinet_OnWaitCursorNotify(pfc, (NMHDR *)lParam);
SendMessage(v_hwndDesktop, uMsg, wParam, lParam); // forward it along
break;
#define lpttt ((LPTOOLTIPTEXT)lParam)
case TTN_NEEDTEXT:
GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL, lpttt->szText, ARRAYSIZE(lpttt->szText));
return TRUE;
case TTN_SHOW:
SetWindowPos(g_ts.hwndTrayTips,
HWND_TOP,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
break;
}
break;
case WM_CLOSE:
DoExitWindows(hWnd);
return 0;
case WM_NCHITTEST:
GetClientRect(hWnd, &r1);
MapWindowPoints(hWnd, NULL, (LPPOINT)&r1, 2);
pt.x = LOWORD(lParam);
pt.y = HIWORD(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(pfc, (LPWINDOWPOS)lParam);
break;
// BUGBUG this needs to be changed with WM_MOVING
// trap the start and end of a sizing/moving operation and
// make sure that we don't actually do anything until done.
// this avoids some crazy resizing with full window dragging.
case WM_ENTERSIZEMOVE:
//DebugMsg(DM_TRACE, "Tray -- WM_ENTERSIZEMOVE");
g_ts.fSysSizing = TRUE;
break;
case WM_EXITSIZEMOVE:
//DebugMsg(DM_TRACE, "Tray -- WM_EXITSIZEMOVE");
g_ts.fSysSizing = FALSE;
PostMessage(hWnd, WM_SIZE, 0, 0L);
break;
case WM_MOVING:
//DebugMsg(DM_TRACE, "Tray -- WM_MOVING");
Tray_HandleMoving((LPRECT)lParam);
Tray_HandleSizing(pfc, wParam, (LPRECT)lParam, g_ts.uMoveStuckPlace);
break;
case WM_ENTERMENULOOP:
// DebugMsg(DM_TRACE, "c.twp: Enter menu loop.");
Tray_HandleEnterMenuLoop();
Cabinet_ForwardViewMsg(pfc, uMsg, wParam, lParam);
break;
case WM_EXITMENULOOP:
// DebugMsg(DM_TRACE, "c.twp: Exit menu loop.");
Tray_HandleExitMenuLoop();
Cabinet_ForwardViewMsg(pfc, uMsg, wParam, lParam);
break;
case WM_TIMER:
Tray_HandleTimer(pfc, wParam);
break;
case WM_SIZING:
//DebugMsg(DM_TRACE, "Tray -- WM_SIZING");
Tray_HandleSizing(pfc, wParam, (LPRECT)lParam, g_ts.uStuckPlace);
break;
case WM_SIZE:
if (pfc && ((GetWindowLong(hWnd, GWL_STYLE)) & WS_MINIMIZE)) {
ShowWindow(hWnd, SW_RESTORE);
}
if (!g_ts.fSysSizing || g_fDragFullWindows)
{
Tray_SizeWindows();
}
if (!g_ts.fSysSizing && !g_ts.fSelfSizing) {
// we get a couple size messages while being created.
// our window rect means nothing then, so punt out
if (IsWindowVisible(hWnd)) {
StuckSizeChange();
AppBarNotifyAll(ABN_POSCHANGED, NULL, 0);
}
}
break;
case WM_DISPLAYCHANGE:
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:
Cabinet_PropagateMessage(hWnd, uMsg, wParam, lParam);
Tray_OnWinIniChange(pfc, hWnd, wParam, lParam);
break;
case WM_TIMECHANGE:
Cabinet_PropagateMessage(hWnd, uMsg, wParam, lParam);
break;
case WM_SYSCOLORCHANGE:
StartButton_ResetAndSize(pfc);
Cabinet_PropagateMessage(hWnd, uMsg, wParam, lParam);
break;
case WM_MEASUREITEM:
return Tray_HandleMeasureItem(hWnd, (LPMEASUREITEMSTRUCT)lParam);
case WM_DRAWITEM:
return Tray_HandleDrawItem(hWnd, (LPDRAWITEMSTRUCT)lParam);
case WM_INITMENUPOPUP:
// DebugMsg(DM_TRACE, "c.twp: Init menu popup.");
// bugbug, does Tray_HandleInitMenuPopup return anything other than 1?
if (!Tray_HandleInitMenuPopup((HMENU)wParam))
Cabinet_ForwardViewMsg(pfc, uMsg, wParam, lParam);
break;
case WM_SETCURSOR:
if (pfc->iWaitCount) {
//DebugMsg(DM_TRACE,"########### SET WAIT CURSOR WM_SETCURSOR %d", pfc->iWaitCount);
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
return TRUE;
} else
goto DoDefault;
case WM_SETFOCUS:
if (pfc && pfc->hwndView)
SetFocus(pfc->hwndView);
break;
// this keeps our window from comming to the front on button down
// instead, we activate the window on the up click
case WM_MOUSEACTIVATE:
if (LOWORD(lParam) == HTCLIENT)
return MA_NOACTIVATE;
else
goto DoDefault;
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(pfc, hmenu, FALSE);
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(hWnd);
break;
default:
goto DoDefault;
}
break;
case TM_ACTASTASKSW:
Tray_ActAsSwitcher(pfc);
break;
case TM_RELAYPOSCHANGED:
AppBarNotifyAll(ABN_POSCHANGED, (HWND)wParam, lParam);
break;
case TM_BRINGTOTOP:
SetWindowPos((HWND)wParam, HWND_TOP,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
break;
case TM_INVALIDATEREBUILDMENU:
InvalidateRebuildMenu((WORD)wParam, (HMENU)lParam);
break;
case WM_NCRBUTTONUP:
wParam = (WPARAM)pfc->hwndView;
case WM_CONTEXTMENU:
DebugMsg(DM_TRACE, TEXT("TRAY GOT CONTEXT MENU!"));
if (SHRestricted(REST_NOTRAYCONTEXTMENU)) {
break;
}
if (((HWND)wParam) == g_ts.hwndStart)
{
#if 0
/* The following test is bogus, as it stops the start context menu being displayed if
/ the find option is disabled, therefore we no longer perform it, we just display the menu
/ which filters out the options anyway. daviddv 27mar95 */
// put up the context menu for the start menu folder.
if (!SHRestricted(REST_NOFIND) && !SHRestricted(REST_NOSETTASKBAR))
#endif
StartMenuFolder_ContextMenu(pfc, lParam);
} else {
BOOL fIncludeTime;
// if it was done by keyboard or if click was inthe clock, include
// the time
fIncludeTime = (lParam == (DWORD)-1) || IsPosInHwnd(lParam, g_ts.hwndNotify);
Tray_ContextMenu(pfc, lParam, fIncludeTime);
}
break;
case WM_HOTKEY:
HotkeyList_HandleHotkey(pfc, (WORD)wParam);
break;
case WM_COMMAND:
TrayCommand(pfc, GET_WM_COMMAND_ID(wParam, lParam));
break;
case WM_WINDOWPOSCHANGED:
IAppBarActivationChange(hWnd, g_ts.uStuckPlace);
goto DoDefault;
case WM_ACTIVATE:
IAppBarActivationChange(hWnd, g_ts.uStuckPlace);
if (wParam != WA_INACTIVE)
TrayUnhide();
// Fall through
default:
DoDefault:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//----------------------------------------------------------------------------
// 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)
{
if (_Restricted(hwnd, REST_NOCLOSE))
return;
SetFocus(hwnd);
_SaveTrayAndDesktop();
g_ts.uModalMode = MM_SHUTDOWN;
ExitWindowsDialog(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);
DebugMsg(DM_TRACE, TEXT("c.dew: Posting quit message for %#08x."), GetCurrentThreadId());
PostMessage(v_hwndDesktop, WM_QUIT, 0, 0);
}
}
#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, PFileCabinet pfc)
{
HPROPSHEETPAGE ahpage[MAX_FILE_PROP_PAGES];
TCHAR szPath[MAX_PATH];
PROPSHEETHEADER psh;
LoadString(hinstCabinet, IDS_TASKBAR, 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 = 0;
psh.phpage = ahpage;
CShellTray_AddViewPropertySheetPages(&pfc->sb, 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(LPVOID lpData)
{
HWND hwnd;
RECT rc;
GetWindowRect(g_ts.hwndStart, &rc);
g_ts.hwndProp = hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, c_szStatic, 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, (PFileCabinet)lpData);
g_ts.hwndProp = NULL;
DestroyWindow(hwnd);
}
return TRUE;
}
void DoTrayProperties(PFileCabinet pfc)
{
HANDLE ht;
DWORD id;
if (!_Restricted(pfc->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;
ht = CreateThread(NULL, 0, TrayPropertiesThread, (LPVOID)pfc, 0, &id);
if (ht) {
CloseHandle(ht);
} else {
g_ts.hwndProp = 0;
}
}
}
}
void ShowFolder(HWND hwnd, WPARAM wparam, UINT uFlags)
{
LPITEMIDLIST pidl = NULL;
if (_Restricted(hwnd, REST_NOSETFOLDERS))
return;
switch(wparam)
{
case IDM_CONTROLS:
pidl = SHCloneSpecialIDList(NULL, CSIDL_CONTROLS, FALSE);
break;
case IDM_PRINTERS:
pidl = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE);
break;
case IDM_MYCOMPUTER:
pidl = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
break;
}
if (pidl)
{
NEWFOLDERINFO fi;
fi.hwndCaller = NULL;
fi.pidl = pidl;
fi.uFlags = uFlags;
fi.nShow = SW_SHOWNORMAL;
fi.dwHotKey = 0;
Cabinet_OpenFolder(&fi);
ILFree(pidl);
}
}
void DoEjectPC()
{
#ifndef WINNT // BUGBUG - Fix this when NT gets docking capability
BIOSPARAMS bp;
DWORD cbOut;
DECLAREWAITCURSOR;
SetWaitCursor();
if (g_ts.hBIOS!=INVALID_HANDLE_VALUE)
DeviceIoControl(
g_ts.hBIOS,
PNPBIOS_SERVICE_SOFTUNDOCK,
&bp,
SIZEOF(bp),
&bp,
SIZEOF(bp),
&cbOut,
NULL);
ResetWaitCursor();
return;
#endif
}
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) && (hwnd != v_hwndTray) && hwnd != v_hwndDesktop) {
*pf = TRUE;
}
return !*pf;
}
BOOL CanTileAnyWindows()
{
BOOL f = FALSE;
EnumWindows(CanTileWindow, (LPARAM)&f);
return f;
}
void Tray_ContextMenu(PFileCabinet pfc, DWORD dwPos, BOOL fIncludeTime)
{
HMENU hmenu;
HMENU hmContext;
TCHAR szMenu[64];
TCHAR szTemplate[30];
TCHAR szCommand[30];
int idCmd;
if (dwPos == (DWORD)-1) {
POINT pt = {0,0};
ClientToScreen(pfc->hwndView, &pt);
dwPos = MAKELONG(pt.x, pt.y);
}
SwitchToThisWindow(pfc->hwndMain, TRUE);
SetForegroundWindow(pfc->hwndMain);
hmenu = LoadMenu(hinstCabinet, MAKEINTRESOURCE(MENU_TRAYCONTEXT));
if (hmenu) {
hmContext = GetSubMenu(hmenu, 0);
if (fIncludeTime) {
SetMenuDefaultItem(hmContext, IDM_SETTIME, MF_BYCOMMAND);
} else {
DeleteMenu(hmContext, IDM_SETTIME, MF_BYCOMMAND);
}
if (!g_ts.pPositions) {
DeleteMenu(hmContext, IDM_UNDO, MF_BYCOMMAND);
} else {
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(pfc->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);
}
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
idCmd = TrackPopupMenu(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
LOWORD(dwPos), HIWORD(dwPos), 0, v_hwndTray, NULL);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
DestroyMenu(hmenu);
if (idCmd)
TrayCommand(pfc, idCmd);
}
}
//----------------------------------------------------------------------------
void _RunFileDlg(HWND hwnd, UINT idIcon, LPCITEMIDLIST pidlWorkingDir,
UINT idTitle, UINT idPrompt, DWORD dwFlags)
{
HICON hIcon = NULL;
LPCTSTR lpszTitle = NULL;
LPCTSTR lpszPrompt = NULL;
TCHAR szTitle[256];
TCHAR szPrompt[256];
TCHAR szWorkingDir[MAX_PATH];
int idDrive;
dwFlags |= RFD_USEFULLPATHDIR;
szWorkingDir[0] = 0;
if (idIcon)
{
hIcon = LoadIcon(hinstCabinet, MAKEINTRESOURCE(idIcon));
}
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).
SHGetSpecialFolderPath(hwnd, szWorkingDir, CSIDL_DESKTOPDIRECTORY,
FALSE);
}
// if it's a removable dir, make sure it's still there
if (szWorkingDir[0]) {
idDrive = PathGetDriveNumber(szWorkingDir);
if ((idDrive != -1))
{
UINT dtype = DriveType(idDrive);
if ( ( (dtype == DRIVE_REMOVABLE) || (dtype == DRIVE_CDROM) )
&& !PathFileExists(szWorkingDir) )
{
SHGetSpecialFolderPath(hwnd, szWorkingDir, CSIDL_DESKTOPDIRECTORY,
FALSE);
}
}
}
if (idTitle)
{
LoadString(hinstCabinet, idTitle, szTitle, ARRAYSIZE(szTitle));
lpszTitle = szTitle;
}
if (idPrompt)
{
LoadString(hinstCabinet, idPrompt, szPrompt, ARRAYSIZE(szPrompt));
lpszPrompt = szPrompt;
}
RunFileDlg(hwnd, hIcon, szWorkingDir, lpszTitle, lpszPrompt, dwFlags);
}
BOOL SavePosEnumProc(HWND hwnd, LPARAM lParam)
{
Assert(g_ts.pPositions);
if (IsWindowVisible(hwnd) &&
(hwnd != v_hwndTray) &&
(hwnd != v_hwndDesktop))
{
HWNDANDPLACEMENT hap;
hap.wp.length = SIZEOF(WINDOWPLACEMENT);
GetWindowPlacement(hwnd, &hap.wp);
DebugMsg(DM_TRACE, TEXT("SaveWindowPostitionsCallback %d %d"), hwnd, hap.wp.showCmd);
if (hap.wp.showCmd != SW_SHOWMINIMIZED) {
hap.hwnd = hwnd;
DSA_InsertItem(g_ts.pPositions->hdsaWP, 0x7FFF, &hap);
}
}
return TRUE;
}
void SaveWindowPositions(UINT idRes)
{
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)
return;
g_ts.pPositions->hdsaWP = DSA_Create(SIZEOF(HWNDANDPLACEMENT), 4);
}
g_ts.pPositions->idRes = idRes;
EnumWindows(SavePosEnumProc, 0);
}
void RestoreWindowPositions()
{
int i;
LPHWNDANDPLACEMENT phap;
LONG iAnimate;
ANIMATIONINFO ami;
if (!g_ts.pPositions)
return;
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(g_ts.pPositions->hdsaWP) - 1;
for ( ; i >= 0; i--) {
phap = DSA_GetItemPtr(g_ts.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
phap->wp.length = SIZEOF(WINDOWPLACEMENT);
if ((phap->wp.showCmd) == SW_SHOWMAXIMIZED)
{
ShowWindowAsync(phap->hwnd, SW_MAXIMIZE);
}
else
{
// If the window to be undone is now iconic, we just need
// to restore it off the tray, otherwise (as in undoing from
// a tile) we need to reset its position
if (IsIconic(phap->hwnd))
{
ShowWindowAsync(phap->hwnd, SW_RESTORE);
}
else
{
SetWindowPos(phap->hwnd,
NULL,
phap->wp.rcNormalPosition.left,
phap->wp.rcNormalPosition.top,
phap->wp.rcNormalPosition.right - phap->wp.rcNormalPosition.left,
phap->wp.rcNormalPosition.bottom - phap->wp.rcNormalPosition.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
}
}
}
}
ami.iMinAnimate = iAnimate;
SystemParametersInfo(SPI_SETANIMATION, SIZEOF(ami), &ami, FALSE);
DestroySavedWindowPositions();
}
void DestroySavedWindowPositions()
{
// free the global struct
DSA_Destroy(g_ts.pPositions->hdsaWP);
LocalFree(g_ts.pPositions);
g_ts.pPositions = 0;
}
void TrayHandleWindowDestroyed(HWND hwnd)
{
int i;
LPHWNDANDPLACEMENT phap;
Assert(g_ts.pPositions);
i = DSA_GetItemCount(g_ts.pPositions->hdsaWP) - 1;
for ( ; i >= 0; i--) {
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();
}
//----------------------------------------------------------------------------
// 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) {
// relay it to the tray
return SendMessage(v_hwndTray, uMsg, wParam, lParam);
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
const TCHAR c_szRunDlgReady[] = TEXT("MSShellRunDlgReady");
const TCHAR c_szWaitingThreadID[] = TEXT("WaitingThreadID");
DWORD WINAPI RunDlgThread(LPVOID data)
{
HWND hwnd;
RECT rc;
GetWindowRect(g_ts.hwndStart, &rc);
hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, c_szStatic, NULL, 0 ,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
// Subclass it.
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)(FARPROC)RunDlgStaticSubclassWndProc);
if (hwnd) {
// SwitchToThisWindow(hwnd, TRUE);
// SetForegroundWindow(hwnd);
HANDLE hMemWorkDir = NULL;
LPITEMIDLIST pidlWorkingDir = NULL;
#ifdef WINNT
TCHAR szDir[MAX_PATH];
TCHAR szPath[MAX_PATH];
BOOL fSimple;
#endif
if (data)
SetProp(hwnd, c_szWaitingThreadID, (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 && !IsMinimized(g_ts.hwndLastActive) &&
(Cabinet_IsExplorerWindow(g_ts.hwndLastActive) || Cabinet_IsFolderWindow(g_ts.hwndLastActive))
&& Desktop_IsSameRoot(g_ts.hwndLastActive,NULL))
{
// 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());
}
}
_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, c_szWaitingThreadID);
DestroyWindow(hwnd);
}
return TRUE;
}
void Tray_RunDlg()
{
HANDLE ht;
DWORD id;
HANDLE hEvent;
LPVOID 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))
{
SetWindowPos(hwndOldRun, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
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, c_szRunDlgReady);
if (hEvent)
pvThreadParam = (LPVOID)GetCurrentThreadId();
else
pvThreadParam = NULL;
ht = CreateThread(NULL, 0, RunDlgThread, pvThreadParam, 0, &id);
if (ht) {
if (hEvent)
{
MsgWaitForMultipleObjectsLoop(hEvent, 10*1000);
DebugMsg(DM_TRACE, TEXT("c.t_rd: Done waiting."));
CloseHandle(hEvent);
}
CloseHandle(ht);
}
}
}
DWORD MsgWaitForMultipleObjectsLoop(HANDLE hEvent, DWORD dwTimeout)
{
MSG msg;
DWORD dwObject;
// DebugMsg(DM_TRACE, "c.t_rd: Waiting for run dlg...");
while (1)
{
// NB We need to let the run dialog become active so we have to half handle sent
// messages but we don't want to handle any input events or we'll swallow the
// type-ahead.
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_OBJECT_0 + 1:
// Almost.
// DebugMsg(DM_TRACE, "c.t_rd: Almost done waiting.");
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.
//
if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_STARTMENU, TRUE)) {
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);
}
}
void StartMenuFolder_ContextMenu(PFileCabinet pfc, DWORD dwPos)
{
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(v_hwndTray, CSIDL_STARTMENU, TRUE);
Tray_HandleFullScreenApp(FALSE, NULL);
SetForegroundWindow(pfc->hwndMain);
if (pidlStart) {
LPITEMIDLIST pidlLast = ILClone(ILFindLastID(pidlStart));
ILRemoveLastID(pidlStart);
if (pidlLast)
{
LPSHELLFOLDER psf;
if (SUCCEEDED(s_pshfRoot->lpVtbl->BindToObject(s_pshfRoot,
pidlStart, NULL, &IID_IShellFolder, &psf)))
{
HMENU hmenu = CreatePopupMenu();
if (hmenu)
{
LPCONTEXTMENU 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)) {
TCHAR szCommon[50];
if (g_bIsUserAnAdmin) {
AppendMenu (hmenu, MF_SEPARATOR, 0, NULL);
LoadString (hinstCabinet, IDS_OPENCOMMON,
szCommon, 50);
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_LAST - 1,
szCommon);
LoadString (hinstCabinet, IDS_EXPLORECOMMON,
szCommon, 50);
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_LAST,
szCommon);
}
}
if (dwPos == (DWORD)-1)
{
idCmd = Tray_TrackMenu(pfc, hmenu, FALSE);
}
else
{
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
idCmd = TrackPopupMenu(hmenu,
TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
LOWORD(dwPos), HIWORD(dwPos), 0, v_hwndTray, NULL);
SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
}
if (idCmd)
{
if (idCmd == (IDSYSPOPUP_LAST - 1)) {
ExploreCommonStartMenu(FALSE);
} else if (idCmd == IDSYSPOPUP_LAST) {
ExploreCommonStartMenu(TRUE);
} else {
TCHAR szPath[MAX_PATH];
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
v_hwndTray,
(LPSTR)MAKEINTRESOURCE(idCmd - IDSYSPOPUP_FIRST),
NULL, NULL,
SW_NORMAL,
};
#ifdef UNICODE
CHAR szPathAnsi[MAX_PATH];
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);
}
}
//----------------------------------------------------------------------------
void Tray_Suspend(void)
{
DECLAREWAITCURSOR;
SetWaitCursor();
#if 1
SetSystemPowerState(FALSE, FALSE);
#else
// See RemoveSuspendMenu comment as to why this is ifdef'd out but not
// (yet) deleted. 13-Oct-94
if (!SetSystemPowerState(FALSE, FALSE) &&
GetLastError() == ERROR_SET_POWER_STATE_FAILED)
RemoveSuspendMenu(pfc->hwndMain);
#endif
ResetWaitCursor();
}
#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;
if (CreateProcess(NULL, szName, NULL, NULL, FALSE, 0,
NULL, NULL, &startup, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
#endif
void HandleGlobalHotkey(WPARAM wParam);
void TrayCommand(PFileCabinet pfc, UINT idCmd)
{
switch (idCmd) {
// NB Everything off the programs menu, cascades and all comes in as an IDM_PROGRAMS.
// Sim for recent docs.
case IDM_STARTMENU:
case IDM_RECENT:
case IDM_PROGRAMS:
FileMenu_HandleCommand(pfc->hwndMain, idCmd);
break;
case IDM_CONTROLS:
case IDM_PRINTERS:
// case IDM_PROGRAMSFOLDER:
// case IDM_FONTS:
ShowFolder(pfc->hwndMain, idCmd, COF_USEOPENSETTINGS);
break;
#ifndef WINNT // BUGBUG - Fix this when NT supports APM & Docking
case IDM_SUSPEND:
Tray_Suspend();
break;
case IDM_EJECTPC:
DoEjectPC();
break;
#endif
case IDM_EXITWIN:
DoExitWindows(pfc->hwndMain);
break;
case IDM_FILERUN:
Tray_RunDlg();
break;
case IDM_MINIMIZEALLHOTKEY:
HandleGlobalHotkey(GHID_MINIMIZEALL);
break;
case IDM_MINIMIZEALL:
// minimize all window
MinimizeAll(pfc->hwndView);
break;
case IDM_UNDO:
RestoreWindowPositions();
break;
case IDM_SETTIME:
// run the default applet in timedate.cpl
SHRunControlPanel( TEXT("timedate.cpl"), pfc->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(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);
AppBarNotifyAll(ABN_WINDOWARRANGE, NULL, FALSE);
}
break;
case IDM_TRAYPROPERTIES:
DoTrayProperties(pfc);
break;
// REVIEW: windows.hlp should be a string table resource
case IDM_HELPSEARCH:
WinHelp(pfc->hwndMain, c_szWindowsHlp, HELP_FINDER, 0);
break;
// NB The Alt-s comes in here.
case IDC_KBSTART:
// This pushes the start button and causes the start menu to popup.
SendMessage(GetDlgItem(pfc->hwndMain, IDC_START), BM_SETSTATE, TRUE, 0);
// This forces the button back up.
SendMessage(GetDlgItem(pfc->hwndMain, IDC_START), BM_SETSTATE, FALSE, 0);
break;
case IDC_ASYNCSTART:
// Make sure the button is down.
// DebugMsg(DM_TRACE, "c.twp: IDC_START.");
// Make sure the Start button is down.
if (SendMessage(g_ts.hwndStart, BM_GETSTATE, 0, 0) & 0x0004)
{
// DebugMsg(DM_TRACE, "c.twp: Start button down.");
// Set the focus.
SetFocus(g_ts.hwndStart);
ToolbarMenu(pfc);
}
else
{
// DebugMsg(DM_TRACE, "c.twp: Start button up.");
}
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(pfc->hwndMain, WM_COMMAND, IDC_ASYNCSTART, 0);
break;
case FCIDM_FINDFILES:
SHFindFiles(NULL, NULL);
break;
case FCIDM_FINDCOMPUTER:
SHFindComputer(NULL, NULL);
break;
case FCIDM_REFRESH:
RebuildEntireMenu(pfc);
break;
case FCIDM_NEXTCTL:
{
HWND hwndFocus = GetFocus();
BOOL fShift = (GetAsyncKeyState(VK_SHIFT) < 0);
if (hwndFocus == pfc->hwndToolbar) {
SetFocus(fShift ? v_hwndDesktop : pfc->hwndView);
} else if (hwndFocus == pfc->hwndView || IsChild(pfc->hwndView, hwndFocus)) {
SetFocus(fShift ? pfc->hwndToolbar : v_hwndDesktop);
} else {
SetFocus(fShift ? pfc->hwndView : pfc->hwndToolbar);
}
break;
}
case IDM_RETURN:
DebugMsg(DM_TRACE, TEXT("c.tc: Return key hit."));
if (GetFocus() == g_ts.hwndStart)
{
PostMessage(pfc->hwndMain, WM_COMMAND, IDC_KBSTART, 0);
}
else
{
SendMessage(GetDlgItem(pfc->hwndView, IDC_FOLDERTABS), WM_KEYDOWN, VK_RETURN, 0);
}
break;
default:
if (IsInRange(idCmd, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST) && !_Restricted(v_hwndTray, REST_NOFIND)) {
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
pfc->hwndMain,
(LPSTR)MAKEINTRESOURCE(idCmd - TRAY_IDM_FINDFIRST),
NULL, NULL,
SW_NORMAL,
};
pfc->pcmFind->lpVtbl->InvokeCommand(pfc->pcmFind,
(LPCMINVOKECOMMANDINFO)&ici);
} else {
DebugMsg(DM_ERROR, TEXT("tray Unknown command (%x)"), idCmd);
}
break;
}
}
//// Start menu/Tray tab as a drop target
extern IDropTarget c_dtgtStart;
extern IDropTarget c_dtgtTab;
extern IDropTarget c_dtgtTray;
//=============================================================================
// CDVDropTarget : member
//=============================================================================
STDMETHODIMP CStartDropTarget_QueryInterface(LPDROPTARGET pdtgt, REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, &IID_IDropTarget) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = pdtgt;
return S_OK;
}
*ppvObj = NULL;
return (E_NOINTERFACE);
}
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;
RECT rcLockWindow;
POINT pt;
DebugMsg(DM_TRACE, TEXT("sh - TR CStartDropTarget::DragEnter called"));
TraySetUnhideTimer(ptl.x, ptl.y);
GetWindowRect(hwndLock, &rcLockWindow);
pt.x = ptl.x-rcLockWindow.left;
pt.y = ptl.y-rcLockWindow.top;
DAD_DragEnterEx(hwndLock, pt);
if (pdtgt==&c_dtgtStart) {
*pdwEffect = DROPEFFECT_LINK;
} else if (pdtgt==&c_dtgtTab) {
*pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_SCROLL;
} else {
*pdwEffect = DROPEFFECT_NONE;
}
return S_OK;
}
STDMETHODIMP CStartDropTarget_DragOver(LPDROPTARGET pdtgt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
POINT pt = { ptl.x, ptl.y };
TraySetUnhideTimer(ptl.x, ptl.y);
ScreenToClient(v_hwndTray, &pt);
DAD_DragMove(pt);
*pdwEffect = DROPEFFECT_LINK;
return S_OK;
}
#define MSEC_ACTIVATE (1*1000)
void CTabDropTarget_HitTest(BOOL fEnter, POINTL ptl)
{
extern int Task_HitTest(HWND hwndTask, POINTL ptl);
extern void Task_SetCurSel(HWND hwndTask, int i);
static int s_iHit = -1;
static DWORD s_dwBegin = 0;
static BOOL s_fWaiting = FALSE;
LPFileCabinet pfc = g_pfcTray;
if (!pfc) {
return;
}
if (fEnter) {
s_iHit = -1;
s_fWaiting = FALSE;
} else {
int iHitNew = Task_HitTest(pfc->hwndView, ptl);
if (s_iHit != iHitNew)
{
DebugMsg(DM_TRACE, TEXT("ca TR - CTDT::HitTest new target (%d->%d)"), s_iHit, iHitNew);
s_iHit = iHitNew;
if (iHitNew != -1)
{
s_dwBegin = GetCurrentTime();
s_fWaiting = TRUE;
}
else
{
s_fWaiting = FALSE;
}
}
else if (s_fWaiting)
{
DWORD dwWaited = GetCurrentTime()-s_dwBegin;
DebugMsg(DM_TRACE, TEXT("ca TR - CTDT::HitTest waiting... (%d on %x)"), dwWaited, s_iHit);
if (dwWaited > MSEC_ACTIVATE)
{
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
Task_SetCurSel(pfc->hwndView, s_iHit);
UpdateWindow(v_hwndTray);
DAD_ShowDragImage(TRUE); // restore the lock state.
s_fWaiting = FALSE;
}
}
}
}
STDMETHODIMP CTabDropTarget_DragEnter(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
CTabDropTarget_HitTest(TRUE, ptl);
return CStartDropTarget_DragEnter(pdtgt, pdtobj, grfKeyState, ptl, pdwEffect);
}
STDMETHODIMP CTabDropTarget_DragOver(LPDROPTARGET pdtgt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
CTabDropTarget_HitTest(FALSE, ptl);
CStartDropTarget_DragOver(pdtgt, grfKeyState, ptl, pdwEffect);
*pdwEffect = (pdtgt==&c_dtgtTab) ? (DROPEFFECT_MOVE|DROPEFFECT_SCROLL) : DROPEFFECT_NONE;
return S_OK;
}
STDMETHODIMP CStartDropTarget_DragLeave(LPDROPTARGET pdtgt)
{
DebugMsg(DM_TRACE, TEXT("sh - TR CStartDropTarget::DragLeave called"));
DAD_DragLeave();
return S_OK;
}
STDMETHODIMP CStartDropTarget_Drop(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj,
DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(v_hwndTray, CSIDL_STARTMENU, TRUE);
LPSHELLFOLDER psf;
HRESULT hres = (E_OUTOFMEMORY);
if (pidlStart) {
hres = s_pshfRoot->lpVtbl->BindToObject(s_pshfRoot, pidlStart, NULL, &IID_IShellFolder, &psf);
if (SUCCEEDED(hres))
{
IDropTarget *pdrop;
hres = psf->lpVtbl->CreateViewObject(psf, v_hwndTray, &IID_IDropTarget, &pdrop);
if (SUCCEEDED(hres))
{
POINTL pt = { 0, 0 };
DWORD grfKeyState;
*pdwEffect &= DROPEFFECT_LINK;
grfKeyState = 0;
pdrop->lpVtbl->DragEnter(pdrop, pdtobj, grfKeyState, pt,
pdwEffect);
hres = pdrop->lpVtbl->Drop(pdrop, pdtobj, grfKeyState, pt,
pdwEffect);
if (SUCCEEDED(hres) && *pdwEffect) {
Sleep(SECOND);
g_ts.fFavInvalid = TRUE;
}
pdrop->lpVtbl->DragLeave(pdrop);
pdrop->lpVtbl->Release(pdrop);
}
psf->lpVtbl->Release(psf);
}
ILFree(pidlStart);
}
DAD_DragLeave();
return hres;
}
STDMETHODIMP CTabDropTarget_Drop(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj,
DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
if (pdtgt==&c_dtgtTab)
{
LPFileCabinet pfc = g_pfcTray;
DAD_DragLeave();
if (pfc)
{
ShellMessageBox(hinstCabinet, pfc->hwndMain,
MAKEINTRESOURCE(IDS_TASKDROP_ERROR),
MAKEINTRESOURCE(IDS_TASKBAR),
MB_ICONHAND | MB_OK);
}
//
// We should not return the 'move' bit.
//
if (*pdwEffect & DROPEFFECT_COPY) {
*pdwEffect = DROPEFFECT_COPY;
} else {
*pdwEffect &= DROPEFFECT_LINK;
}
}
return S_OK;
}
// store our drop target in pdtgtTree
//=============================================================================
// CStartDropTarget : Register and Revoke
//=============================================================================
void CStartDropTarget_Register(LPFileCabinet pfc)
{
g_cfIDList = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
SHRegisterDragDrop(g_ts.hwndStart, &c_dtgtStart);
SHRegisterDragDrop(pfc->hwndView, &c_dtgtTab);
SHRegisterDragDrop(pfc->hwndMain, &c_dtgtTray);
}
void CStartDropTarget_Revoke(LPFileCabinet pfc)
{
SHRevokeDragDrop(pfc->hwndMain);
SHRevokeDragDrop(pfc->hwndView);
SHRevokeDragDrop(g_ts.hwndStart);
}
//=============================================================================
// CStartDropTarget : VTable
//=============================================================================
IDropTargetVtbl c_CStartDropTargetVtbl = {
CStartDropTarget_QueryInterface,
CStartDropTarget_AddRef,
CStartDropTarget_Release,
CStartDropTarget_DragEnter,
CStartDropTarget_DragOver,
CStartDropTarget_DragLeave,
CStartDropTarget_Drop
};
IDropTarget c_dtgtStart = { &c_CStartDropTargetVtbl };
IDropTargetVtbl c_CTabDropTargetVtbl = {
CStartDropTarget_QueryInterface,
CStartDropTarget_AddRef,
CStartDropTarget_Release,
CTabDropTarget_DragEnter,
CTabDropTarget_DragOver,
CStartDropTarget_DragLeave,
CTabDropTarget_Drop
};
IDropTarget c_dtgtTab = { &c_CTabDropTargetVtbl };
IDropTarget c_dtgtTray = { &c_CTabDropTargetVtbl };
//// end Start Drop target
////////////////////////////////////////////////////////////////////////////
// Begin Print Notify stuff
////////////////////////////////////////////////////////////////////////////
typedef BOOL (* PFNENUMPRINTERS) (DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
typedef BOOL (* PFNENUMJOBS) (HANDLE, DWORD, DWORD, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
typedef BOOL (* PFNOPENPRINTER) (LPTSTR, LPHANDLE, LPVOID);
typedef BOOL (* PFNCLOSEPRINTER) (HANDLE);
PFNENUMPRINTERS g_pfnEnumPrinters = NULL;
PFNENUMJOBS g_pfnEnumJobs = NULL;
PFNOPENPRINTER g_pfnOpenPrinter = NULL;
PFNCLOSEPRINTER g_pfnClosePrinter = NULL;
TCHAR const c_szSpoolClass[] = TEXT("SpoolProcessClass");
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;
DebugMsg(DM_TRACE, TEXT("sh 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(c_szSpoolClass, NULL);
if (hwndSpool)
{
NMHDR nmhdr = {hWnd, 0, NM_ENDWAIT};
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - Telling print subsystem we're here"));
SendMessage(hwndSpool, WM_NOTIFY, (WPARAM)NM_ENDWAIT, (LPARAM)&nmhdr);
}
}
}
typedef BOOL (*ENUMPROP)(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum);
LPVOID Printer_EnumProps(HANDLE hPrinter, DWORD dwLevel, DWORD *lpdwNum,
ENUMPROP lpfnEnum, LPVOID 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 = Alloc(dwSize);
if (!pEnum)
{
goto Error1;
}
SetLastError(0);
if (!lpfnEnum(lpData, hPrinter, dwLevel, pEnum, dwSize, &dwNeeded, lpdwNum))
{
Free(pEnum);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
dwSize = dwNeeded;
goto TryAgain;
}
pEnum = NULL;
}
Error1:
return(pEnum);
}
BOOL _EnumPrintersCB(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return(g_pfnEnumPrinters((DWORD)lpData, NULL, dwLevel, pEnum, dwSize, lpdwNeeded, lpdwNum));
}
DWORD _EnumPrinters(DWORD dwType, DWORD dwLevel, LPVOID *ppPrinters)
{
DWORD dwNum = 0L;
*ppPrinters = Printer_EnumProps(NULL, dwLevel, &dwNum, _EnumPrintersCB, (LPVOID)dwType);
if (*ppPrinters==NULL)
{
dwNum=0;
}
return(dwNum);
}
BOOL _EnumJobsCB(LPVOID 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, LPVOID *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 aArgs[2];
NOTIFYICONDATA IconData;
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);
if (!SFCInitializeThread())
{
goto Error0;
}
// 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_pfnEnumPrinters = (PFNENUMPRINTERS)GetProcAddress(hmodWinspool, "EnumPrintersW");
g_pfnEnumJobs = (PFNENUMJOBS) GetProcAddress(hmodWinspool, "EnumJobsW");
g_pfnOpenPrinter = (PFNOPENPRINTER) GetProcAddress(hmodWinspool, "OpenPrinterW");
#else
g_pfnEnumPrinters = (PFNENUMPRINTERS)GetProcAddress(hmodWinspool, "EnumPrintersA");
g_pfnEnumJobs = (PFNENUMJOBS) GetProcAddress(hmodWinspool, "EnumJobsA");
g_pfnOpenPrinter = (PFNOPENPRINTER) GetProcAddress(hmodWinspool, "OpenPrinterA");
#endif
g_pfnClosePrinter = (PFNCLOSEPRINTER)GetProcAddress(hmodWinspool, "ClosePrinter");
if (!g_pfnEnumPrinters || !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.
DebugMsg(DM_TRACE, TEXT("sh TR - GetUserName failed!"));
Assert(0); // why would this fail?
szUserName[0] = TEXT('\0');
}
LoadString(hinstCabinet, IDS_NUMPRINTJOBS, szFormat, ARRAYSIZE(szFormat));
aArgs[0] = (DWORD)-1;
aArgs[1] = (DWORD)szUserName;
// Set up initial data
ZeroMemory(&IconData, SIZEOF(IconData));
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
DebugMsg(DM_TRACE, TEXT("sh 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
DebugMsg(DM_TRACE, TEXT("sh 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:
{
HMENU hmenu;
// REVIEW: why does Tray_ContextMenu do it this way?
// Don't we have a function to get the submenu?
hmenu = LoadMenu(hinstCabinet, MAKEINTRESOURCE(MENU_PRINTNOTIFYCONTEXT));
if (hmenu)
{
HMENU hmContext;
int idCmd;
int idDefCmd = IDM_PRINTNOTIFY_FOLDER;
hmContext = GetSubMenu(hmenu, 0);
// 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)
{
LPPRINTERNAME pPrinter;
TCHAR szBuf[MAXPRINTERBUFFER+60];
LPTSTR pName;
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)
{
HWND hwndOwner;
DWORD dwPos;
// We need an hwnd owned by this thread.
hwndOwner = CreateWindow(c_szStatic, NULL,
WS_DISABLED, 0, 0, 0, 0,
NULL, NULL, hinstCabinet, 0L);
dwPos = GetMessagePos();
SetMenuDefaultItem(hmContext, idDefCmd, MF_BYCOMMAND);
SetForegroundWindow(hwndOwner);
SetFocus(hwndOwner);
idCmd = TrackPopupMenu(hmContext,
TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
LOWORD(dwPos), HIWORD(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;
LPCONTEXTMENU pcm;
pPrinter = DSA_GetItemPtr(haPrinterNames,
iStart);
// open selected queue view
DebugMsg(DM_TRACE, TEXT("sh 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))
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
NULL,
(LPSTR)MAKEINTRESOURCE(GetMenuDefaultItem(hmenuTmp, MF_BYCOMMAND, 0)),
NULL, NULL,
SW_NORMAL,
};
pcm->lpVtbl->InvokeCommand(pcm,
(LPCMINVOKECOMMANDINFO)&ici);
}
DestroyMenu(hmenuTmp);
}
pcm->lpVtbl->Release(pcm);
}
}
}
DestroyMenu(hmenu);
} // if (hmenu)
break;
}
case SHCNE_CREATE:
case SHCNE_DELETE:
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - Thread_PrinterPoll SHCNE_CREATE/DELETE"));
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
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - can't open [%s]"), pPrinter->szPrinterName);
goto remove_printer;
}
DebugMsg(DM_TRACE, TEXT("sh 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_ERROR_BITS)
pPrinter->fInErrorState = TRUE;
fFound = TRUE;
}
}
g_pfnClosePrinter(hPrinter);
if (pJobs)
{
Free(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)
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - net is down for [%s]"), pPrinter->szPrinterName);
else
DebugMsg(DM_TRACE, TEXT("sh 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)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)
{
DebugMsg(DM_TRACE, TEXT("sh 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:
//
// We need to free the pidl if one is passed in.
// (Currently one is not.)
//
pidlFree = (LPITEMIDLIST)msg.lParam;
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - Thread_PrinterPoll SHCNE_UPDATEITEM"));
// if the JOB_STATUS_ERROR_BITS 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:
DebugMsg(DM_TRACE, TEXT("sh 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:
SFCTerminateThread();
Error0:
{
HANDLE htPrinterPoll;
// 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).
ENTERCRITICAL;
htPrinterPoll = g_ts.htPrinterPoll;
g_ts.htPrinterPoll = NULL;
LEAVECRITICAL;
Assert(htPrinterPoll);
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);
}
// 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;
LPOneTreeNode pnode;
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)
{
pnode = OTGetNodeFromIDList(g_ts.pidlPrintersFolder, OTGNF_TRYADD | OTGNF_VALIDATE);
if (!pnode)
{
goto exit;
}
psf = OTBindToFolder(pnode);
OTRelease(pnode);
if (!psf)
{
goto exit;
}
*ppsf = psf;
}
// all this work to get the name of the printer:
psf->lpVtbl->GetDisplayNameOf(psf, pidlPrinter, SHGDN_FORPARSING, &strret);
// 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
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - [%s] already in poll list"), pNewPrinter);
goto exit;
}
}
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - adding [%s] to poll list"), pNewPrinter);
lstrcpy(pn.szPrinterName, pNewPrinter);
pn.fInErrorState = FALSE;
pn.pidlPrinter = ILClone(pidlPrinter);
DSA_InsertItem(*phaPrinterNames, 0x7fff, &pn);
exit:
#ifdef UNICODE
//
// Free the allocated string from GetDisplayNameOf in strret.
//
if (pszFree)
{
SHFree((LPVOID)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);
DebugMsg(DM_TRACE, TEXT("sh PRINTNOTIFY - Thread_PrinterPoll started"));
}
else
{
GlobalFree(pPrinterPollData);
}
goto Exit;
Error_Exit:
LEAVECRITICAL;
Exit:
return fRet;
}
void PrintNotify_HandleFSNotify(HWND hWnd, LPCITEMIDLIST *ppidl, LONG lEvent)
{
LPITEMIDLIST pidl;
// 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).
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)
{
// this was whacked with one of the above ILRemoveLastID()s
pidlPrinter->mkid.cb = cbPidlPrinter;
// this is freed in the polling thread
pidlPrinterName = ILGlobalClone(pidlPrinter);
}
// 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, UINT uMsg)
{
switch (uMsg)
{
case WM_LBUTTONDBLCLK:
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, WM_USER+uMsg, 0, 0);
}
else if (uMsg == WM_LBUTTONDBLCLK)
{
ShowFolder(hWnd, IDM_PRINTERS, COF_USEOPENSETTINGS);
}
break;
}
}
////////////////////////////////////////////////////////////////////////////
// End Print Notify stuff
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////////////////////
BOOL IsDisplayChangeSafe()
{
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;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
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
DebugMsg(DM_TRACE, TEXT("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);
DebugMsg(DM_TRACE, TEXT("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);
}
DebugMsg(DM_TRACE, TEXT("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 HandleGlobalHotkey(WPARAM wParam)
{
PFileCabinet pfc = g_pfcTray;
switch(wParam)
{
case GHID_RUN:
Tray_RunDlg();
break;
case GHID_MINIMIZEALL:
if (CanMinimizeAll(pfc->hwndView))
MinimizeAll(pfc->hwndView);
SetForegroundWindow(v_hwndDesktop);
break;
case GHID_UNMINIMIZEALL:
RestoreWindowPositions();
break;
case GHID_HELP:
TrayCommand(pfc, IDM_HELPSEARCH);
break;
case GHID_EXPLORER:
ShowFolder(pfc->hwndMain, IDM_MYCOMPUTER, COF_CREATENEWWINDOW | COF_EXPLORE);
break;
case GHID_FINDFILES:
TrayCommand(pfc, FCIDM_FINDFILES);
break;
case GHID_FINDCOMPUTER:
TrayCommand(pfc, FCIDM_FINDCOMPUTER);
break;
case GHID_TASKTAB:
case GHID_TASKSHIFTTAB:
if (GetForegroundWindow() != v_hwndTray)
SetForegroundWindow(v_hwndTray);
SendMessage(pfc->hwndView, TM_TASKTAB, wParam == GHID_TASKTAB ? 1 : -1, 0L);
break;
case GHID_SYSPROPERTIES:
#define IDS_SYSDMCPL 0x2334 // from shelldll
SHRunControlPanel(MAKEINTRESOURCE(IDS_SYSDMCPL), pfc->hwndMain);
break;
}
}
void UnregisterGlobalHotkeys()
{
int i;
for (i = 0 ; i < GHID_MAX; i++) {
UnregisterHotKey(v_hwndDesktop, i);
};
}
void RegisterGlobalHotkeys()
{
int i;
for (i = 0 ; i < GHID_MAX; i++) {
RegisterHotKey(v_hwndDesktop, i, HIWORD(GlobalKeylist[i]), LOWORD(GlobalKeylist[i]));
};
}