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.
9156 lines
271 KiB
9156 lines
271 KiB
#include "cabinet.h"
|
|
|
|
#include <wtsapi32.h> // for NOTIFY_FOR_THIS_SESSION
|
|
#include <winsta.h> // for disconnect and reconnect messages from terminal server
|
|
#include "mmsysp.h"
|
|
|
|
#include "rcids.h"
|
|
#include "dlg.h"
|
|
|
|
#include <atlstuff.h>
|
|
|
|
#include <shlapip.h>
|
|
#include "trayclok.h"
|
|
#include <help.h> // help ids
|
|
#include <desktray.h>
|
|
|
|
#include "util.h"
|
|
#include "tray.h"
|
|
|
|
#if defined(FE_IME)
|
|
#include <immp.h>
|
|
#endif
|
|
|
|
#include <regstr.h>
|
|
|
|
#include "bandsite.h"
|
|
|
|
#include "startmnu.h"
|
|
#include "uemapp.h"
|
|
#include <uxthemep.h>
|
|
|
|
#define NO_NOTIFYSUBCLASSWNDPROC
|
|
#include "cwndproc.cpp"
|
|
|
|
#include "desktop2.h"
|
|
#include "mixer.h"
|
|
|
|
#include "strsafe.h"
|
|
|
|
#define DM_FOCUS 0 // focus
|
|
#define DM_SHUTDOWN TF_TRAY // shutdown
|
|
#define DM_UEMTRACE TF_TRAY // timer service, other UEM stuff
|
|
#define DM_MISC 0 // miscellany
|
|
|
|
const GUID CLSID_MSUTBDeskBand = {0x540d8a8b, 0x1c3f, 0x4e32, 0x81, 0x32, 0x53, 0x0f, 0x6a, 0x50, 0x20, 0x90};
|
|
|
|
// From Desktop2\proglist.cpp
|
|
HRESULT AddMenuItemsCacheTask(IShellTaskScheduler* pSystemScheduler, BOOL fKeepCacheWhenFinished);
|
|
|
|
// import the WIN31 Compatibility HACKs from the shell32.dll
|
|
STDAPI_(void) CheckWinIniForAssocs(void);
|
|
|
|
// hooks to Shell32.dll
|
|
STDAPI CheckDiskSpace();
|
|
STDAPI CheckStagingArea();
|
|
|
|
// startmnu.cpp
|
|
void HandleFirstTime();
|
|
|
|
HWND v_hwndDesktop = NULL;
|
|
HWND v_hwndTray = NULL;
|
|
HWND v_hwndStartPane = NULL;
|
|
|
|
BOOL g_fDesktopRaised = FALSE;
|
|
BOOL g_fInSizeMove = FALSE;
|
|
|
|
UINT _uMsgEnableUserTrackedBalloonTips = 0;
|
|
|
|
void ClearRecentDocumentsAndMRUStuff(BOOL fBroadcastChange);
|
|
void DoTaskBarProperties(HWND hwnd, DWORD dwFlags);
|
|
|
|
void ClassFactory_Start();
|
|
void ClassFactory_Stop();
|
|
|
|
//
|
|
// Settings UI entry point types.
|
|
//
|
|
typedef void (WINAPI *PTRAYPROPSHEETCALLBACK)(DWORD nStartPage);
|
|
typedef void (WINAPI *PSETTINGSUIENTRY)(PTRAYPROPSHEETCALLBACK);
|
|
|
|
// Shell perf automation
|
|
extern DWORD g_dwShellStartTime;
|
|
extern DWORD g_dwShellStopTime;
|
|
extern DWORD g_dwStopWatchMode;
|
|
|
|
CTray c_tray;
|
|
|
|
// from explorer\desktop2
|
|
STDAPI DesktopV2_Create(
|
|
IMenuPopup **ppmp, IMenuBand **ppmb, void **ppvStartPane);
|
|
STDAPI DesktopV2_Build(void *pvStartPane);
|
|
|
|
// dyna-res change for multi-config hot/warm-doc
|
|
void HandleDisplayChange(int x, int y, BOOL fCritical);
|
|
DWORD GetMinDisplayRes(void);
|
|
|
|
// timer IDs
|
|
#define IDT_AUTOHIDE 2
|
|
#define IDT_AUTOUNHIDE 3
|
|
#ifdef DELAYWININICHANGE
|
|
#define IDT_DELAYWININICHANGE 5
|
|
#endif
|
|
#define IDT_DESKTOP 6
|
|
#define IDT_PROGRAMS IDM_PROGRAMS
|
|
#define IDT_RECENT IDM_RECENT
|
|
#define IDT_REBUILDMENU 7
|
|
#define IDT_HANDLEDELAYBOOTSTUFF 8
|
|
#define IDT_REVERTPROGRAMS 9
|
|
#define IDT_REVERTRECENT 10
|
|
#define IDT_REVERTFAVORITES 11
|
|
|
|
#define IDT_STARTMENU 12
|
|
|
|
#define IDT_ENDUNHIDEONTRAYNOTIFY 13
|
|
|
|
#define IDT_SERVICE0 14
|
|
#define IDT_SERVICE1 15
|
|
#define IDT_SERVICELAST IDT_SERVICE1
|
|
#define IDT_SAVESETTINGS 17
|
|
#define IDT_ENABLEUNDO 18
|
|
#define IDT_STARTUPFAILED 19
|
|
#define IDT_CHECKDISKSPACE 21
|
|
#define IDT_STARTBUTTONBALLOON 22
|
|
#define IDT_CHANGENOTIFY 23
|
|
#define IDT_COFREEUNUSED 24
|
|
#define IDT_DESKTOPCLEANUP 25
|
|
|
|
#define FADEINDELAY 100
|
|
#define BALLOONTIPDELAY 10000 // default balloon time copied from traynot.cpp
|
|
|
|
|
|
// INSTRUMENTATION WARNING: If you change anything here, make sure to update instrument.c
|
|
// we need to start at 500 because we're now sharing the hotkey handler
|
|
// with shortcuts.. they use an index array so they need to be 0 based
|
|
// NOTE, this constant is also in desktop.cpp, so that we can forward hotkeys from the desktop for
|
|
// NOTE, app compatibility.
|
|
#define GHID_FIRST 500
|
|
enum
|
|
{
|
|
GHID_RUN = GHID_FIRST,
|
|
GHID_MINIMIZEALL,
|
|
GHID_UNMINIMIZEALL,
|
|
GHID_HELP,
|
|
GHID_EXPLORER,
|
|
GHID_FINDFILES,
|
|
GHID_FINDCOMPUTER,
|
|
GHID_TASKTAB,
|
|
GHID_TASKSHIFTTAB,
|
|
GHID_SYSPROPERTIES,
|
|
GHID_DESKTOP,
|
|
GHID_TRAYNOTIFY,
|
|
GHID_MAX
|
|
};
|
|
|
|
const DWORD GlobalKeylist[] =
|
|
{
|
|
MAKELONG(TEXT('R'), MOD_WIN),
|
|
MAKELONG(TEXT('M'), MOD_WIN),
|
|
MAKELONG(TEXT('M'), MOD_SHIFT|MOD_WIN),
|
|
MAKELONG(VK_F1,MOD_WIN),
|
|
MAKELONG(TEXT('E'),MOD_WIN),
|
|
MAKELONG(TEXT('F'),MOD_WIN),
|
|
MAKELONG(TEXT('F'), MOD_CONTROL|MOD_WIN),
|
|
MAKELONG(VK_TAB, MOD_WIN),
|
|
MAKELONG(VK_TAB, MOD_WIN|MOD_SHIFT),
|
|
MAKELONG(VK_PAUSE,MOD_WIN),
|
|
MAKELONG(TEXT('D'),MOD_WIN),
|
|
MAKELONG(TEXT('B'),MOD_WIN),
|
|
};
|
|
|
|
CTray::CTray() : _fCanSizeMove(TRUE), _fIsLogoff(FALSE), _fIsDesktopConnected(TRUE)
|
|
{
|
|
}
|
|
|
|
void CTray::ClosePopupMenus()
|
|
{
|
|
if (_pmpStartMenu)
|
|
_pmpStartMenu->OnSelect(MPOS_FULLCANCEL);
|
|
if (_pmpStartPane)
|
|
_pmpStartPane->OnSelect(MPOS_FULLCANCEL);
|
|
}
|
|
|
|
BOOL Tray_StartPanelEnabled()
|
|
{
|
|
SHELLSTATE ss = {0};
|
|
SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE);
|
|
return ss.fStartPanelOn;
|
|
}
|
|
|
|
//
|
|
// The StartButtonBalloonTip registry value can have one of these values:
|
|
//
|
|
// 0 (or nonexistent): User has never clicked the Start Button.
|
|
// 1: User has clicked the Start Button on a pre-Whistler system.
|
|
// 2: User has clicked the Start Button on a Whistler system.
|
|
//
|
|
// In case 0, we always want to show the balloon tip regardless of whether
|
|
// the user is running Classic or Personal.
|
|
//
|
|
// In case 1, we want to show the balloon tip if the user is using the
|
|
// Personal Start Menu, but not if using Classic (since he's already
|
|
// seen the Classic Start Menu). In the Classic case, upgrade the counter
|
|
// to 2 so the user won't be annoyed when they switch from Classic to
|
|
// Personal.
|
|
//
|
|
// In case 2, we don't want to show the balloon tip at all since the
|
|
// user has seen all we have to offer.
|
|
//
|
|
BOOL CTray::_ShouldWeShowTheStartButtonBalloon()
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwData = 0;
|
|
DWORD cbSize = sizeof(DWORD);
|
|
SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED,
|
|
TEXT("StartButtonBalloonTip"), &dwType, (BYTE*)&dwData, &cbSize);
|
|
|
|
if (Tray_StartPanelEnabled())
|
|
{
|
|
// Personal Start Menu is enabled, so show the balloon if the
|
|
// user has never logged on to a Whistler machine before.
|
|
return dwData < 2;
|
|
}
|
|
else
|
|
{
|
|
// Classic Start Menu is enabled.
|
|
switch (dwData)
|
|
{
|
|
case 0:
|
|
// User has never seen the Start Menu before, not even the
|
|
// classic one. So show the tip.
|
|
return TRUE;
|
|
|
|
case 1:
|
|
// User has already seen the Classic Start Menu, so don't
|
|
// prompt them again. Note that this means that they aren't
|
|
// prompted when they turn on the Personal Start Menu, but
|
|
// that's okay, because by the time they switch to Personal,
|
|
// they clearly have demonstrated that they know how the
|
|
// Start Button works and don't need a tip.
|
|
_DontShowTheStartButtonBalloonAnyMore();
|
|
return FALSE;
|
|
|
|
default:
|
|
// User has seen Whistler Start menu before, so don't show tip.
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the value to 2 to indicate that the user has seen a Whistler
|
|
// Start Menu (either Classic or Personal).
|
|
//
|
|
void CTray::_DontShowTheStartButtonBalloonAnyMore()
|
|
{
|
|
DWORD dwData = 2;
|
|
SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED,
|
|
TEXT("StartButtonBalloonTip"), REG_DWORD, (BYTE*)&dwData, sizeof(dwData));
|
|
}
|
|
|
|
void CTray::_DestroyStartButtonBalloon()
|
|
{
|
|
if (_hwndStartBalloon)
|
|
{
|
|
DestroyWindow(_hwndStartBalloon);
|
|
_hwndStartBalloon = NULL;
|
|
}
|
|
KillTimer(_hwnd, IDT_STARTBUTTONBALLOON);
|
|
}
|
|
|
|
void CTray::CreateStartButtonBalloon(UINT idsTitle, UINT idsMessage)
|
|
{
|
|
if (!_hwndStartBalloon)
|
|
{
|
|
|
|
_hwndStartBalloon = CreateWindow(TOOLTIPS_CLASS, NULL,
|
|
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
_hwnd, NULL, hinstCabinet,
|
|
NULL);
|
|
|
|
if (_hwndStartBalloon)
|
|
{
|
|
// set the version so we can have non buggy mouse event forwarding
|
|
SendMessage(_hwndStartBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
|
|
SendMessage(_hwndStartBalloon, TTM_SETMAXTIPWIDTH, 0, (LPARAM)300);
|
|
|
|
// taskbar windows are themed under Taskbar subapp name
|
|
SendMessage(_hwndStartBalloon, TTM_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskbarTheme);
|
|
|
|
// Tell the Start Menu that this is a special balloon tip
|
|
SetProp(_hwndStartBalloon, PROP_DV2_BALLOONTIP, DV2_BALLOONTIP_STARTBUTTON);
|
|
}
|
|
}
|
|
|
|
if (_hwndStartBalloon)
|
|
{
|
|
TCHAR szTip[MAX_PATH];
|
|
szTip[0] = TEXT('\0');
|
|
LoadString(hinstCabinet, idsMessage, szTip, ARRAYSIZE(szTip));
|
|
if (szTip[0])
|
|
{
|
|
RECT rc;
|
|
TOOLINFO ti = {0};
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
|
|
ti.hwnd = _hwnd;
|
|
ti.uId = (UINT_PTR)_hwndStart;
|
|
//ti.lpszText = NULL;
|
|
SendMessage(_hwndStartBalloon, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
SendMessage(_hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
|
|
|
|
ti.lpszText = szTip;
|
|
SendMessage(_hwndStartBalloon, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
|
|
|
|
LoadString(hinstCabinet, idsTitle, szTip, ARRAYSIZE(szTip));
|
|
if (szTip[0])
|
|
{
|
|
SendMessage(_hwndStartBalloon, TTM_SETTITLE, TTI_INFO, (LPARAM)szTip);
|
|
}
|
|
|
|
GetWindowRect(_hwndStart, &rc);
|
|
|
|
SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
|
|
|
|
SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
|
|
|
|
SendMessage(_hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
|
|
|
|
SetTimer(_hwnd, IDT_STARTBUTTONBALLOON, BALLOONTIPDELAY, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_ShowStartButtonToolTip()
|
|
{
|
|
if (!_ShouldWeShowTheStartButtonBalloon() || SHRestricted(REST_NOSMBALLOONTIP))
|
|
{
|
|
PostMessage(_hwnd, TM_SHOWTRAYBALLOON, TRUE, 0);
|
|
return;
|
|
}
|
|
|
|
if (Tray_StartPanelEnabled())
|
|
{
|
|
// In order to display the Start Menu, we need foreground activation
|
|
// so keyboard focus will work properly.
|
|
if (SetForegroundWindow(_hwnd))
|
|
{
|
|
// Inform the tray that start button is auto-popping, so the tray
|
|
// can hold off on showing balloons.
|
|
PostMessage(_hwnd, TM_SHOWTRAYBALLOON, FALSE, 0);
|
|
|
|
// This pushes the start button and causes the start menu to popup.
|
|
SendMessage(GetDlgItem(_hwnd, IDC_START), BM_SETSTATE, TRUE, 0);
|
|
|
|
// Once successfully done once, don't do it again.
|
|
_DontShowTheStartButtonBalloonAnyMore();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PostMessage(_hwnd, TM_SHOWTRAYBALLOON, TRUE, 0);
|
|
CreateStartButtonBalloon(IDS_STARTMENUBALLOON_TITLE, IDS_STARTMENUBALLOON_TIP);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CTray::_CreateClockWindow()
|
|
{
|
|
_hwndNotify = _trayNotify.TrayNotifyCreate(_hwnd, IDC_CLOCK, hinstCabinet);
|
|
SendMessage(_hwndNotify, TNM_UPDATEVERTICAL, 0, !STUCK_HORIZONTAL(_uStuckPlace));
|
|
|
|
return BOOLFROMPTR(_hwndNotify);
|
|
}
|
|
|
|
BOOL CTray::_InitTrayClass()
|
|
{
|
|
WNDCLASS wc = {0};
|
|
|
|
wc.lpszClassName = TEXT(WNDCLASS_TRAYNOTIFY);
|
|
wc.style = CS_DBLCLKS;
|
|
wc.lpfnWndProc = s_WndProc;
|
|
wc.hInstance = hinstCabinet;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
|
|
wc.cbWndExtra = sizeof(LONG_PTR);
|
|
|
|
return RegisterClass(&wc);
|
|
}
|
|
|
|
|
|
HFONT CTray::_CreateStartFont(HWND hwndTray)
|
|
{
|
|
HFONT hfontStart = NULL;
|
|
NONCLIENTMETRICS ncm;
|
|
|
|
ncm.cbSize = sizeof(ncm);
|
|
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
|
|
{
|
|
WORD wLang = GetUserDefaultLangID();
|
|
|
|
// Select normal weight font for chinese language.
|
|
if (PRIMARYLANGID(wLang) == LANG_CHINESE &&
|
|
((SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) ||
|
|
(SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED)))
|
|
ncm.lfCaptionFont.lfWeight = FW_NORMAL;
|
|
else
|
|
ncm.lfCaptionFont.lfWeight = FW_BOLD;
|
|
|
|
hfontStart = CreateFontIndirect(&ncm.lfCaptionFont);
|
|
}
|
|
|
|
return hfontStart;
|
|
}
|
|
|
|
// Set the stuck monitor for the tray window
|
|
void CTray::_SetStuckMonitor()
|
|
{
|
|
// use STICK_LEFT because most of the multi-monitors systems are set up
|
|
// side by side. use DEFAULTTONULL because we don't want to get the wrong one
|
|
// use the center point to call again in case we failed the first time.
|
|
_hmonStuck = MonitorFromRect(&_arStuckRects[STICK_LEFT],
|
|
MONITOR_DEFAULTTONULL);
|
|
if (!_hmonStuck)
|
|
{
|
|
POINT pt;
|
|
pt.x = (_arStuckRects[STICK_LEFT].left + _arStuckRects[STICK_LEFT].right)/2;
|
|
pt.y = (_arStuckRects[STICK_LEFT].top + _arStuckRects[STICK_LEFT].bottom)/2;
|
|
_hmonStuck = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
|
}
|
|
|
|
_hmonOld = _hmonStuck;
|
|
}
|
|
|
|
DWORD _GetDefaultTVSDFlags()
|
|
{
|
|
DWORD dwFlags = TVSD_TOPMOST;
|
|
|
|
// if we are on a remote hydra session and if there is no previous saved value,
|
|
// do not display the clock.
|
|
if (SHGetMachineInfo(GMI_TSCLIENT))
|
|
{
|
|
dwFlags |= TVSD_HIDECLOCK;
|
|
}
|
|
return dwFlags;
|
|
}
|
|
|
|
void CTray::_GetSaveStateAndInitRects()
|
|
{
|
|
TVSDCOMPAT tvsd;
|
|
RECT rcDisplay;
|
|
DWORD dwTrayFlags;
|
|
UINT uStick;
|
|
SIZE size;
|
|
|
|
// first fill in the defaults
|
|
SetRect(&rcDisplay, 0, 0, g_cxPrimaryDisplay, g_cyPrimaryDisplay);
|
|
|
|
// size gets defaults
|
|
size.cx = _sizeStart.cx + 2 * (g_cxDlgFrame + g_cxBorder);
|
|
size.cy = _sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
|
|
|
|
// sStuckWidths gets minimum
|
|
_sStuckWidths.cx = 2 * (g_cxDlgFrame + g_cxBorder);
|
|
_sStuckWidths.cy = _sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
|
|
|
|
_uStuckPlace = STICK_BOTTOM;
|
|
dwTrayFlags = _GetDefaultTVSDFlags();
|
|
|
|
_uAutoHide = 0;
|
|
|
|
// now try to load saved vaules
|
|
|
|
// BUG : 231077
|
|
// Since Tasbar properties don't roam from NT5 to NT4, (NT4 -> NT5 yes)
|
|
// Allow roaming from NT4 to NT5 only for the first time the User logs
|
|
// on to NT5, so that future changes to NT5 are not lost when the user
|
|
// logs on to NT4 after customizing the taskbar properties on NT5.
|
|
|
|
DWORD cbData1 = sizeof(tvsd);
|
|
DWORD cbData2 = sizeof(tvsd);
|
|
if (Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"),
|
|
&tvsd, &cbData1)
|
|
||
|
|
Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects"), TEXT("Settings"),
|
|
&tvsd, &cbData2))
|
|
{
|
|
if (IS_CURRENT_TVSD(tvsd.t) && IsValidSTUCKPLACE(tvsd.t.uStuckPlace))
|
|
{
|
|
_GetDisplayRectFromRect(&rcDisplay, &tvsd.t.rcLastStuck,
|
|
MONITOR_DEFAULTTONEAREST);
|
|
|
|
size = tvsd.t.sStuckWidths;
|
|
_uStuckPlace = tvsd.t.uStuckPlace;
|
|
|
|
dwTrayFlags = tvsd.t.dwFlags;
|
|
}
|
|
else if (MAYBE_WIN95_TVSD(tvsd.w95) &&
|
|
IsValidSTUCKPLACE(tvsd.w95.uStuckPlace))
|
|
{
|
|
_uStuckPlace = tvsd.w95.uStuckPlace;
|
|
dwTrayFlags = tvsd.w95.dwFlags;
|
|
if (tvsd.w95.uAutoHide & AH_ON)
|
|
dwTrayFlags |= TVSD_AUTOHIDE;
|
|
|
|
switch (_uStuckPlace)
|
|
{
|
|
case STICK_LEFT:
|
|
size.cx = tvsd.w95.dxLeft;
|
|
break;
|
|
|
|
case STICK_RIGHT:
|
|
size.cx = tvsd.w95.dxRight;
|
|
break;
|
|
|
|
case STICK_BOTTOM:
|
|
size.cy = tvsd.w95.dyBottom;
|
|
break;
|
|
|
|
case STICK_TOP:
|
|
size.cy = tvsd.w95.dyTop;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ASSERT(IsValidSTUCKPLACE(_uStuckPlace));
|
|
|
|
//
|
|
// use the size only if it is not bogus
|
|
//
|
|
if (_sStuckWidths.cx < size.cx)
|
|
_sStuckWidths.cx = size.cx;
|
|
|
|
if (_sStuckWidths.cy < size.cy)
|
|
_sStuckWidths.cy = size.cy;
|
|
|
|
//
|
|
// set the tray flags
|
|
//
|
|
_fAlwaysOnTop = BOOLIFY(dwTrayFlags & TVSD_TOPMOST);
|
|
_fSMSmallIcons = BOOLIFY(dwTrayFlags & TVSD_SMSMALLICONS);
|
|
_fHideClock = SHRestricted(REST_HIDECLOCK) || BOOLIFY(dwTrayFlags & TVSD_HIDECLOCK);
|
|
_uAutoHide = (dwTrayFlags & TVSD_AUTOHIDE) ? AH_ON | AH_HIDING : 0;
|
|
_RefreshSettings();
|
|
|
|
//
|
|
// initialize stuck rects
|
|
//
|
|
for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
|
|
_MakeStuckRect(&_arStuckRects[uStick], &rcDisplay, _sStuckWidths, uStick);
|
|
|
|
_UpdateVertical(_uStuckPlace);
|
|
// Determine which monitor the tray is on using its stuck rectangles
|
|
_SetStuckMonitor();
|
|
}
|
|
|
|
IBandSite * BandSite_CreateView();
|
|
HRESULT BandSite_SaveView(IUnknown *pbs);
|
|
LRESULT BandSite_OnMarshallBS(WPARAM wParam, LPARAM lParam);
|
|
|
|
void CTray::_SaveTrayStuff(void)
|
|
{
|
|
TVSD tvsd;
|
|
|
|
tvsd.dwSize = sizeof(tvsd);
|
|
tvsd.lSignature = TVSDSIG_CURRENT;
|
|
|
|
// position
|
|
CopyRect(&tvsd.rcLastStuck, &_arStuckRects[_uStuckPlace]);
|
|
tvsd.sStuckWidths = _sStuckWidths;
|
|
tvsd.uStuckPlace = _uStuckPlace;
|
|
|
|
tvsd.dwFlags = 0;
|
|
if (_fAlwaysOnTop) tvsd.dwFlags |= TVSD_TOPMOST;
|
|
if (_fSMSmallIcons) tvsd.dwFlags |= TVSD_SMSMALLICONS;
|
|
if (_fHideClock && !SHRestricted(REST_HIDECLOCK)) tvsd.dwFlags |= TVSD_HIDECLOCK;
|
|
if (_uAutoHide & AH_ON) tvsd.dwFlags |= TVSD_AUTOHIDE;
|
|
|
|
// Save in Stuck rects.
|
|
Reg_SetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), &tvsd, sizeof(tvsd));
|
|
|
|
BandSite_SaveView(_ptbs);
|
|
|
|
return;
|
|
}
|
|
|
|
// align toolbar so that buttons are flush with client area
|
|
// and make toolbar's buttons to be MENU style
|
|
void CTray::_AlignStartButton()
|
|
{
|
|
HWND hwndStart = _hwndStart;
|
|
if (hwndStart)
|
|
{
|
|
TCHAR szStart[50];
|
|
LoadString(hinstCabinet, _hTheme ? IDS_START : IDS_STARTCLASSIC, szStart, ARRAYSIZE(szStart));
|
|
SetWindowText(_hwndStart, szStart);
|
|
|
|
RECT rcClient;
|
|
if (!_sizeStart.cx)
|
|
{
|
|
Button_GetIdealSize(hwndStart, &_sizeStart);
|
|
}
|
|
GetClientRect(_hwnd, &rcClient);
|
|
|
|
if (rcClient.right < _sizeStart.cx)
|
|
{
|
|
SetWindowText(_hwndStart, L"");
|
|
}
|
|
|
|
int cyStart = _sizeStart.cy;
|
|
|
|
if (_hwndTasks)
|
|
{
|
|
if (_hTheme)
|
|
{
|
|
cyStart = max(cyStart, SendMessage(_hwndTasks, TBC_BUTTONHEIGHT, 0, 0));
|
|
}
|
|
else
|
|
{
|
|
cyStart = SendMessage(_hwndTasks, TBC_BUTTONHEIGHT, 0, 0);
|
|
}
|
|
}
|
|
|
|
SetWindowPos(hwndStart, NULL, 0, 0, min(rcClient.right, _sizeStart.cx),
|
|
cyStart, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
}
|
|
|
|
// Helper function for CDesktopHost so clicking twice on the Start Button
|
|
// treats the second click as a dismiss rather than a redisplay.
|
|
//
|
|
// The crazy state machine goes like this:
|
|
//
|
|
// SBSM_NORMAL - normal state, nothing exciting
|
|
//
|
|
// When user opens Start Pane, we become
|
|
//
|
|
// SBSM_SPACTIVE - start pane is active
|
|
//
|
|
// If user clicks Start Button while SBSM_SPACTIVE, then we become
|
|
//
|
|
// SBSM_EATING - eat mouse clicks
|
|
//
|
|
// Until we receive a WM_MOUSEFIRST/WM_MOUSELAST message, and then
|
|
// we return to SBSM_NORMAL.
|
|
//
|
|
// If user dismisses Start Pane, we go straight to SBSM_NORMAL.
|
|
//
|
|
//
|
|
// We eat the mouse clicks so that the click that the user made
|
|
// to "unclick" the start button doesn't cause it to get pushed down
|
|
// again (and cause the Start Menu to reopen).
|
|
//
|
|
#define SBSM_NORMAL 0
|
|
#define SBSM_SPACTIVE 1
|
|
#define SBSM_EATING 2
|
|
|
|
void Tray_SetStartPaneActive(BOOL fActive)
|
|
{
|
|
if (fActive)
|
|
{ // Start Pane appearing
|
|
c_tray._uStartButtonState = SBSM_SPACTIVE;
|
|
}
|
|
else if (c_tray._uStartButtonState != SBSM_EATING)
|
|
{ // Start Pane dismissing, not eating messages -> return to normal
|
|
c_tray._uStartButtonState = SBSM_NORMAL;
|
|
}
|
|
}
|
|
|
|
// Allow us to do stuff on a "button-down".
|
|
|
|
LRESULT WINAPI CTray::StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return c_tray._StartButtonSubclassWndProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CTray::_StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lRet;
|
|
|
|
ASSERT(_pfnButtonProc)
|
|
|
|
// Is the button going down?
|
|
if (uMsg == BM_SETSTATE)
|
|
{
|
|
// Is it going Down?
|
|
if (wParam)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
|
|
// Yes - proceed if it's currently up and it's allowed to be down
|
|
if (!_uDown)
|
|
{
|
|
// Nope.
|
|
INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_DOWN);
|
|
_uDown = 1;
|
|
|
|
// If we are going down, then we do not want to popup again until the Start Menu is collapsed
|
|
_fAllowUp = FALSE;
|
|
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
|
|
|
|
// Show the button down.
|
|
lRet = CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
|
|
// Notify the parent.
|
|
SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)LOWORD(GetDlgCtrlID(hwnd)), (LPARAM)hwnd);
|
|
_tmOpen = GetTickCount();
|
|
return lRet;
|
|
}
|
|
else
|
|
{
|
|
// Yep. Do nothing.
|
|
// fDown = FALSE;
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
|
|
// Nope, buttons coming up.
|
|
|
|
// Is it supposed to be down? Is it not allowed to be up?
|
|
if (_uDown == 1 || !_fAllowUp)
|
|
{
|
|
INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_UP);
|
|
|
|
// Yep, do nothing.
|
|
_uDown = 2;
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
|
|
// Nope, Forward it on.
|
|
_uDown = 0;
|
|
return CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_uStartButtonState == SBSM_EATING &&
|
|
uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
|
|
{
|
|
_uStartButtonState = SBSM_NORMAL;
|
|
|
|
// Explicitly dismiss the Start Panel because it might be
|
|
// stuck in this limbo state where it is open but not the
|
|
// foreground window (_ShowStartButtonToolTip does this)
|
|
// so it doesn't know that it needs to go away.
|
|
ClosePopupMenus();
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_LBUTTONDOWN:
|
|
// The button was clicked on, then we don't need no stink'n focus rect.
|
|
SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_SET,
|
|
UISF_HIDEFOCUS), 0);
|
|
|
|
goto ProcessCapture;
|
|
break;
|
|
|
|
|
|
case WM_KEYDOWN:
|
|
// The user pressed enter or return or some other bogus key combination when
|
|
// the start button had keyboard focus, so show the rect....
|
|
SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
|
|
UISF_HIDEFOCUS), 0);
|
|
|
|
if (wParam == VK_RETURN)
|
|
PostMessage(_hwnd, WM_COMMAND, IDC_KBSTART, 0);
|
|
|
|
// We do not need the capture, because we do all of our button processing
|
|
// on the button down. In fact taking capture for no good reason screws with
|
|
// drag and drop into the menus. We're overriding user.
|
|
ProcessCapture:
|
|
lRet = CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
|
|
SetCapture(NULL);
|
|
return lRet;
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
MSG msg;
|
|
|
|
msg.lParam = lParam;
|
|
msg.wParam = wParam;
|
|
msg.message = uMsg;
|
|
msg.hwnd = hwnd;
|
|
SendMessage(_hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)& msg);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEACTIVATE:
|
|
if (_uStartButtonState != SBSM_NORMAL)
|
|
{
|
|
_uStartButtonState = SBSM_EATING;
|
|
return MA_ACTIVATEANDEAT;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Debounce the Start Button. Usability shows that lots of people
|
|
// double-click the Start Button, resulting in the menu opening
|
|
// and then immediately closing...
|
|
//
|
|
case WM_NCHITTEST:
|
|
if (GetTickCount() - _tmOpen < GetDoubleClickTime())
|
|
{
|
|
return HTNOWHERE;
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_NULL:
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
EXTERN_C const WCHAR c_wzTaskbarTheme[] = L"Taskbar";
|
|
EXTERN_C const WCHAR c_wzTaskbarVertTheme[] = L"TaskbarVert";
|
|
|
|
// create the toolbar with the three buttons and align windows
|
|
|
|
HWND CTray::_CreateStartButton()
|
|
{
|
|
DWORD dwStyle = 0;//BS_BITMAP;
|
|
|
|
_uStartButtonBalloonTip = RegisterWindowMessage(TEXT("Welcome Finished"));
|
|
|
|
_uLogoffUser = RegisterWindowMessage(TEXT("Logoff User"));
|
|
|
|
// Register for MM device changes
|
|
_uWinMM_DeviceChange = RegisterWindowMessage(WINMMDEVICECHANGEMSGSTRING);
|
|
|
|
|
|
HWND hwnd = CreateWindowEx(0, WC_BUTTON, TEXT("Start"),
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
|
|
BS_PUSHBUTTON | BS_LEFT | BS_VCENTER | dwStyle,
|
|
0, 0, 0, 0, _hwnd, (HMENU)IDC_START, hinstCabinet, NULL);
|
|
if (hwnd)
|
|
{
|
|
// taskbar windows are themed under Taskbar subapp name
|
|
SetWindowTheme(hwnd, L"Start", NULL);
|
|
|
|
SendMessage(hwnd, CCM_DPISCALE, TRUE, 0);
|
|
|
|
// Subclass it.
|
|
_hwndStart = hwnd;
|
|
_pfnButtonProc = SubclassWindow(hwnd, StartButtonSubclassWndProc);
|
|
|
|
_StartButtonReset();
|
|
}
|
|
return hwnd;
|
|
}
|
|
|
|
void CTray::_GetWindowSizes(UINT uStuckPlace, PRECT prcClient, PRECT prcView, PRECT prcNotify)
|
|
{
|
|
prcView->top = 0;
|
|
prcView->left = 0;
|
|
prcView->bottom = prcClient->bottom;
|
|
prcView->right = prcClient->right;
|
|
|
|
if (STUCK_HORIZONTAL(uStuckPlace))
|
|
{
|
|
DWORD_PTR dwNotifySize = SendMessage(_hwndNotify, WM_CALCMINSIZE, prcClient->right / 2, prcClient->bottom);
|
|
prcNotify->top = 0;
|
|
prcNotify->left = prcClient->right - LOWORD(dwNotifySize);
|
|
prcNotify->bottom = HIWORD(dwNotifySize);
|
|
prcNotify->right = prcClient->right;
|
|
|
|
prcView->left = _sizeStart.cx + g_cxFrame + 1;
|
|
prcView->right = prcNotify->left;
|
|
}
|
|
else
|
|
{
|
|
DWORD_PTR dwNotifySize = SendMessage(_hwndNotify, WM_CALCMINSIZE, prcClient->right, prcClient->bottom / 2);
|
|
prcNotify->top = prcClient->bottom - HIWORD(dwNotifySize);
|
|
prcNotify->left = 0;
|
|
prcNotify->bottom = prcClient->bottom;
|
|
prcNotify->right = LOWORD(dwNotifySize);
|
|
|
|
prcView->top = _sizeStart.cy + g_cyTabSpace;
|
|
prcView->bottom = prcNotify->top;
|
|
}
|
|
}
|
|
|
|
void CTray::_RestoreWindowPos()
|
|
{
|
|
WINDOWPLACEMENT wp;
|
|
|
|
//first restore the stuck postitions
|
|
_GetSaveStateAndInitRects();
|
|
|
|
wp.length = sizeof(wp);
|
|
wp.showCmd = SW_HIDE;
|
|
|
|
_uMoveStuckPlace = (UINT)-1;
|
|
_GetDockedRect(&wp.rcNormalPosition, FALSE);
|
|
|
|
SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, _fHideClock);
|
|
SetWindowPlacement(_hwnd, &wp);
|
|
}
|
|
|
|
// Get the display (monitor) rectangle from the given arbitrary point
|
|
HMONITOR CTray::_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags)
|
|
{
|
|
RECT rcEmpty = {0};
|
|
HMONITOR hmon = MonitorFromPoint(pt, uFlags);
|
|
if (hmon && prcDisplay)
|
|
GetMonitorRect(hmon, prcDisplay);
|
|
else if (prcDisplay)
|
|
*prcDisplay = rcEmpty;
|
|
|
|
return hmon;
|
|
}
|
|
|
|
// Get the display (monitor) rectangle from the given arbitrary rectangle
|
|
HMONITOR CTray::_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn, UINT uFlags)
|
|
{
|
|
RECT rcEmpty = {0};
|
|
HMONITOR hmon = MonitorFromRect(prcIn, uFlags);
|
|
if (hmon && prcDisplay)
|
|
GetMonitorRect(hmon, prcDisplay);
|
|
else if (prcDisplay)
|
|
*prcDisplay = rcEmpty;
|
|
|
|
return hmon;
|
|
}
|
|
|
|
// Get the display (monitor) rectangle where the taskbar is currently on,
|
|
// if that monitor is invalid, get the nearest one.
|
|
void CTray::_GetStuckDisplayRect(UINT uStuckPlace, LPRECT prcDisplay)
|
|
{
|
|
ASSERT(prcDisplay);
|
|
BOOL fValid = GetMonitorRect(_hmonStuck, prcDisplay);
|
|
|
|
if (!fValid)
|
|
_GetDisplayRectFromRect(prcDisplay, &_arStuckRects[uStuckPlace], MONITOR_DEFAULTTONEAREST);
|
|
}
|
|
|
|
void CTray::_AdjustRectForSizingBar(UINT uStuckPlace, LPRECT prc, int iIncrement)
|
|
{
|
|
if (iIncrement != 0)
|
|
{
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_BOTTOM: prc->top -= iIncrement * _sizeSizingBar.cy; break;
|
|
case STICK_TOP: prc->bottom += iIncrement * _sizeSizingBar.cy; break;
|
|
case STICK_LEFT: prc->right += iIncrement * _sizeSizingBar.cx; break;
|
|
case STICK_RIGHT: prc->left -= iIncrement * _sizeSizingBar.cx; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IS_BIDI_LOCALIZED_SYSTEM())
|
|
{
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_BOTTOM: prc->bottom = prc->top + _sizeSizingBar.cy; break;
|
|
case STICK_TOP: prc->top = prc->bottom - _sizeSizingBar.cy; break;
|
|
case STICK_LEFT: prc->right = prc->left + _sizeSizingBar.cx; break;
|
|
case STICK_RIGHT: prc->left = prc->right - _sizeSizingBar.cx; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_BOTTOM: prc->bottom = prc->top + _sizeSizingBar.cy; break;
|
|
case STICK_TOP: prc->top = prc->bottom - _sizeSizingBar.cy; break;
|
|
case STICK_LEFT: prc->left = prc->right - _sizeSizingBar.cx; break;
|
|
case STICK_RIGHT: prc->right = prc->left + _sizeSizingBar.cx; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Snap a StuckRect to the edge of a containing rectangle
|
|
// fClip determines whether to clip the rectangle if it's off the display or move it onto the screen
|
|
void CTray::_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size, UINT uStick)
|
|
{
|
|
CopyRect(prcStick, prcBound);
|
|
|
|
if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
|
|
{
|
|
_AdjustRectForSizingBar(uStick, prcStick, 1);
|
|
}
|
|
|
|
if (!_hTheme)
|
|
{
|
|
InflateRect(prcStick, g_cxEdge, g_cyEdge);
|
|
}
|
|
|
|
if (size.cx < 0) size.cx *= -1;
|
|
if (size.cy < 0) size.cy *= -1;
|
|
|
|
switch (uStick)
|
|
{
|
|
case STICK_LEFT: prcStick->right = (prcStick->left + size.cx); break;
|
|
case STICK_TOP: prcStick->bottom = (prcStick->top + size.cy); break;
|
|
case STICK_RIGHT: prcStick->left = (prcStick->right - size.cx); break;
|
|
case STICK_BOTTOM: prcStick->top = (prcStick->bottom - size.cy); break;
|
|
}
|
|
}
|
|
|
|
// the screen size has changed, so the docked rectangles need to be
|
|
// adjusted to the new screen.
|
|
void CTray::_ResizeStuckRects(RECT *arStuckRects)
|
|
{
|
|
RECT rcDisplay;
|
|
_GetStuckDisplayRect(_uStuckPlace, &rcDisplay);
|
|
for (UINT uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
|
|
{
|
|
_MakeStuckRect(&arStuckRects[uStick], &rcDisplay, _sStuckWidths, uStick);
|
|
}
|
|
}
|
|
|
|
|
|
//*** CTray::InvisibleUnhide -- temporary 'invisible' un-autohide
|
|
// DESCRIPTION
|
|
// various tray resize routines need the tray to be un-autohide'd for
|
|
// stuff to be calculated correctly. so we un-autohide it (invisibly...)
|
|
// here. note the WM_SETREDRAW to prevent flicker (nt5:182340).
|
|
// note that this is kind of a hack -- ideally the tray code would do
|
|
// stuff correctly even if hidden.
|
|
//
|
|
void CTray::InvisibleUnhide(BOOL fShowWindow)
|
|
{
|
|
if (fShowWindow == FALSE)
|
|
{
|
|
if (_cHided++ == 0)
|
|
{
|
|
SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
|
|
ShowWindow(_hwnd, SW_HIDE);
|
|
Unhide();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(_cHided > 0); // must be push/pop
|
|
if (--_cHided == 0)
|
|
{
|
|
_Hide();
|
|
ShowWindow(_hwnd, SW_SHOWNA);
|
|
SendMessage(_hwnd, WM_SETREDRAW, TRUE, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::VerifySize(BOOL fWinIni, BOOL fRoundUp /* = FALSE */)
|
|
{
|
|
RECT rc;
|
|
BOOL fHiding;
|
|
|
|
fHiding = (_uAutoHide & AH_HIDING);
|
|
if (fHiding)
|
|
{
|
|
// force it visible so various calculations will happen relative
|
|
// to unhidden size/position.
|
|
//
|
|
// fixes (e.g.) ie5:154536, where dropping a large-icon ISFBand
|
|
// onto hidden tray didn't do size negotiation.
|
|
//
|
|
InvisibleUnhide(FALSE);
|
|
}
|
|
|
|
rc = _arStuckRects[_uStuckPlace];
|
|
_HandleSizing(0, NULL, _uStuckPlace);
|
|
|
|
if (!EqualRect(&rc, &_arStuckRects[_uStuckPlace]))
|
|
{
|
|
if (fWinIni)
|
|
{
|
|
// if we're changing size or position, we need to be unhidden
|
|
Unhide();
|
|
SizeWindows();
|
|
}
|
|
rc = _arStuckRects[_uStuckPlace];
|
|
|
|
if (EVAL((_uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING)))
|
|
{
|
|
_fSelfSizing = TRUE;
|
|
SetWindowPos(_hwnd, NULL,
|
|
rc.left, rc.top,
|
|
RECTWIDTH(rc),RECTHEIGHT(rc),
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
|
|
|
|
_fSelfSizing = FALSE;
|
|
}
|
|
|
|
_StuckTrayChange();
|
|
}
|
|
|
|
if (fWinIni)
|
|
SizeWindows();
|
|
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(TRUE);
|
|
}
|
|
}
|
|
|
|
HWND CTray::_GetClockWindow(void)
|
|
{
|
|
return (HWND)SendMessage(_hwndNotify, TNM_GETCLOCK, 0, 0L);
|
|
}
|
|
|
|
UINT _GetStartIDB()
|
|
{
|
|
UINT id;
|
|
|
|
if (IsOS(OS_TABLETPC))
|
|
{
|
|
id = IDB_TABLETPCSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_EMBEDDED))
|
|
{
|
|
if (IsOS(OS_ANYSERVER))
|
|
id = IDB_EMBEDDEDSERVER;
|
|
else
|
|
id = IDB_EMBEDDED;
|
|
}
|
|
else if (IsOS(OS_DATACENTER))
|
|
{
|
|
id = IDB_DCSERVERSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_ADVSERVER))
|
|
{
|
|
id = IDB_ADVSERVERSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_SERVER))
|
|
{
|
|
id = IDB_SERVERSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_PERSONAL))
|
|
{
|
|
id = IDB_PERSONALSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_BLADE))
|
|
{
|
|
id = IDB_BLADESTARTBKG;
|
|
}
|
|
else if (IsOS(OS_SMALLBUSINESSSERVER))
|
|
{
|
|
id = IDB_SMALLBUSINESSSTARTBKG;
|
|
}
|
|
else if (IsOS(OS_APPLIANCE))
|
|
{
|
|
id = IDB_APPLIANCESTARTBKG;
|
|
}
|
|
else
|
|
{
|
|
#ifdef _WIN64
|
|
id = IDB_PROFESSIONAL64STARTBKG;
|
|
#else
|
|
id = IDB_PROFESSIONALSTARTBKG;
|
|
#endif
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
void CTray::_CreateTrayTips()
|
|
{
|
|
_hwndTrayTips = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,
|
|
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
_hwnd, NULL, hinstCabinet,
|
|
NULL);
|
|
|
|
if (_hwndTrayTips)
|
|
{
|
|
// taskbar windows are themed under Taskbar subapp name
|
|
SendMessage(_hwndTrayTips, TTM_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskbarTheme);
|
|
|
|
SetWindowZorder(_hwndTrayTips, HWND_TOPMOST);
|
|
|
|
TOOLINFO ti;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = TTF_IDISHWND | TTF_EXCLUDETOOLAREA;
|
|
ti.hwnd = _hwnd;
|
|
ti.uId = (UINT_PTR)_hwndStart;
|
|
ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_STARTBUTTONTIP);
|
|
ti.hinst = hinstCabinet;
|
|
SendMessage(_hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
|
|
HWND hwndClock = _GetClockWindow();
|
|
if (hwndClock)
|
|
{
|
|
ti.uFlags = TTF_EXCLUDETOOLAREA;
|
|
ti.uId = (UINT_PTR)hwndClock;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
|
|
SendMessage(_hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SHCNE_STAGINGAREANOTIFICATIONS (SHCNE_CREATE | SHCNE_MKDIR | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM)
|
|
LRESULT CTray::_CreateWindows()
|
|
{
|
|
if (_CreateStartButton() && _CreateClockWindow())
|
|
{
|
|
//
|
|
// We need to set the tray position, before creating
|
|
// the view window, because it will call back our
|
|
// GetWindowRect member functions.
|
|
//
|
|
_RestoreWindowPos();
|
|
|
|
_CreateTrayTips();
|
|
|
|
SendMessage(_hwndNotify, TNM_HIDECLOCK, 0, _fHideClock);
|
|
|
|
_ptbs = BandSite_CreateView();
|
|
if (_ptbs)
|
|
{
|
|
IUnknown_GetWindow(_ptbs, &_hwndRebar);
|
|
SetWindowStyle(_hwndRebar, RBS_BANDBORDERS, FALSE);
|
|
|
|
// No need to check the disk space thing for non-privileged users, this reduces activity in the TS case
|
|
// and only admins can properly free disk space anyways.
|
|
if (IsUserAnAdmin() && !SHRestricted(REST_NOLOWDISKSPACECHECKS))
|
|
{
|
|
SetTimer(_hwnd, IDT_CHECKDISKSPACE, 60 * 1000, NULL); // 60 seconds poll
|
|
}
|
|
|
|
if (IsOS(OS_PERSONAL) || IsOS(OS_PROFESSIONAL))
|
|
{
|
|
SetTimer(_hwnd, IDT_DESKTOPCLEANUP, 24 * 60 * 60 * 1000, NULL); // 24 hour poll
|
|
}
|
|
|
|
if (!SHRestricted(REST_NOCDBURNING))
|
|
{
|
|
LPITEMIDLIST pidlStaging;
|
|
if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA | CSIDL_FLAG_CREATE, NULL, 0, &pidlStaging)))
|
|
{
|
|
SHChangeNotifyEntry fsne;
|
|
fsne.fRecursive = FALSE;
|
|
fsne.pidl = pidlStaging;
|
|
_uNotify = SHChangeNotifyRegister(_hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
|
|
SHCNE_STAGINGAREANOTIFICATIONS, TM_CHANGENOTIFY, 1, &fsne);
|
|
|
|
// start off by checking the first time.
|
|
_CheckStagingAreaOnTimer();
|
|
|
|
ILFree(pidlStaging);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
LRESULT CTray::_InitStartButtonEtc()
|
|
{
|
|
// NOTE: This bitmap is used as a flag in CTaskBar::OnPosRectChangeDB to
|
|
// tell when we are done initializing, so we don't resize prematurely
|
|
_hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(_GetStartIDB()));
|
|
|
|
if (_hbmpStartBkg)
|
|
{
|
|
UpdateWindow(_hwnd);
|
|
_BuildStartMenu();
|
|
_RegisterDropTargets();
|
|
|
|
if (_CheckAssociations())
|
|
CheckWinIniForAssocs();
|
|
|
|
SendNotifyMessage(HWND_BROADCAST,
|
|
RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void CTray::_AdjustMinimizedMetrics()
|
|
{
|
|
MINIMIZEDMETRICS mm;
|
|
|
|
mm.cbSize = sizeof(mm);
|
|
SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
|
|
mm.iArrange |= ARW_HIDE;
|
|
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
|
|
}
|
|
|
|
void CTray::_UpdateBandSiteStyle()
|
|
{
|
|
if (_ptbs)
|
|
{
|
|
BANDSITEINFO bsi;
|
|
bsi.dwMask = BSIM_STYLE;
|
|
_ptbs->GetBandSiteInfo(&bsi);
|
|
|
|
BOOL fCanMoveBands = _fCanSizeMove && !SHRestricted(REST_NOMOVINGBAND);
|
|
|
|
DWORD dwStyleNew;
|
|
if (fCanMoveBands)
|
|
{
|
|
dwStyleNew = (bsi.dwStyle & ~(BSIS_NOGRIPPER | BSIS_LOCKED)) | BSIS_AUTOGRIPPER
|
|
| BSIS_PREFERNOLINEBREAK;
|
|
}
|
|
else
|
|
{
|
|
dwStyleNew = (bsi.dwStyle & ~BSIS_AUTOGRIPPER) | BSIS_NOGRIPPER | BSIS_LOCKED
|
|
| BSIS_PREFERNOLINEBREAK;
|
|
}
|
|
|
|
// only bother with refresh if something's changed
|
|
if (bsi.dwStyle ^ dwStyleNew)
|
|
{
|
|
bsi.dwStyle = dwStyleNew;
|
|
_ptbs->SetBandSiteInfo(&bsi);
|
|
IUnknown_Exec(_ptbs, &CGID_DeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL _IsSizeMoveRestricted()
|
|
{
|
|
return SHRegGetBoolUSValue(REGSTR_POLICIES_EXPLORER, TEXT("LockTaskbar"), FALSE, FALSE);
|
|
}
|
|
|
|
BOOL _IsSizeMoveEnabled()
|
|
{
|
|
BOOL fCanSizeMove;
|
|
|
|
if (_IsSizeMoveRestricted())
|
|
{
|
|
fCanSizeMove = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fCanSizeMove = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarSizeMove"), FALSE, TRUE);
|
|
}
|
|
|
|
return fCanSizeMove;
|
|
}
|
|
|
|
void CTray::_RefreshSettings()
|
|
{
|
|
BOOL fOldCanSizeMove = _fCanSizeMove;
|
|
_fCanSizeMove = _IsSizeMoveEnabled();
|
|
BOOL fOldShowSizingBarAlways = _fShowSizingBarAlways;
|
|
_fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
|
|
|
|
if ((fOldCanSizeMove != _fCanSizeMove) || (_fShowSizingBarAlways != fOldShowSizingBarAlways))
|
|
{
|
|
BOOL fHiding = (_uAutoHide & AH_HIDING);
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(FALSE);
|
|
}
|
|
|
|
RECT rc;
|
|
GetWindowRect(_hwnd, &rc);
|
|
|
|
if (_hTheme && !_fShowSizingBarAlways)
|
|
{
|
|
if (_fCanSizeMove)
|
|
{
|
|
_AdjustRectForSizingBar(_uStuckPlace, &rc, 1);
|
|
}
|
|
else
|
|
{
|
|
_AdjustRectForSizingBar(_uStuckPlace, &rc, -1);
|
|
}
|
|
}
|
|
|
|
_ClipWindow(FALSE);
|
|
_fSelfSizing = TRUE;
|
|
SetWindowPos(_hwnd, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
_fSelfSizing = FALSE;
|
|
_ClipWindow(TRUE);
|
|
|
|
_arStuckRects[_uStuckPlace] = rc;
|
|
_StuckTrayChange();
|
|
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(TRUE);
|
|
}
|
|
|
|
if (!_fCanSizeMove)
|
|
{
|
|
SetWindowPos(_hwnd, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER);
|
|
}
|
|
}
|
|
}
|
|
|
|
LRESULT CTray::_OnCreateAsync()
|
|
{
|
|
LRESULT lres;
|
|
|
|
if (g_dwProfileCAP & 0x00000004)
|
|
{
|
|
StartCAP();
|
|
}
|
|
|
|
lres = _InitStartButtonEtc();
|
|
|
|
if (g_dwProfileCAP & 0x00000004)
|
|
{
|
|
StopCAP();
|
|
}
|
|
|
|
_hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_TRAY));
|
|
|
|
_RegisterGlobalHotkeys();
|
|
|
|
// We spin a thread that will process "Load=", "Run=", CU\Run, and CU\RunOnce
|
|
RunStartupApps();
|
|
|
|
// If there were any startup failures that occurred before we were
|
|
// ready to handle them, re-raise the failure now that we're ready.
|
|
if (_fEarlyStartupFailure)
|
|
LogFailedStartupApp();
|
|
|
|
// we run the tray thread that handles Ctrl-Esc with a high priority
|
|
// class so that it can respond even on a stressed system.
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
|
return lres;
|
|
}
|
|
|
|
LRESULT CTray::_OnCreate(HWND hwnd)
|
|
{
|
|
LRESULT lres = -1;
|
|
v_hwndTray = hwnd;
|
|
|
|
Mixer_SetCallbackWindow(hwnd);
|
|
SendMessage(_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
|
|
|
|
_AdjustMinimizedMetrics();
|
|
|
|
_hTheme = OpenThemeData(hwnd, c_wzTaskbarTheme);
|
|
|
|
_fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
|
|
if (_hTheme)
|
|
{
|
|
GetThemeBool(_hTheme, 0, 0, TMT_ALWAYSSHOWSIZINGBAR, &_fShowSizingBarAlways);
|
|
}
|
|
|
|
SetWindowStyle(_hwnd, WS_BORDER | WS_THICKFRAME, !_hTheme);
|
|
|
|
// Force Refresh of frame
|
|
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
|
|
|
|
if (_HotkeyCreate())
|
|
{
|
|
lres = _CreateWindows();
|
|
}
|
|
return lres;
|
|
}
|
|
|
|
typedef struct tagFSEPDATA
|
|
{
|
|
LPRECT prc;
|
|
HMONITOR hmon;
|
|
CTray* ptray;
|
|
}
|
|
FSEPDATA, *PFSEPDATA;
|
|
|
|
BOOL WINAPI CTray::FullScreenEnumProc(HMONITOR hmon, HDC hdc, LPRECT prc, LPARAM dwData)
|
|
{
|
|
BOOL fFullScreen; // Is there a rude app on this monitor?
|
|
|
|
PFSEPDATA pd = (PFSEPDATA)dwData;
|
|
if (pd->hmon == hmon)
|
|
{
|
|
fFullScreen = TRUE;
|
|
}
|
|
else if (pd->prc)
|
|
{
|
|
RECT rc, rcMon;
|
|
GetMonitorRect(hmon, &rcMon);
|
|
IntersectRect(&rc, &rcMon, pd->prc);
|
|
fFullScreen = EqualRect(&rc, &rcMon);
|
|
}
|
|
else
|
|
{
|
|
fFullScreen = FALSE;
|
|
}
|
|
|
|
if (hmon == pd->ptray->_hmonStuck)
|
|
{
|
|
pd->ptray->_fStuckRudeApp = fFullScreen;
|
|
}
|
|
|
|
//
|
|
// Tell all the appbars on the same display to get out of the way too
|
|
//
|
|
pd->ptray->_AppBarNotifyAll(hmon, ABN_FULLSCREENAPP, NULL, fFullScreen);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::HandleFullScreenApp(HWND hwnd)
|
|
{
|
|
//
|
|
// First check to see if something has actually changed
|
|
//
|
|
_hwndRude = hwnd;
|
|
|
|
//
|
|
// Enumerate all the monitors, see if the app is rude on each, adjust
|
|
// app bars and _fStuckRudeApp as necessary. (Some rude apps, such
|
|
// as the NT Logon Screen Saver, span multiple monitors.)
|
|
//
|
|
FSEPDATA d = {0};
|
|
RECT rc;
|
|
if (hwnd && GetWindowRect(hwnd, &rc))
|
|
{
|
|
d.prc = &rc;
|
|
d.hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
|
|
}
|
|
d.ptray = this;
|
|
|
|
EnumDisplayMonitors(NULL, NULL, FullScreenEnumProc, (LPARAM)&d);
|
|
|
|
//
|
|
// Now that we've set _fStuckRudeApp, update the tray's z-order position
|
|
//
|
|
_ResetZorder();
|
|
|
|
//
|
|
// stop the clock so we don't eat cycles and keep tons of code paged in
|
|
//
|
|
SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, _fStuckRudeApp);
|
|
|
|
//
|
|
// Finally, let traynot know about whether the tray is hiding
|
|
//
|
|
SendMessage(_hwndNotify, TNM_RUDEAPP, _fStuckRudeApp, 0);
|
|
}
|
|
|
|
BOOL CTray::_IsTopmost()
|
|
{
|
|
return BOOLIFY(GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST);
|
|
}
|
|
|
|
BOOL CTray::_IsPopupMenuVisible()
|
|
{
|
|
HWND hwnd;
|
|
return ((SUCCEEDED(IUnknown_GetWindow(_pmpStartMenu, &hwnd)) && IsWindowVisible(hwnd)) ||
|
|
(SUCCEEDED(IUnknown_GetWindow(_pmpStartPane, &hwnd)) && IsWindowVisible(hwnd)) ||
|
|
(SUCCEEDED(IUnknown_GetWindow(_pmpTasks, &hwnd)) && IsWindowVisible(hwnd)));
|
|
}
|
|
|
|
BOOL CTray::_IsActive()
|
|
{
|
|
//
|
|
// We say the tray is "active" iff:
|
|
//
|
|
// (a) the foreground window is the tray or a window owned by the tray, or
|
|
// (b) the start menu is showing
|
|
//
|
|
|
|
BOOL fActive = FALSE;
|
|
HWND hwnd = GetForegroundWindow();
|
|
|
|
if (hwnd != NULL &&
|
|
(hwnd == _hwnd || (GetWindowOwner(hwnd) == _hwnd)))
|
|
{
|
|
fActive = TRUE;
|
|
}
|
|
else if (_IsPopupMenuVisible())
|
|
{
|
|
fActive = TRUE;
|
|
}
|
|
|
|
return fActive;
|
|
}
|
|
|
|
void CTray::_ResetZorder()
|
|
{
|
|
HWND hwndZorder, hwndZorderCurrent;
|
|
|
|
if (g_fDesktopRaised || _fProcessingDesktopRaise || (_fAlwaysOnTop && !_fStuckRudeApp))
|
|
{
|
|
hwndZorder = HWND_TOPMOST;
|
|
}
|
|
else if (_IsActive())
|
|
{
|
|
hwndZorder = HWND_TOP;
|
|
}
|
|
else if (_fStuckRudeApp)
|
|
{
|
|
hwndZorder = HWND_BOTTOM;
|
|
}
|
|
else
|
|
{
|
|
hwndZorder = HWND_NOTOPMOST;
|
|
}
|
|
|
|
//
|
|
// We don't have to worry about the HWND_BOTTOM current case -- it's ok
|
|
// to keep moving ourselves down to the bottom when there's a rude app.
|
|
//
|
|
// Nor do we have to worry about the HWND_TOP current case -- it's ok
|
|
// to keep moving ourselves up to the top when we're active.
|
|
//
|
|
hwndZorderCurrent = _IsTopmost() ? HWND_TOPMOST : HWND_NOTOPMOST;
|
|
|
|
if (hwndZorder != hwndZorderCurrent)
|
|
{
|
|
// only do this if somehting has changed.
|
|
// this keeps us from popping up over menus as desktop async
|
|
// notifies us of it's state
|
|
|
|
SHForceWindowZorder(_hwnd, hwndZorder);
|
|
}
|
|
}
|
|
|
|
void CTray::_MessageLoop()
|
|
{
|
|
for (;;)
|
|
{
|
|
MSG msg;
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
if (_hwnd && IsWindow(_hwnd))
|
|
{
|
|
// Tell the tray to save everything off if we got here
|
|
// without it being destroyed.
|
|
SendMessage(_hwnd, WM_ENDSESSION, 1, 0);
|
|
}
|
|
return; // break all the way out of the main loop
|
|
}
|
|
|
|
if (_pmbTasks)
|
|
{
|
|
HRESULT hr = _pmbTasks->IsMenuMessage(&msg);
|
|
if (hr == E_FAIL)
|
|
{
|
|
if (_hwndTasks)
|
|
SendMessage(_hwndTasks, TBC_FREEPOPUPMENUS, 0, 0);
|
|
}
|
|
else if (hr == S_OK)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Note that this needs to come before _pmbStartMenu since
|
|
// the start pane sometimes hosts the start menu and it needs
|
|
// to handle the start menu messages in that case.
|
|
if (_pmbStartPane &&
|
|
_pmbStartPane->IsMenuMessage(&msg) == S_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_pmbStartMenu &&
|
|
_pmbStartMenu->IsMenuMessage(&msg) == S_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_hMainAccel && TranslateAccelerator(_hwnd, _hMainAccel, &msg))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
WaitMessage();
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CTray::Init()
|
|
{
|
|
// use _COINIT to make sure COM is inited disabling the OLE1 support
|
|
return SHCreateThread(MainThreadProc, this, CTF_COINIT, SyncThreadProc) && (_hwnd != NULL);
|
|
}
|
|
|
|
int CTray::_GetPart(BOOL fSizingBar, UINT uStuckPlace)
|
|
{
|
|
if (fSizingBar)
|
|
{
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_BOTTOM: return TBP_SIZINGBARBOTTOM;
|
|
case STICK_LEFT: return TBP_SIZINGBARLEFT;
|
|
case STICK_TOP: return TBP_SIZINGBARTOP;
|
|
case STICK_RIGHT: return TBP_SIZINGBARRIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_BOTTOM: return TBP_BACKGROUNDBOTTOM;
|
|
case STICK_LEFT: return TBP_BACKGROUNDLEFT;
|
|
case STICK_TOP: return TBP_BACKGROUNDTOP;
|
|
case STICK_RIGHT: return TBP_BACKGROUNDRIGHT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CTray::_UpdateVertical(UINT uStuckPlace, BOOL fForce)
|
|
{
|
|
static UINT _uOldStuckPlace = STICK_MAX + 1;
|
|
|
|
if ((_uOldStuckPlace != uStuckPlace) || fForce)
|
|
{
|
|
_uOldStuckPlace = uStuckPlace;
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_uv tray is now %s"), STUCK_HORIZONTAL(uStuckPlace) ? TEXT("HORIZONTAL") : TEXT("VERTICAL"));
|
|
|
|
if (_ptbs)
|
|
{
|
|
// This following function will cause a WINDOWPOSCHANGING which will call DoneMoving
|
|
// DoneMoving will then go a screw up all of the window sizing
|
|
_fIgnoreDoneMoving = TRUE;
|
|
BandSite_SetMode(_ptbs, STUCK_HORIZONTAL(uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
|
|
BandSite_SetWindowTheme(_ptbs, (LPWSTR)(STUCK_HORIZONTAL(uStuckPlace) ? c_wzTaskbarTheme : c_wzTaskbarVertTheme));
|
|
_fIgnoreDoneMoving = FALSE;
|
|
}
|
|
|
|
SendMessage(_hwndNotify, TNM_UPDATEVERTICAL, 0, !STUCK_HORIZONTAL(uStuckPlace));
|
|
|
|
if (_hTheme)
|
|
{
|
|
HDC hdc = GetDC(_hwnd);
|
|
GetThemePartSize(_hTheme, hdc, _GetPart(TRUE, uStuckPlace), 0, NULL, TS_TRUE, &_sizeSizingBar);
|
|
ReleaseDC(_hwnd, hdc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_InitBandsite()
|
|
{
|
|
ASSERT(_hwnd);
|
|
|
|
// we initilize the contents after all the infrastructure is created and sized properly
|
|
// need to notify which side we're on.
|
|
// nt5:211881: set mode *before* load, o.w. Update->RBAutoSize messed up
|
|
_UpdateBandSiteStyle();
|
|
|
|
BandSite_Load();
|
|
// now that the mode is set, we need to force an update because we
|
|
// explicitly avoided the update during BandSite_Load
|
|
_UpdateVertical(_uStuckPlace, TRUE);
|
|
BandSite_Update(_ptbs);
|
|
BandSite_UIActivateDBC(_ptbs, DBC_SHOW);
|
|
|
|
BandSite_FindBand(_ptbs, CLSID_TaskBand, IID_PPV_ARG(IDeskBand, &_pdbTasks), NULL, NULL);
|
|
IUnknown_GetWindow(_pdbTasks, &_hwndTasks);
|
|
|
|
// Now that bandsite is ready, set the correct size
|
|
VerifySize(FALSE, TRUE);
|
|
}
|
|
|
|
void CTray::_KickStartAutohide()
|
|
{
|
|
if (_uAutoHide & AH_ON)
|
|
{
|
|
// tray always starts out hidden on autohide
|
|
_uAutoHide = AH_ON | AH_HIDING;
|
|
|
|
// we and many apps rely upon us having calculated the size correctly
|
|
Unhide();
|
|
|
|
// register it
|
|
if (!_AppBarSetAutoHideBar2(_hwnd, TRUE, _uStuckPlace))
|
|
{
|
|
// don't bother putting up UI in this case
|
|
// if someone is there just silently convert to normal
|
|
// (the shell is booting who would be there anyway?)
|
|
_SetAutoHideState(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_InitNonzeroGlobals()
|
|
{
|
|
// initalize globals that need to be non-zero
|
|
|
|
if (GetSystemMetrics(SM_SLOWMACHINE))
|
|
{
|
|
_dtSlideHide = 0; // dont slide the tray out
|
|
_dtSlideShow = 0;
|
|
}
|
|
else
|
|
{
|
|
_dtSlideHide = 400;
|
|
_dtSlideShow = 200;
|
|
}
|
|
|
|
_RefreshSettings();
|
|
}
|
|
|
|
void CTray::_CreateTrayWindow()
|
|
{
|
|
_InitTrayClass();
|
|
|
|
_uMsgEnableUserTrackedBalloonTips = RegisterWindowMessage(ENABLE_BALLOONTIP_MESSAGE);
|
|
|
|
_fNoToolbarsOnTaskbarPolicyEnabled = (SHRestricted(REST_NOTOOLBARSONTASKBAR) != 0);
|
|
|
|
DWORD dwExStyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW;
|
|
// Don't fadein because layered windows suck
|
|
// If you create a layered window on a non-active desktop then the window goes black
|
|
dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
|
|
|
|
CreateWindowEx(dwExStyle, TEXT(WNDCLASS_TRAYNOTIFY), NULL,
|
|
WS_CLIPCHILDREN | WS_POPUP,
|
|
0, 0, 0, 0, NULL, NULL, hinstCabinet, (void*)this);
|
|
|
|
}
|
|
|
|
DWORD WINAPI CTray::SyncThreadProc(void *pv)
|
|
{
|
|
CTray* ptray = (CTray*)pv;
|
|
return ptray->_SyncThreadProc();
|
|
}
|
|
|
|
DWORD CTray::_SyncThreadProc()
|
|
{
|
|
if (g_dwStopWatchMode)
|
|
StopWatch_StartTimed(SWID_STARTUP, TEXT("_SyncThreadProc"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
|
|
|
|
if (g_dwProfileCAP & 0x00000002)
|
|
StartCAP();
|
|
|
|
|
|
InitializeCriticalSection(&_csHotkey);
|
|
|
|
OleInitialize(NULL); // matched in MainThreadProc()
|
|
ClassFactory_Start();
|
|
|
|
_InitNonzeroGlobals();
|
|
_ssomgr.Init();
|
|
|
|
//
|
|
// Watch the registry key that tells us which app is the default
|
|
// web browser, so we can track it in
|
|
// HKLM\Software\Clients\StartMenuInternet. We have to track it
|
|
// ourselves because downlevel browsers won't know about it.
|
|
//
|
|
// We need to do this only if we have write access to the key.
|
|
// (If we don't have write access, then we can't change it,
|
|
// so there's no point watching for it to change...)
|
|
//
|
|
// Well, okay, even if we only have read access, we have to do
|
|
// it once in case it changed while we were logged off.
|
|
//
|
|
// The order of these operations is important...
|
|
//
|
|
// 1. Migrate browser settings.
|
|
// 2. Build default MFU. (Depends on browser settings.)
|
|
// 3. Create tray window. (Relies on value MFU.)
|
|
//
|
|
|
|
_hHTTPEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
|
|
if (_hHTTPEvent)
|
|
{
|
|
// Make one migration pass immediately so HandleFirstTime
|
|
// sees good information. This also kick-starts the
|
|
// registry change notification process if the current user
|
|
// has write permission.
|
|
_MigrateOldBrowserSettings();
|
|
|
|
if (RegisterWaitForSingleObject(&_hHTTPWait, _hHTTPEvent,
|
|
_MigrateOldBrowserSettingsCB, this,
|
|
INFINITE, WT_EXECUTEDEFAULT))
|
|
{
|
|
// Yay, everything is fine.
|
|
}
|
|
}
|
|
|
|
// Build the default MFU if necessary
|
|
HandleFirstTime();
|
|
|
|
_CreateTrayWindow();
|
|
|
|
if (_hwnd && _ptbs)
|
|
{
|
|
_ResetZorder(); // obey the "always on top" flag
|
|
_KickStartAutohide();
|
|
|
|
_InitBandsite();
|
|
|
|
_ClipWindow(TRUE); // make sure we clip the taskbar to the current monitor before showing it
|
|
|
|
// it looks really strange for the tray to pop up and rehide at logon
|
|
// if we are autohide don't activate the tray when we show it
|
|
// if we aren't autohide do what Win95 did (tray is active by default)
|
|
ShowWindow(_hwnd, (_uAutoHide & AH_HIDING) ? SW_SHOWNA : SW_SHOW);
|
|
|
|
UpdateWindow(_hwnd);
|
|
_StuckTrayChange();
|
|
|
|
// get the system background scheduler thread
|
|
IShellTaskScheduler* pScheduler;
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC,
|
|
IID_PPV_ARG(IShellTaskScheduler, &pScheduler))))
|
|
{
|
|
AddMenuItemsCacheTask(pScheduler, Tray_StartPanelEnabled());
|
|
pScheduler->Release();
|
|
}
|
|
|
|
SetTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF, 5 * 1000, NULL);
|
|
}
|
|
|
|
if (g_dwProfileCAP & 0x00020000)
|
|
StopCAP();
|
|
|
|
if (g_dwStopWatchMode)
|
|
StopWatch_StopTimed(SWID_STARTUP, TEXT("_SyncThreadProc"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// the rest of the thread proc that includes the message loop
|
|
DWORD WINAPI CTray::MainThreadProc(void *pv)
|
|
{
|
|
CTray* ptray = (CTray*)pv;
|
|
|
|
if (!ptray->_hwnd)
|
|
return FALSE;
|
|
|
|
ptray->_OnCreateAsync();
|
|
|
|
PERFSETMARK("ExplorerStartMenuReady");
|
|
|
|
ptray->_MessageLoop();
|
|
|
|
ClassFactory_Stop();
|
|
OleUninitialize(); // matched in _SyncThreadProc()
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define DM_IANELHK 0
|
|
|
|
#define HKIF_NULL 0
|
|
#define HKIF_CACHED 1
|
|
#define HKIF_FREEPIDLS 2
|
|
|
|
typedef struct
|
|
{
|
|
LPITEMIDLIST pidlFolder;
|
|
LPITEMIDLIST pidlItem;
|
|
WORD wGHotkey;
|
|
WORD wFlags;
|
|
} HOTKEYITEM;
|
|
|
|
|
|
UINT CTray::_HotkeyGetFreeItemIndex(void)
|
|
{
|
|
int i, cItems;
|
|
HOTKEYITEM *phki;
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
cItems = DSA_GetItemCount(_hdsaHKI);
|
|
for (i=0; i<cItems; i++)
|
|
{
|
|
phki = (HOTKEYITEM *)DSA_GetItemPtr(_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 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;
|
|
UINT 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 CTray::HotkeyAdd(WORD wHotkey, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fClone)
|
|
{
|
|
if (wHotkey)
|
|
{
|
|
LPCITEMIDLIST pidl1, pidl2;
|
|
|
|
HOTKEYITEM hki;
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
int i = _HotkeyGetFreeItemIndex();
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
// DebugMsg(DM_IANELHK, "c.hl_a: Hotkey %x with id %d.", wHotkey, i);
|
|
|
|
if (fClone)
|
|
{
|
|
pidl1 = ILClone(pidlFolder);
|
|
pidl2 = ILClone(pidlItem);
|
|
hki.wFlags = HKIF_FREEPIDLS;
|
|
}
|
|
else
|
|
{
|
|
pidl1 = pidlFolder;
|
|
pidl2 = pidlItem;
|
|
hki.wFlags = HKIF_NULL;
|
|
}
|
|
|
|
hki.pidlFolder = (LPITEMIDLIST)pidl1;
|
|
hki.pidlItem = (LPITEMIDLIST)pidl2;
|
|
hki.wGHotkey = _MapHotkeyToGlobalHotkey(wHotkey);
|
|
DSA_SetItem(_hdsaHKI, i, &hki);
|
|
|
|
LeaveCriticalSection(&_csHotkey);
|
|
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 CTray::_HotkeyAddCached(WORD wGHotkey, LPITEMIDLIST pidl)
|
|
{
|
|
int i = -1;
|
|
|
|
if (wGHotkey)
|
|
{
|
|
LPITEMIDLIST pidlItem = ILClone(ILFindLastID(pidl));
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
if (pidlItem)
|
|
{
|
|
if (ILRemoveLastID(pidl))
|
|
{
|
|
HOTKEYITEM hki;
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
i = _HotkeyGetFreeItemIndex();
|
|
|
|
// 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(_hdsaHKI, i, &hki);
|
|
|
|
LeaveCriticalSection(&_csHotkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// NB Again, this takes window hotkey not a Global one.
|
|
// NB This doesn't delete cached hotkeys.
|
|
int CTray::_HotkeyRemove(WORD wHotkey)
|
|
{
|
|
int iRet = -1;
|
|
if (EVAL(_hdsaHKI))
|
|
{
|
|
int i, cItems;
|
|
HOTKEYITEM *phki;
|
|
WORD wGHotkey;
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
// DebugMsg(DM_IANELHK, "c.hl_r: Remove hotkey for %x" , wHotkey);
|
|
|
|
// Unmap the modifiers.
|
|
wGHotkey = _MapHotkeyToGlobalHotkey(wHotkey);
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
cItems = DSA_GetItemCount(_hdsaHKI);
|
|
for (i=0; i<cItems; i++)
|
|
{
|
|
phki = (HOTKEYITEM *)DSA_GetItemPtr(_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;
|
|
iRet = i;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_csHotkey);
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
// NB This takes a global hotkey.
|
|
int CTray::_HotkeyRemoveCached(WORD wGHotkey)
|
|
{
|
|
int iRet = -1;
|
|
int i, cItems;
|
|
HOTKEYITEM *phki;
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
// DebugMsg(DM_IANELHK, "c.hl_rc: Remove hotkey for %x" , wGHotkey);
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
cItems = DSA_GetItemCount(_hdsaHKI);
|
|
for (i=0; i<cItems; i++)
|
|
{
|
|
phki = (HOTKEYITEM *)DSA_GetItemPtr(_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);
|
|
iRet = i;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_csHotkey);
|
|
|
|
return iRet;
|
|
}
|
|
|
|
// 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 CTray::_HotkeyCreate(void)
|
|
{
|
|
if (!_hdsaHKI)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.hkl_c: Creating global hotkey list.");
|
|
_hdsaHKI = DSA_Create(sizeof(HOTKEYITEM), 0);
|
|
}
|
|
|
|
if (_hdsaHKI)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CTray::_BuildStartMenu()
|
|
{
|
|
HRESULT hr;
|
|
|
|
ClosePopupMenus();
|
|
|
|
//
|
|
// Avoid redundant rebuilds: Peek out any pending SBM_REBUILDMENU messages
|
|
// since the rebuild we're about to do will take care of it. Do this
|
|
// before destroying the Start Menu so we never yield while there isn't
|
|
// a Start Menu.
|
|
//
|
|
MSG msg;
|
|
while (PeekMessage(&msg, _hwnd, SBM_REBUILDMENU, SBM_REBUILDMENU, PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
// Keep sucking them out
|
|
}
|
|
|
|
|
|
_DestroyStartMenu();
|
|
|
|
if (Tray_StartPanelEnabled())
|
|
{
|
|
hr = DesktopV2_Create(&_pmpStartPane, &_pmbStartPane, &_pvStartPane);
|
|
DesktopV2_Build(_pvStartPane);
|
|
}
|
|
else
|
|
{
|
|
hr = StartMenuHost_Create(&_pmpStartMenu, &_pmbStartMenu);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IBanneredBar* pbb;
|
|
|
|
hr = _pmpStartMenu->QueryInterface(IID_PPV_ARG(IBanneredBar, &pbb));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pbb->SetBitmap(_hbmpStartBkg);
|
|
if (_fSMSmallIcons)
|
|
pbb->SetIconSize(BMICON_SMALL);
|
|
else
|
|
pbb->SetIconSize(BMICON_LARGE);
|
|
|
|
pbb->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg(TF_ERROR, "Could not create StartMenu");
|
|
}
|
|
}
|
|
|
|
void CTray::_DestroyStartMenu()
|
|
{
|
|
IUnknown_SetSite(_pmpStartMenu, NULL);
|
|
ATOMICRELEASET(_pmpStartMenu, IMenuPopup);
|
|
ATOMICRELEASET(_pmbStartMenu, IMenuBand);
|
|
IUnknown_SetSite(_pmpStartPane, NULL);
|
|
ATOMICRELEASET(_pmpStartPane, IMenuPopup);
|
|
ATOMICRELEASET(_pmbStartPane, IMenuBand);
|
|
ATOMICRELEASET(_pmpTasks, IMenuPopup);
|
|
ATOMICRELEASET(_pmbTasks, IMenuBand);
|
|
}
|
|
|
|
void CTray::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, _hwndStart, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
|
|
SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
|
|
SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
|
|
|
|
if (_hwndTasks)
|
|
SendMessage(_hwndTasks, TBC_SETPREVFOCUS, 0, NULL);
|
|
|
|
PostMessage(_hwnd, TM_STARTMENUDISMISSED, 0, 0);
|
|
}
|
|
|
|
void Tray_OnStartMenuDismissed()
|
|
{
|
|
c_tray._bMainMenuInit = FALSE;
|
|
// Tell the Start Button that it's allowed to be in the up position now. This
|
|
// prevents the problem where the start menu is displayed but the button is
|
|
// in the up position... This happens when dialog boxes are displayed
|
|
c_tray._fAllowUp = TRUE;
|
|
|
|
// Now tell it to be in the up position
|
|
c_tray.ForceStartButtonUp();
|
|
|
|
PostMessage(v_hwndTray, TM_SHOWTRAYBALLOON, TRUE, 0);
|
|
}
|
|
|
|
int CTray::_TrackMenu(HMENU hmenu)
|
|
{
|
|
TPMPARAMS tpm;
|
|
int iret;
|
|
|
|
tpm.cbSize = sizeof(tpm);
|
|
GetClientRect(_hwndStart, &tpm.rcExclude);
|
|
|
|
RECT rcClient;
|
|
GetClientRect(_hwnd, &rcClient);
|
|
tpm.rcExclude.bottom = min(tpm.rcExclude.bottom, rcClient.bottom);
|
|
|
|
MapWindowPoints(_hwndStart, NULL, (LPPOINT)&tpm.rcExclude, 2);
|
|
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
|
|
iret = TrackPopupMenuEx(hmenu, TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_RETURNCMD,
|
|
tpm.rcExclude.left, tpm.rcExclude.bottom, _hwnd, &tpm);
|
|
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
|
|
return iret;
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
** Respond to a button's pressing by bringing up the appropriate menu.
|
|
** Clean up the button depression when the menu is dismissed.
|
|
**------------------------------------------------------------------*/
|
|
void CTray::_ToolbarMenu()
|
|
{
|
|
RECTL rcExclude;
|
|
POINTL ptPop;
|
|
DWORD dwFlags = MPPF_KEYBOARD; // Assume that we're popuping
|
|
// up because of the keyboard
|
|
// This is for the underlines on NT5
|
|
|
|
if (_hwndTasks)
|
|
SendMessage(_hwndTasks, TBC_FREEPOPUPMENUS, 0, 0);
|
|
|
|
if (_hwndStartBalloon)
|
|
{
|
|
_DontShowTheStartButtonBalloonAnyMore();
|
|
ShowWindow(_hwndStartBalloon, SW_HIDE);
|
|
_DestroyStartButtonBalloon();
|
|
}
|
|
|
|
SetActiveWindow(_hwnd);
|
|
_bMainMenuInit = TRUE;
|
|
|
|
// Exclude rect is the VISIBLE portion of the Start Button.
|
|
{
|
|
RECT rcParent;
|
|
GetClientRect(_hwndStart, (RECT *)&rcExclude);
|
|
MapWindowRect(_hwndStart, HWND_DESKTOP, &rcExclude);
|
|
|
|
GetClientRect(_hwnd, &rcParent);
|
|
MapWindowRect(_hwnd, HWND_DESKTOP, &rcParent);
|
|
|
|
IntersectRect((RECT*)&rcExclude, (RECT*)&rcExclude, &rcParent);
|
|
}
|
|
ptPop.x = rcExclude.left;
|
|
ptPop.y = rcExclude.top;
|
|
|
|
// Close any Context Menus
|
|
SendMessage(_hwnd, WM_CANCELMODE, 0, 0);
|
|
|
|
// Is the "Activate" button down (If the buttons are swapped, then it's the
|
|
// right button, otherwise the left button)
|
|
if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON)?VK_RBUTTON:VK_LBUTTON) < 0)
|
|
{
|
|
dwFlags = 0; // Then set to the default
|
|
}
|
|
|
|
IMenuPopup **ppmpToDisplay = &_pmpStartMenu;
|
|
|
|
if (_pmpStartPane)
|
|
{
|
|
ppmpToDisplay = &_pmpStartPane;
|
|
}
|
|
|
|
// Close race window: The user can click on the Start Button
|
|
// before we get a chance to rebuild the Start Menu in its new
|
|
// form. In such case, rebuild it now.
|
|
if (!*ppmpToDisplay)
|
|
{
|
|
TraceMsg(TF_WARNING, "e.tbm: Rebuilding Start Menu");
|
|
_BuildStartMenu();
|
|
}
|
|
|
|
|
|
if (*ppmpToDisplay && SUCCEEDED((*ppmpToDisplay)->Popup(&ptPop, &rcExclude, dwFlags)))
|
|
{
|
|
// All is well - the menu is up
|
|
TraceMsg(DM_MISC, "e.tbm: dwFlags=%x (0=mouse 1=key)", dwFlags);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_WARNING, "e.tbm: %08x->Popup failed", *ppmpToDisplay);
|
|
// Start Menu failed to display -- reset the Start Button
|
|
// so the user can click it again to try again
|
|
Tray_OnStartMenuDismissed();
|
|
}
|
|
|
|
if (dwFlags == MPPF_KEYBOARD)
|
|
{
|
|
// Since the user has launched the start button by Ctrl-Esc, or some other worldly
|
|
// means, then turn the rect on.
|
|
SendMessage(_hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
|
|
UISF_HIDEFOCUS), 0);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CTray::_AppBarSetState(UINT uFlags)
|
|
{
|
|
if (uFlags & ~(ABS_AUTOHIDE | ABS_ALWAYSONTOP))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
_SetAutoHideState(uFlags & ABS_AUTOHIDE);
|
|
_UpdateAlwaysOnTop(uFlags & ABS_ALWAYSONTOP);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// can't use SubtractRect sometimes because of inclusion limitations
|
|
//
|
|
void CTray::_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 CTray::_AppBarSubtractRects(HMONITOR hmon, LPRECT lprc)
|
|
{
|
|
int i;
|
|
|
|
if (!_hdpaAppBars)
|
|
return;
|
|
|
|
i = DPA_GetPtrCount(_hdpaAppBars);
|
|
|
|
while (i--)
|
|
{
|
|
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
|
|
|
|
//
|
|
// autohide bars are not in our DPA or live on the edge
|
|
// don't subtract the appbar if it's on a different display
|
|
// don't subtract the appbar if we are in a locked desktop
|
|
//
|
|
// if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL))
|
|
if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL) && !_fIsDesktopLocked)
|
|
_AppBarSubtractRect(pab, lprc);
|
|
}
|
|
}
|
|
|
|
#define RWA_NOCHANGE 0
|
|
#define RWA_CHANGED 1
|
|
#define RWA_BOTTOMMOSTTRAY 2
|
|
|
|
// (dli) This is a hack put in because bottommost tray is wierd, once
|
|
// it becomes a toolbar, this code should go away.
|
|
// In the bottommost tray case, even though the work area has not changed,
|
|
// we should notify the desktop.
|
|
int CTray::_RecomputeWorkArea(HWND hwndCause, HMONITOR hmon, LPRECT prcWork)
|
|
{
|
|
int iRet = RWA_NOCHANGE;
|
|
MONITORINFO mi;
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
if (_fIsLogoff)
|
|
{
|
|
if (GetMonitorInfo(hmon, &mi))
|
|
{
|
|
*prcWork = mi.rcMonitor;
|
|
iRet = RWA_CHANGED;
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
ASSERT(!_fIsLogoff);
|
|
|
|
//
|
|
// tell everybody that this window changed positions _on_this_monitor_
|
|
// note that this notify happens even if we don't change the work area
|
|
// since it may cause another app to change the work area...
|
|
//
|
|
PostMessage(_hwnd, TM_RELAYPOSCHANGED, (WPARAM)hwndCause, (LPARAM)hmon);
|
|
|
|
//
|
|
// get the current info for this monitor
|
|
// we subtract down from the display rectangle to build the work area
|
|
//
|
|
if (GetMonitorInfo(hmon, &mi))
|
|
{
|
|
//
|
|
// don't subtract the tray if it is autohide
|
|
// don't subtract the tray if it is not always on top
|
|
// don't subtract the tray if it's on a different display
|
|
// don't subtract the tray if it is on a different desktop
|
|
//
|
|
if (!(_uAutoHide & AH_ON) && _fAlwaysOnTop &&
|
|
(hmon == _hmonStuck) && !_fIsDesktopLocked)
|
|
{
|
|
SubtractRect(prcWork, &mi.rcMonitor,
|
|
&_arStuckRects[_uStuckPlace]);
|
|
}
|
|
else
|
|
*prcWork = mi.rcMonitor;
|
|
|
|
//
|
|
// now subtract off all the appbars on this display
|
|
//
|
|
_AppBarSubtractRects(hmon, prcWork);
|
|
|
|
//
|
|
// return whether we changed anything
|
|
//
|
|
if (!EqualRect(prcWork, &mi.rcWork))
|
|
iRet = RWA_CHANGED;
|
|
else if (!(_uAutoHide & AH_ON) && (!_fAlwaysOnTop) &&
|
|
(!IsRectEmpty(&_arStuckRects[_uStuckPlace])))
|
|
// NOTE: This is the bottommost case, it only applies for the tray.
|
|
// this should be taken out when bottommost tray becomes toolbar
|
|
iRet = RWA_BOTTOMMOSTTRAY;
|
|
}
|
|
else
|
|
{
|
|
iRet = RWA_NOCHANGE;
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
void RedrawDesktop(RECT *prcWork)
|
|
{
|
|
// This rect point should always be valid (dli)
|
|
RIP(prcWork);
|
|
|
|
if (v_hwndDesktop && g_fCleanBoot)
|
|
{
|
|
MapWindowRect(NULL, v_hwndDesktop, prcWork);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac invalidating desktop rect {%d,%d,%d,%d}"), prcWork->left, prcWork->top, prcWork->right, prcWork->bottom);
|
|
RedrawWindow(v_hwndDesktop, prcWork, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
|
|
}
|
|
}
|
|
|
|
void CTray::_StuckAppChange(HWND hwndCause, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray)
|
|
{
|
|
RECT rcWork1, rcWork2;
|
|
HMONITOR hmon1, hmon2 = 0;
|
|
int iChange = 0;
|
|
|
|
//
|
|
// PERF FEATURE:
|
|
// there are cases where we end up setting the work area multiple times
|
|
// we need to keep a static array of displays that have changed and a
|
|
// reenter count so we can avoid pain of sending notifies to the whole
|
|
// planet...
|
|
//
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac from_AppBar %08X"), hwndCause);
|
|
|
|
//
|
|
// see if the work area changed on the display containing prcOld
|
|
//
|
|
if (prcOld)
|
|
{
|
|
if (bTray)
|
|
hmon1 = _hmonOld;
|
|
else
|
|
hmon1 = MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac old pos {%d,%d,%d,%d} on monitor %08X"), prcOld->left, prcOld->top, prcOld->right, prcOld->bottom, hmon1);
|
|
|
|
if (hmon1)
|
|
{
|
|
int iret = _RecomputeWorkArea(hwndCause, hmon1, &rcWork1);
|
|
if (iret == RWA_CHANGED)
|
|
iChange = 1;
|
|
if (iret == RWA_BOTTOMMOSTTRAY)
|
|
iChange = 4;
|
|
}
|
|
}
|
|
else
|
|
hmon1 = NULL;
|
|
|
|
//
|
|
// see if the work area changed on the display containing prcNew
|
|
//
|
|
if (prcNew)
|
|
{
|
|
hmon2 = MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac new pos {%d,%d,%d,%d} on monitor %08X"), prcNew->left, prcNew->top, prcNew->right, prcNew->bottom, hmon2);
|
|
|
|
if (hmon2 && (hmon2 != hmon1))
|
|
{
|
|
int iret = _RecomputeWorkArea(hwndCause, hmon2, &rcWork2);
|
|
if (iret == RWA_CHANGED)
|
|
iChange |= 2;
|
|
else if (iret == RWA_BOTTOMMOSTTRAY && (!iChange))
|
|
iChange = 4;
|
|
}
|
|
}
|
|
|
|
//
|
|
// did the prcOld's display's work area change?
|
|
//
|
|
if (iChange & 1)
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon1);
|
|
|
|
// only send SENDWININICHANGE if the desktop has been created (otherwise
|
|
// we will hang the explorer because the main thread is currently blocked)
|
|
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork1,
|
|
(iChange == 1 && v_hwndDesktop)? SPIF_SENDWININICHANGE : 0);
|
|
|
|
RedrawDesktop(&rcWork1);
|
|
}
|
|
|
|
//
|
|
// did the prcOld's display's work area change?
|
|
//
|
|
if (iChange & 2)
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon2);
|
|
|
|
// only send SENDWININICHANGE if the desktop has been created (otherwise
|
|
// we will hang the explorer because the main thread is currently blocked)
|
|
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork2,
|
|
v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
|
|
|
|
RedrawDesktop(&rcWork2);
|
|
}
|
|
|
|
// only send if the desktop has been created...
|
|
// need to send if it's from the tray or any outside app that causes size change
|
|
// from the tray because autohideness will affect desktop size even if it's not always on top
|
|
if ((bTray || iChange == 4) && v_hwndDesktop)
|
|
SendMessage(v_hwndDesktop, WM_SIZE, 0, 0);
|
|
}
|
|
|
|
void CTray::_StuckTrayChange()
|
|
{
|
|
// We used to blow off the _StuckAppChange when the tray was in autohide
|
|
// mode, since moving or resizing an autohid tray doesn't change the
|
|
// work area. Now we go ahead with the _StuckAppChange in this case
|
|
// too. The reason is that we can get into a state where the work area
|
|
// size is incorrect, and we want the taskbar to always be self-repairing
|
|
// in this case (so that resizing or moving the taskbar will correct the
|
|
// work area size).
|
|
|
|
//
|
|
// pass a NULL window here since we don't want to hand out our window and
|
|
// the tray doesn't get these anyway (nobody cares as long as its not them)
|
|
//
|
|
_StuckAppChange(NULL, &_rcOldTray,
|
|
&_arStuckRects[_uStuckPlace], TRUE);
|
|
|
|
//
|
|
// save off the new tray position...
|
|
//
|
|
_rcOldTray = _arStuckRects[_uStuckPlace];
|
|
}
|
|
|
|
UINT CTray::_RecalcStuckPos(LPRECT prc)
|
|
{
|
|
RECT rcDummy;
|
|
POINT pt;
|
|
|
|
if (!prc)
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp no rect supplied, using window rect"));
|
|
|
|
prc = &rcDummy;
|
|
GetWindowRect(_hwnd, prc);
|
|
}
|
|
|
|
// use the center of the original drag rect as a staring point
|
|
pt.x = prc->left + RECTWIDTH(*prc) / 2;
|
|
pt.y = prc->top + RECTHEIGHT(*prc) / 2;
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp rect is {%d, %d, %d, %d} point is {%d, %d}"), prc->left, prc->top, prc->right, prc->bottom, pt.x, pt.y);
|
|
|
|
// reset this so the drag code won't give it preference
|
|
_uMoveStuckPlace = (UINT)-1;
|
|
|
|
// simulate a drag back to figure out where we originated from
|
|
// you may be tempted to remove this. before you do think about dragging
|
|
// the tray across monitors and then hitting ESC...
|
|
return _CalcDragPlace(pt);
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
** the position is changing in response to a move operation.
|
|
**
|
|
** if the docking status changed, we need to get a new size and
|
|
** maybe a new frame style. change the WINDOWPOS to reflect
|
|
** these changes accordingly.
|
|
**------------------------------------------------------------------*/
|
|
void CTray::_DoneMoving(LPWINDOWPOS lpwp)
|
|
{
|
|
RECT rc, *prc;
|
|
|
|
if ((_uMoveStuckPlace == (UINT)-1) || (_fIgnoreDoneMoving))
|
|
return;
|
|
|
|
if (_fSysSizing)
|
|
_fDeferedPosRectChange = TRUE;
|
|
|
|
rc.left = lpwp->x;
|
|
rc.top = lpwp->y;
|
|
rc.right = lpwp->x + lpwp->cx;
|
|
rc.bottom = lpwp->y + lpwp->cy;
|
|
|
|
prc = &_arStuckRects[_uMoveStuckPlace];
|
|
|
|
if (!EqualRect(prc, &rc))
|
|
{
|
|
_uMoveStuckPlace = _RecalcStuckPos(&rc);
|
|
prc = &_arStuckRects[_uMoveStuckPlace];
|
|
}
|
|
|
|
// Get the new hmonitor
|
|
_hmonStuck = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
|
|
|
|
lpwp->x = prc->left;
|
|
lpwp->y = prc->top;
|
|
lpwp->cx = RECTWIDTH(*prc);
|
|
lpwp->cy = RECTHEIGHT(*prc);
|
|
|
|
lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
// if we were autohiding, we need to update our appbar autohide rect
|
|
if (_uAutoHide & AH_ON)
|
|
{
|
|
// unregister us from the old side
|
|
_AppBarSetAutoHideBar2(_hwnd, FALSE, _uStuckPlace);
|
|
}
|
|
|
|
// All that work might've changed _uMoveStuckPlace (since there
|
|
// was a lot of message traffic), so check one more time.
|
|
// Somehow, NT Stress manages to get us in here with an invalid
|
|
// uMoveStuckPlace.
|
|
if (IsValidSTUCKPLACE(_uMoveStuckPlace))
|
|
{
|
|
// remember the new state
|
|
_uStuckPlace = _uMoveStuckPlace;
|
|
}
|
|
_uMoveStuckPlace = (UINT)-1;
|
|
_UpdateVertical(_uStuckPlace);
|
|
|
|
_HandleSizing(0, prc, _uStuckPlace);
|
|
if ((_uAutoHide & AH_ON) &&
|
|
!_AppBarSetAutoHideBar2(_hwnd, TRUE, _uStuckPlace))
|
|
{
|
|
_AutoHideCollision();
|
|
}
|
|
}
|
|
|
|
UINT CTray::_CalcDragPlace(POINT pt)
|
|
{
|
|
UINT uPlace = _uMoveStuckPlace;
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp starting point is {%d, %d}"), pt.x, pt.y);
|
|
|
|
//
|
|
// if the mouse is currently over the tray position leave it alone
|
|
//
|
|
if ((uPlace == (UINT)-1) || !PtInRect(&_arStuckRects[uPlace], pt))
|
|
{
|
|
HMONITOR hmonDrag;
|
|
SIZE screen, error;
|
|
UINT uHorzEdge, uVertEdge;
|
|
RECT rcDisplay, *prcStick;
|
|
|
|
//
|
|
// which display is the mouse on?
|
|
//
|
|
hmonDrag = _GetDisplayRectFromPoint(&rcDisplay, pt,
|
|
MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp monitor is %08X"), hmonDrag);
|
|
|
|
//
|
|
// re-origin at zero to make calculations simpler
|
|
//
|
|
screen.cx = RECTWIDTH(rcDisplay);
|
|
screen.cy = RECTHEIGHT(rcDisplay);
|
|
pt.x -= rcDisplay.left;
|
|
pt.y -= rcDisplay.top;
|
|
|
|
//
|
|
// are we closer to the left or right side of this display?
|
|
//
|
|
if (pt.x < (screen.cx / 2))
|
|
{
|
|
uVertEdge = STICK_LEFT;
|
|
error.cx = pt.x;
|
|
}
|
|
else
|
|
{
|
|
uVertEdge = STICK_RIGHT;
|
|
error.cx = screen.cx - pt.x;
|
|
}
|
|
|
|
//
|
|
// are we closer to the top or bottom side of this display?
|
|
//
|
|
if (pt.y < (screen.cy / 2))
|
|
{
|
|
uHorzEdge = STICK_TOP;
|
|
error.cy = pt.y;
|
|
}
|
|
else
|
|
{
|
|
uHorzEdge = STICK_BOTTOM;
|
|
error.cy = screen.cy - pt.y;
|
|
}
|
|
|
|
//
|
|
// closer to a horizontal or vertical edge?
|
|
//
|
|
uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))?
|
|
uVertEdge : uHorzEdge;
|
|
|
|
// which StuckRect should we use?
|
|
prcStick = &_arStuckRects[uPlace];
|
|
|
|
//
|
|
// need to recalc stuck rect for new monitor?
|
|
//
|
|
if ((hmonDrag != _GetDisplayRectFromRect(NULL, prcStick,
|
|
MONITOR_DEFAULTTONULL)))
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp re-snapping rect for new display"));
|
|
_MakeStuckRect(prcStick, &rcDisplay, _sStuckWidths, uPlace);
|
|
}
|
|
}
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp edge is %d, rect is {%d, %d, %d, %d}"), uPlace, _arStuckRects[uPlace].left, _arStuckRects[uPlace].top, _arStuckRects[uPlace].right, _arStuckRects[uPlace].bottom);
|
|
ASSERT(IsValidSTUCKPLACE(uPlace));
|
|
return uPlace;
|
|
}
|
|
|
|
void CTray::_HandleMoving(WPARAM wParam, LPRECT lprc)
|
|
{
|
|
POINT ptCursor;
|
|
GetCursorPos(&ptCursor);
|
|
|
|
// If the cursor is not far from its starting point, then ignore it.
|
|
// A very common problem is the user clicks near the corner of the clock,
|
|
// twitches the mouse 5 pixels, and BLAM, their taskbar is now vertical
|
|
// and they don't know what they did or how to get it back.
|
|
|
|
if (g_fInSizeMove && PtInRect(&_rcSizeMoveIgnore, ptCursor))
|
|
{
|
|
// Ignore -- user is merely twitching
|
|
_uMoveStuckPlace = _uStuckPlace;
|
|
}
|
|
else
|
|
{
|
|
_uMoveStuckPlace = _CalcDragPlace(ptCursor);
|
|
}
|
|
|
|
*lprc = _arStuckRects[_uMoveStuckPlace];
|
|
_HandleSizing(wParam, lprc, _uMoveStuckPlace);
|
|
}
|
|
|
|
// store the tray size when dragging is finished
|
|
void CTray::_SnapshotStuckRectSize(UINT uPlace)
|
|
{
|
|
RECT rcDisplay, *prc = &_arStuckRects[uPlace];
|
|
|
|
//
|
|
// record the width of this stuck rect
|
|
//
|
|
if (STUCK_HORIZONTAL(uPlace))
|
|
_sStuckWidths.cy = RECTHEIGHT(*prc);
|
|
else
|
|
_sStuckWidths.cx = RECTWIDTH(*prc);
|
|
|
|
//
|
|
// we only present a horizontal or vertical size to the end user
|
|
// so update the StuckRect on the other side of the screen to match
|
|
//
|
|
_GetStuckDisplayRect(uPlace, &rcDisplay);
|
|
|
|
uPlace += 2;
|
|
uPlace %= 4;
|
|
prc = &_arStuckRects[uPlace];
|
|
|
|
_MakeStuckRect(prc, &rcDisplay, _sStuckWidths, uPlace);
|
|
}
|
|
|
|
|
|
// Size the icon area to fill as much of the tray window as it can.
|
|
|
|
void CTray::SizeWindows()
|
|
{
|
|
RECT rcView, rcNotify, rcClient;
|
|
int fHiding;
|
|
|
|
if (!_hwndRebar || !_hwnd || !_hwndNotify)
|
|
return;
|
|
|
|
fHiding = (_uAutoHide & AH_HIDING);
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(FALSE);
|
|
}
|
|
|
|
// remember our current size
|
|
_SnapshotStuckRectSize(_uStuckPlace);
|
|
|
|
GetClientRect(_hwnd, &rcClient);
|
|
_AlignStartButton();
|
|
|
|
_GetWindowSizes(_uStuckPlace, &rcClient, &rcView, &rcNotify);
|
|
|
|
InvalidateRect(_hwndStart, NULL, TRUE);
|
|
InvalidateRect(_hwnd, NULL, TRUE);
|
|
|
|
// position the view
|
|
SetWindowPos(_hwndRebar, NULL, rcView.left, rcView.top,
|
|
RECTWIDTH(rcView), RECTHEIGHT(rcView),
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
|
|
UpdateWindow(_hwndRebar);
|
|
|
|
// And the clock
|
|
SetWindowPos(_hwndNotify, NULL, rcNotify.left, rcNotify.top,
|
|
RECTWIDTH(rcNotify), RECTHEIGHT(rcNotify),
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
|
|
|
|
{
|
|
TOOLINFO ti;
|
|
HWND hwndClock = _GetClockWindow();
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = 0;
|
|
ti.hwnd = _hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
ti.uId = (UINT_PTR)hwndClock;
|
|
GetWindowRect(hwndClock, &ti.rect);
|
|
MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&ti.rect, 2);
|
|
SendMessage(_hwndTrayTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
}
|
|
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
void CTray::_HandleSize()
|
|
{
|
|
//
|
|
// if somehow we got minimized go ahead and un-minimize
|
|
//
|
|
if (((GetWindowLong(_hwnd, GWL_STYLE)) & WS_MINIMIZE))
|
|
{
|
|
ASSERT(FALSE);
|
|
ShowWindow(_hwnd, SW_RESTORE);
|
|
}
|
|
|
|
//
|
|
// if we are in the move/size loop and are visible then
|
|
// re-snap the current stuck rect to the new window size
|
|
//
|
|
#ifdef DEBUG
|
|
if (_fSysSizing && (_uAutoHide & AH_HIDING)) {
|
|
TraceMsg(DM_TRACE, "fSysSize && hiding");
|
|
ASSERT(0);
|
|
}
|
|
#endif
|
|
if (_fSysSizing &&
|
|
((_uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING)))
|
|
{
|
|
_uStuckPlace = _RecalcStuckPos(NULL);
|
|
_UpdateVertical(_uStuckPlace);
|
|
}
|
|
|
|
//
|
|
// if we are in fulldrag or we are not in the middle of a move/size then
|
|
// we should resize all our child windows to reflect our new size
|
|
//
|
|
if (g_fDragFullWindows || !_fSysSizing)
|
|
SizeWindows();
|
|
|
|
//
|
|
// if we are just plain resized and we are visible we may need re-dock
|
|
//
|
|
if (!_fSysSizing && !_fSelfSizing && IsWindowVisible(_hwnd))
|
|
{
|
|
if (_uAutoHide & AH_ON)
|
|
{
|
|
UINT uPlace = _uStuckPlace;
|
|
HWND hwndOther =_AppBarGetAutoHideBar(uPlace);
|
|
|
|
//
|
|
// we sometimes defer checking for this until after a move
|
|
// so as to avoid interrupting a full-window-drag in progress
|
|
// if there is a different autohide window in our slot then whimper
|
|
//
|
|
if (hwndOther?
|
|
(hwndOther != _hwnd) :
|
|
!_AppBarSetAutoHideBar2(_hwnd, TRUE, uPlace))
|
|
{
|
|
_AutoHideCollision();
|
|
}
|
|
}
|
|
|
|
_StuckTrayChange();
|
|
|
|
//
|
|
// make sure we clip to tray to the current monitor (if necessary)
|
|
//
|
|
_ClipWindow(TRUE);
|
|
}
|
|
|
|
if (_hwndStartBalloon)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(_hwndStart, &rc);
|
|
SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
|
|
SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
|
|
}
|
|
}
|
|
|
|
BOOL _IsSliverHeight(int cy)
|
|
{
|
|
//
|
|
// Is this height clearly bigger than the pure-border height that you
|
|
// get when you resize the taskbar as small as it will go?
|
|
//
|
|
return (cy < (3 * (g_cyDlgFrame + g_cyBorder)));
|
|
}
|
|
|
|
BOOL CTray::_HandleSizing(WPARAM code, LPRECT lprc, UINT uStuckPlace)
|
|
{
|
|
BOOL fChangedSize = FALSE;
|
|
RECT rcDisplay;
|
|
SIZE sNewWidths;
|
|
RECT rcTemp;
|
|
BOOL fHiding;
|
|
|
|
if (!lprc)
|
|
{
|
|
rcTemp = _arStuckRects[uStuckPlace];
|
|
lprc = &rcTemp;
|
|
}
|
|
|
|
fHiding = (_uAutoHide & AH_HIDING);
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(FALSE);
|
|
}
|
|
|
|
//
|
|
// get the a bunch of relevant dimensions
|
|
//
|
|
// (dli) need to change this funciton or get rid of it
|
|
_GetDisplayRectFromRect(&rcDisplay, lprc, MONITOR_DEFAULTTONEAREST);
|
|
|
|
if (code)
|
|
{
|
|
// if code != 0, this is the user sizing.
|
|
// make sure they clip it to the screen.
|
|
RECT rcMax = rcDisplay;
|
|
if (!_hTheme)
|
|
{
|
|
InflateRect(&rcMax, g_cxEdge, g_cyEdge);
|
|
}
|
|
// don't do intersect rect because of sizing up from the bottom
|
|
// (when taskbar docked on bottom) confuses people
|
|
switch (uStuckPlace)
|
|
{
|
|
case STICK_LEFT:
|
|
lprc->left = rcMax.left;
|
|
break;
|
|
|
|
case STICK_TOP:
|
|
lprc->top = rcMax.top;
|
|
break;
|
|
|
|
case STICK_RIGHT:
|
|
lprc->right = rcMax.right;
|
|
break;
|
|
|
|
case STICK_BOTTOM:
|
|
lprc->top += (rcMax.bottom-lprc->bottom);
|
|
lprc->bottom = rcMax.bottom;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// compute the new widths
|
|
// don't let either be more than half the screen
|
|
//
|
|
sNewWidths.cx = min(RECTWIDTH(*lprc), RECTWIDTH(rcDisplay) / 2);
|
|
sNewWidths.cy = min(RECTHEIGHT(*lprc), RECTHEIGHT(rcDisplay) / 2);
|
|
|
|
if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
|
|
{
|
|
sNewWidths.cy = max(_sizeSizingBar.cy, sNewWidths.cy);
|
|
}
|
|
|
|
//
|
|
// compute an initial size
|
|
//
|
|
_MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
|
|
|
|
//
|
|
// negotiate the exact size with our children
|
|
//
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs tray is being calculated for %s"), STUCK_HORIZONTAL(uStuckPlace) ? TEXT("HORIZONTAL") : TEXT("VERTICAL"));
|
|
|
|
_UpdateVertical(uStuckPlace);
|
|
if (_ptbs)
|
|
{
|
|
IDeskBarClient* pdbc;
|
|
if (SUCCEEDED(_ptbs->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pdbc))))
|
|
{
|
|
RECT rcClient = *lprc;
|
|
RECT rcOldClient = _arStuckRects[uStuckPlace];
|
|
|
|
// Go from a Window Rect to Client Rect
|
|
if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
|
|
{
|
|
_AdjustRectForSizingBar(uStuckPlace, &rcClient, -1);
|
|
_AdjustRectForSizingBar(uStuckPlace, &rcOldClient, -1);
|
|
}
|
|
else if (!_hTheme)
|
|
{
|
|
InflateRect(&rcClient, -g_cxFrame, -g_cyFrame);
|
|
InflateRect(&rcOldClient, -g_cxFrame, -g_cyFrame);
|
|
}
|
|
// Make rcClient start at 0,0, Rebar only used the right and bottom values of this rect
|
|
OffsetRect(&rcClient, -rcClient.left, -rcClient.top);
|
|
OffsetRect(&rcOldClient, -rcOldClient.left, -rcOldClient.top);
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting client rect is {%d, %d, %d, %d}"), rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
|
|
|
|
RECT rcNotify;
|
|
RECT rcView;
|
|
RECT rcOldView;
|
|
// Go from the taskbar's client rect to the rebar's client rect
|
|
_GetWindowSizes(uStuckPlace, &rcClient, &rcView, &rcNotify);
|
|
_GetWindowSizes(uStuckPlace, &rcOldClient, &rcOldView, &rcNotify);
|
|
// Make rcView start at 0,0, Rebar only used the right and bottom values of this rect
|
|
OffsetRect(&rcView, -rcView.left, -rcView.top);
|
|
OffsetRect(&rcOldView, -rcOldView.left, -rcOldView.top);
|
|
if (!_fCanSizeMove || (RECTHEIGHT(rcView) && RECTWIDTH(rcView)))
|
|
{
|
|
// This following function will cause a WINDOWPOSCHAGING which will call DoneMoving
|
|
// DoneMoving will then go a screw up all of the window sizing
|
|
_fIgnoreDoneMoving = TRUE;
|
|
pdbc->GetSize(DBC_GS_SIZEDOWN, &rcView);
|
|
_fIgnoreDoneMoving = FALSE;
|
|
}
|
|
|
|
// Go from a Client Rect to Window Rect
|
|
if (STUCK_HORIZONTAL(uStuckPlace))
|
|
{
|
|
rcClient.top = rcView.top;
|
|
rcClient.bottom = rcView.bottom;
|
|
}
|
|
else
|
|
{
|
|
rcClient.left = rcView.left;
|
|
rcClient.right = rcView.right;
|
|
}
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs ending client rect is {%d, %d, %d, %d}"), rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
|
|
if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
|
|
{
|
|
_AdjustRectForSizingBar(uStuckPlace, &rcClient, 1);
|
|
_AdjustRectForSizingBar(uStuckPlace, &rcOldClient, 1);
|
|
}
|
|
else if (!_hTheme)
|
|
{
|
|
InflateRect(&rcClient, g_cxFrame, g_cyFrame);
|
|
InflateRect(&rcOldClient, g_cxFrame, g_cyFrame);
|
|
}
|
|
|
|
// Prevent huge growth of taskbar, caused by bugs in the rebar sizing code
|
|
if (RECTHEIGHT(rcView) && RECTHEIGHT(rcOldView) && (RECTHEIGHT(rcClient) > (3 * RECTHEIGHT(rcOldClient))))
|
|
{
|
|
rcClient = rcOldClient;
|
|
}
|
|
|
|
if (STUCK_HORIZONTAL(uStuckPlace) && sNewWidths.cy != RECTHEIGHT(rcClient))
|
|
{
|
|
sNewWidths.cy = RECTHEIGHT(rcClient);
|
|
fChangedSize = TRUE;
|
|
}
|
|
if (!STUCK_HORIZONTAL(uStuckPlace) && sNewWidths.cx != RECTWIDTH(rcClient))
|
|
{
|
|
sNewWidths.cx = RECTWIDTH(rcClient);
|
|
fChangedSize = TRUE;
|
|
}
|
|
|
|
pdbc->Release();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// was there a change?
|
|
//
|
|
if (fChangedSize)
|
|
{
|
|
//
|
|
// yes, update the final rectangle
|
|
//
|
|
_MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
|
|
}
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs final rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
|
|
|
|
//
|
|
// store the new size in the appropriate StuckRect
|
|
//
|
|
_arStuckRects[uStuckPlace] = *lprc;
|
|
|
|
if (fHiding)
|
|
{
|
|
InvisibleUnhide(TRUE);
|
|
}
|
|
|
|
if (_hwndStartBalloon)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(_hwndStart, &rc);
|
|
SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
|
|
SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
|
|
}
|
|
|
|
return fChangedSize;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
** 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.
|
|
**-------------------------------------------------------------------*/
|
|
|
|
BOOL WINAPI CTray::MonitorEnumProc(HMONITOR hMonitor, HDC hdc, LPRECT lprc, LPARAM lData)
|
|
{
|
|
CTray* ptray = (CTray*)lData;
|
|
|
|
RECT rcWork;
|
|
int iRet = ptray->_RecomputeWorkArea(NULL, hMonitor, &rcWork);
|
|
|
|
if (iRet == RWA_CHANGED)
|
|
{
|
|
// only send SENDWININICHANGE if the desktop has been created (otherwise
|
|
// we will hang the explorer because the main thread is currently blocked)
|
|
|
|
// PERF FEATURE: it will be nice to send WININICHANGE only once, but we can't
|
|
// because each time the rcWork is different, and there is no way to do it all
|
|
SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork, v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
|
|
RedrawDesktop(&rcWork);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::_RecomputeAllWorkareas()
|
|
{
|
|
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)this);
|
|
}
|
|
|
|
void CTray::_ScreenSizeChange(HWND hwnd)
|
|
{
|
|
// Set our new HMONITOR in case there is some change
|
|
{
|
|
MONITORINFO mi = {0};
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
// Is our old HMONITOR still valid?
|
|
// NOTE: This test is used to tell whether somethings happened to the
|
|
// HMONITOR's or just the screen size changed
|
|
if (!GetMonitorInfo(_hmonStuck, &mi))
|
|
{
|
|
// No, this means the HMONITORS changed, our monitor might have gone away
|
|
_SetStuckMonitor();
|
|
_fIsLogoff = FALSE;
|
|
_RecomputeAllWorkareas();
|
|
}
|
|
}
|
|
|
|
// screen size changed, so we need to adjust globals
|
|
g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
|
|
g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
_ResizeStuckRects(_arStuckRects);
|
|
|
|
if (hwnd)
|
|
{
|
|
//
|
|
// set a bogus windowpos and actually repaint with the right
|
|
// shape/size in handling the WINDOWPOSCHANGING message
|
|
//
|
|
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
|
|
SizeWindows();
|
|
|
|
RECT rc = _arStuckRects[_uStuckPlace];
|
|
_HandleSizing(0, &rc, _uStuckPlace);
|
|
// In the multi-monitor case, we need to turn on clipping for the dynamic
|
|
// monitor changes i.e. when the user add monitors or remove them from the
|
|
// control panel.
|
|
_ClipWindow(TRUE);
|
|
}
|
|
|
|
BOOL CTray::_UpdateAlwaysOnTop(BOOL fAlwaysOnTop)
|
|
{
|
|
BOOL fChanged = ((_fAlwaysOnTop == 0) != (fAlwaysOnTop == 0));
|
|
//
|
|
// The user clicked on the AlwaysOnTop menu item, we should now toggle
|
|
// the state and update the window accordingly...
|
|
//
|
|
_fAlwaysOnTop = fAlwaysOnTop;
|
|
_ResetZorder();
|
|
|
|
// Make sure the screen limits are update to the new state.
|
|
_StuckTrayChange();
|
|
return fChanged;
|
|
}
|
|
|
|
|
|
|
|
void CTray::_SaveTrayAndDesktop(void)
|
|
{
|
|
_SaveTray();
|
|
|
|
if (v_hwndDesktop)
|
|
SendMessage(v_hwndDesktop, DTM_SAVESTATE, 0, 0);
|
|
|
|
if (_hwndNotify)
|
|
SendMessage(_hwndNotify, TNM_SAVESTATE, 0, 0);
|
|
}
|
|
|
|
void CTray::_SlideStep(HWND hwnd, const RECT *prcMonitor, const RECT *prcOld, const RECT *prcNew)
|
|
{
|
|
SIZE sizeOld = {prcOld->right - prcOld->left, prcOld->bottom - prcOld->top};
|
|
SIZE sizeNew = {prcNew->right - prcNew->left, prcNew->bottom - prcNew->top};
|
|
BOOL fClipFirst = FALSE;
|
|
UINT flags;
|
|
|
|
DAD_ShowDragImage(FALSE); // Make sure this is off - client function must turn back on!!!
|
|
if (prcMonitor)
|
|
{
|
|
RECT rcClip, rcClipSafe, rcClipTest;
|
|
|
|
_CalcClipCoords(&rcClip, prcMonitor, prcNew);
|
|
|
|
rcClipTest = rcClip;
|
|
|
|
OffsetRect(&rcClipTest, prcOld->left, prcOld->top);
|
|
IntersectRect(&rcClipSafe, &rcClipTest, prcMonitor);
|
|
|
|
fClipFirst = EqualRect(&rcClipTest, &rcClipSafe);
|
|
|
|
if (fClipFirst)
|
|
_ClipInternal(&rcClip);
|
|
}
|
|
|
|
flags = SWP_NOZORDER|SWP_NOACTIVATE;
|
|
if ((sizeOld.cx == sizeNew.cx) && (sizeOld.cy == sizeNew.cy))
|
|
flags |= SWP_NOSIZE;
|
|
|
|
SetWindowPos(hwnd, NULL,
|
|
prcNew->left, prcNew->top, sizeNew.cx, sizeNew.cy, flags);
|
|
|
|
if (prcMonitor && !fClipFirst)
|
|
{
|
|
RECT rcClip;
|
|
|
|
_CalcClipCoords(&rcClip, prcMonitor, prcNew);
|
|
_ClipInternal(&rcClip);
|
|
}
|
|
}
|
|
|
|
void CTray::_SlideWindow(HWND hwnd, RECT *prc, BOOL fShow)
|
|
{
|
|
RECT rcLast;
|
|
RECT rcMonitor;
|
|
const RECT *prcMonitor;
|
|
DWORD dt;
|
|
BOOL fAnimate;
|
|
|
|
if (!IsWindowVisible(hwnd))
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw window is hidden, just moving"));
|
|
MoveWindow(_hwnd, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc), FALSE);
|
|
return;
|
|
}
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw -----------------BEGIN-----------------"));
|
|
|
|
if (GetSystemMetrics(SM_CMONITORS) > 1)
|
|
{
|
|
_GetStuckDisplayRect(_uStuckPlace, &rcMonitor);
|
|
prcMonitor = &rcMonitor;
|
|
}
|
|
else
|
|
prcMonitor = NULL;
|
|
|
|
GetWindowRect(hwnd, &rcLast);
|
|
|
|
dt = fShow? _dtSlideShow : _dtSlideHide;
|
|
|
|
// See if we can use animation effects.
|
|
SystemParametersInfo(SPI_GETMENUANIMATION, 0, &fAnimate, 0);
|
|
|
|
if (g_fDragFullWindows && fAnimate && (dt > 0))
|
|
{
|
|
RECT rcOld, rcNew, rcMove;
|
|
int dx, dy, priority;
|
|
DWORD t, t2, t0;
|
|
HANDLE me;
|
|
|
|
rcOld = rcLast;
|
|
rcNew = *prc;
|
|
|
|
dx = ((rcNew.left + rcNew.right) - (rcOld.left + rcOld.right)) / 2;
|
|
dy = ((rcNew.top + rcNew.bottom) - (rcOld.top + rcOld.bottom)) / 2;
|
|
ASSERT(dx == 0 || dy == 0);
|
|
|
|
me = GetCurrentThread();
|
|
priority = GetThreadPriority(me);
|
|
SetThreadPriority(me, THREAD_PRIORITY_HIGHEST);
|
|
|
|
t2 = t0 = GetTickCount();
|
|
|
|
rcMove = rcOld;
|
|
while ((t = GetTickCount()) - t0 < dt)
|
|
{
|
|
int dtdiff;
|
|
if (t != t2)
|
|
{
|
|
rcMove.right -= rcMove.left;
|
|
rcMove.left = rcOld.left + (dx) * (t - t0) / dt;
|
|
rcMove.right += rcMove.left;
|
|
|
|
rcMove.bottom -= rcMove.top;
|
|
rcMove.top = rcOld.top + (dy) * (t - t0) / dt;
|
|
rcMove.bottom += rcMove.top;
|
|
|
|
_SlideStep(hwnd, prcMonitor, &rcLast, &rcMove);
|
|
|
|
if (fShow)
|
|
UpdateWindow(hwnd);
|
|
|
|
rcLast = rcMove;
|
|
t2 = t;
|
|
}
|
|
|
|
// don't draw frames faster than user can see, e.g. 20ms
|
|
#define ONEFRAME 20
|
|
dtdiff = GetTickCount();
|
|
if ((dtdiff - t) < ONEFRAME)
|
|
Sleep(ONEFRAME - (dtdiff - t));
|
|
|
|
// try to give desktop a chance to update
|
|
// only do it on hide because desktop doesn't need to paint on taskbar show
|
|
if (!fShow)
|
|
{
|
|
DWORD_PTR lres;
|
|
SendMessageTimeout(v_hwndDesktop, DTM_UPDATENOW, 0, 0, SMTO_ABORTIFHUNG, 50, &lres);
|
|
}
|
|
}
|
|
|
|
SetThreadPriority(me, priority);
|
|
}
|
|
|
|
_SlideStep(hwnd, prcMonitor, &rcLast, prc);
|
|
|
|
if (fShow)
|
|
UpdateWindow(hwnd);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw ------------------END------------------"));
|
|
}
|
|
|
|
void CTray::_UnhideNow()
|
|
{
|
|
if (_uModalMode == MM_SHUTDOWN)
|
|
return;
|
|
|
|
_fSelfSizing = TRUE;
|
|
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
|
|
_SlideWindow(_hwnd, &_arStuckRects[_uStuckPlace], _dtSlideShow);
|
|
DAD_ShowDragImage(TRUE); // restore the lock state.
|
|
_fSelfSizing = FALSE;
|
|
|
|
SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, FALSE);
|
|
}
|
|
|
|
void CTray::_ComputeHiddenRect(LPRECT prc, UINT uStuck)
|
|
{
|
|
int dwh;
|
|
HMONITOR hMon;
|
|
RECT rcMon;
|
|
|
|
hMon = MonitorFromRect(prc, MONITOR_DEFAULTTONULL);
|
|
if (!hMon)
|
|
return;
|
|
GetMonitorRect(hMon, &rcMon);
|
|
|
|
if (STUCK_HORIZONTAL(uStuck))
|
|
dwh = prc->bottom - prc->top;
|
|
else
|
|
dwh = prc->right - prc->left;
|
|
|
|
switch (uStuck)
|
|
{
|
|
case STICK_LEFT:
|
|
prc->right = rcMon.left + (g_cxFrame / 2);
|
|
prc->left = prc->right - dwh;
|
|
break;
|
|
|
|
case STICK_RIGHT:
|
|
prc->left = rcMon.right - (g_cxFrame / 2);
|
|
prc->right = prc->left + dwh;
|
|
break;
|
|
|
|
case STICK_TOP:
|
|
prc->bottom = rcMon.top + (g_cyFrame / 2);
|
|
prc->top = prc->bottom - dwh;
|
|
break;
|
|
|
|
case STICK_BOTTOM:
|
|
prc->top = rcMon.bottom - (g_cyFrame / 2);
|
|
prc->bottom = prc->top + dwh;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UINT CTray::_GetDockedRect(LPRECT prc, BOOL fMoving)
|
|
{
|
|
UINT uPos;
|
|
|
|
if (fMoving && (_uMoveStuckPlace != (UINT)-1))
|
|
uPos = _uMoveStuckPlace;
|
|
else
|
|
uPos = _uStuckPlace;
|
|
|
|
*prc = _arStuckRects[uPos];
|
|
|
|
if ((_uAutoHide & (AH_ON | AH_HIDING)) == (AH_ON | AH_HIDING))
|
|
{
|
|
_ComputeHiddenRect(prc, uPos);
|
|
}
|
|
|
|
return uPos;
|
|
}
|
|
|
|
void CTray::_CalcClipCoords(RECT *prcClip, const RECT *prcMonitor, const RECT *prcNew)
|
|
{
|
|
RECT rcMonitor;
|
|
RECT rcWindow;
|
|
|
|
if (!prcMonitor)
|
|
{
|
|
_GetStuckDisplayRect(_uStuckPlace, &rcMonitor);
|
|
prcMonitor = &rcMonitor;
|
|
}
|
|
|
|
if (!prcNew)
|
|
{
|
|
GetWindowRect(_hwnd, &rcWindow);
|
|
prcNew = &rcWindow;
|
|
}
|
|
|
|
IntersectRect(prcClip, prcMonitor, prcNew);
|
|
OffsetRect(prcClip, -prcNew->left, -prcNew->top);
|
|
}
|
|
|
|
void CTray::_ClipInternal(const RECT *prcClip)
|
|
{
|
|
HRGN hrgnClip;
|
|
|
|
// don't worry about clipping if there's only one monitor
|
|
if (GetSystemMetrics(SM_CMONITORS) <= 1)
|
|
prcClip = NULL;
|
|
|
|
if (prcClip)
|
|
{
|
|
_fMonitorClipped = TRUE;
|
|
hrgnClip = CreateRectRgnIndirect(prcClip);
|
|
}
|
|
else
|
|
{
|
|
// SetWindowRgn is expensive, skip ones that are NOPs
|
|
if (!_fMonitorClipped)
|
|
return;
|
|
|
|
_fMonitorClipped = FALSE;
|
|
hrgnClip = NULL;
|
|
}
|
|
|
|
SetWindowRgn(_hwnd, hrgnClip, TRUE);
|
|
}
|
|
|
|
void CTray::_ClipWindow(BOOL fClipState)
|
|
{
|
|
RECT rcClip;
|
|
RECT *prcClip;
|
|
|
|
if (_fSelfSizing || _fSysSizing)
|
|
{
|
|
TraceMsg(TF_WARNING, "_ClipWindow: _fSelfSizing %x, _fSysSizing %x", _fSelfSizing, _fSysSizing);
|
|
return;
|
|
}
|
|
|
|
if ((GetSystemMetrics(SM_CMONITORS) <= 1) || _hTheme)
|
|
fClipState = FALSE;
|
|
|
|
if (fClipState)
|
|
{
|
|
prcClip = &rcClip;
|
|
_CalcClipCoords(prcClip, NULL, NULL);
|
|
}
|
|
else
|
|
prcClip = NULL;
|
|
|
|
_ClipInternal(prcClip);
|
|
}
|
|
|
|
void CTray::_Hide()
|
|
{
|
|
RECT rcNew;
|
|
|
|
// if we're in shutdown or if we're on boot up
|
|
// don't hide
|
|
if (_uModalMode == MM_SHUTDOWN)
|
|
{
|
|
TraceMsg(TF_TRAY, "e.th: suppress hide (shutdown || Notify)");
|
|
return;
|
|
}
|
|
|
|
KillTimer(_hwnd, IDT_AUTOHIDE);
|
|
|
|
_fSelfSizing = TRUE;
|
|
|
|
//
|
|
// update the flags here to prevent race conditions
|
|
//
|
|
_uAutoHide = AH_ON | AH_HIDING;
|
|
_GetDockedRect(&rcNew, FALSE);
|
|
|
|
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
|
|
_SlideWindow(_hwnd, &rcNew, _dtSlideHide);
|
|
DAD_ShowDragImage(FALSE); // Another thread could have locked while we were gone
|
|
DAD_ShowDragImage(TRUE); // restore the lock state.
|
|
|
|
SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, TRUE);
|
|
|
|
_fSelfSizing = FALSE;
|
|
}
|
|
|
|
void CTray::_AutoHideCollision()
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_ahc COLLISION! (posting UI request)"));
|
|
|
|
PostMessage(_hwnd, TM_WARNNOAUTOHIDE, ((_uAutoHide & AH_ON) != 0),
|
|
0L);
|
|
}
|
|
|
|
LONG CTray::_SetAutoHideState(BOOL fAutoHide)
|
|
{
|
|
//
|
|
// make sure we have something to do
|
|
//
|
|
if ((fAutoHide != 0) == ((_uAutoHide & AH_ON) != 0))
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sahs nothing to do"));
|
|
return MAKELONG(FALSE, TRUE);
|
|
}
|
|
|
|
//
|
|
// make sure we can do it
|
|
//
|
|
if (!_AppBarSetAutoHideBar2(_hwnd, fAutoHide, _uStuckPlace))
|
|
{
|
|
_AutoHideCollision();
|
|
return MAKELONG(FALSE, FALSE);
|
|
}
|
|
|
|
//
|
|
// do it
|
|
//
|
|
if (fAutoHide)
|
|
{
|
|
_uAutoHide = AH_ON;
|
|
_RefreshSettings();
|
|
_Hide();
|
|
#ifdef DEBUG
|
|
// _Hide updates the flags for us (sanity)
|
|
if (!(_uAutoHide & AH_ON))
|
|
{
|
|
TraceMsg(DM_WARNING, "e.sahs: !AH_ON"); // ok to fail on boot/shutdown
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_uAutoHide = 0;
|
|
KillTimer(_hwnd, IDT_AUTOHIDE);
|
|
_UnhideNow();
|
|
_RefreshSettings();
|
|
}
|
|
|
|
//
|
|
// brag about it
|
|
//
|
|
_StuckTrayChange();
|
|
return MAKELONG(TRUE, TRUE);
|
|
}
|
|
|
|
void CTray::_HandleEnterMenuLoop()
|
|
{
|
|
// kill the timer when we're in the menu loop so that we don't
|
|
// pop done while browsing the menus.
|
|
if (_uAutoHide & AH_ON)
|
|
{
|
|
KillTimer(_hwnd, IDT_AUTOHIDE);
|
|
}
|
|
}
|
|
|
|
void CTray::_SetAutoHideTimer()
|
|
{
|
|
if (_uAutoHide & AH_ON)
|
|
{
|
|
SetTimer(_hwnd, IDT_AUTOHIDE, 500, NULL);
|
|
}
|
|
}
|
|
|
|
void CTray::_HandleExitMenuLoop()
|
|
{
|
|
// when we leave the menu stuff, start checking again.
|
|
_SetAutoHideTimer();
|
|
}
|
|
|
|
void CTray::Unhide()
|
|
{
|
|
// handle autohide
|
|
if ((_uAutoHide & AH_ON) &&
|
|
(_uAutoHide & AH_HIDING))
|
|
{
|
|
_UnhideNow();
|
|
_uAutoHide &= ~AH_HIDING;
|
|
_SetAutoHideTimer();
|
|
|
|
if (_fShouldResize)
|
|
{
|
|
ASSERT(0);
|
|
ASSERT(!(_uAutoHide & AH_HIDING));
|
|
SizeWindows();
|
|
_fShouldResize = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_SetUnhideTimer(LONG x, LONG y)
|
|
{
|
|
// handle autohide
|
|
if ((_uAutoHide & AH_ON) &&
|
|
(_uAutoHide & AH_HIDING))
|
|
{
|
|
LONG dx = x-_ptLastHittest.x;
|
|
LONG dy = y-_ptLastHittest.y;
|
|
LONG rr = dx*dx + dy*dy;
|
|
LONG dd = GetSystemMetrics(SM_CXDOUBLECLK) * GetSystemMetrics(SM_CYDOUBLECLK);
|
|
|
|
if (rr > dd)
|
|
{
|
|
SetTimer(_hwnd, IDT_AUTOUNHIDE, 50, NULL);
|
|
_ptLastHittest.x = x;
|
|
_ptLastHittest.y = y;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_StartButtonReset()
|
|
{
|
|
// Get an idea about how big we need everyhting to be.
|
|
TCHAR szStart[50];
|
|
LoadString(hinstCabinet, _hTheme ? IDS_START : IDS_STARTCLASSIC, szStart, ARRAYSIZE(szStart));
|
|
SetWindowText(_hwndStart, szStart);
|
|
|
|
if (_hFontStart)
|
|
DeleteObject(_hFontStart);
|
|
|
|
_hFontStart = _CreateStartFont(_hwndStart);
|
|
|
|
int idbStart = IDB_START16;
|
|
|
|
HDC hdc = GetDC(NULL);
|
|
if (hdc)
|
|
{
|
|
int bpp = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);
|
|
if (bpp > 8)
|
|
{
|
|
idbStart = _hTheme ? IDB_START : IDB_STARTCLASSIC;
|
|
}
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
|
|
HBITMAP hbmFlag = (HBITMAP)LoadImage(hinstCabinet, MAKEINTRESOURCE(idbStart), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
|
|
if (hbmFlag)
|
|
{
|
|
BITMAP bm;
|
|
if (GetObject(hbmFlag, sizeof(BITMAP), &bm))
|
|
{
|
|
BUTTON_IMAGELIST biml = {0};
|
|
if (_himlStartFlag)
|
|
ImageList_Destroy(_himlStartFlag);
|
|
|
|
|
|
DWORD dwFlags = ILC_COLOR32;
|
|
HBITMAP hbmFlagMask = NULL;
|
|
if (idbStart == IDB_START16)
|
|
{
|
|
dwFlags = ILC_COLOR8 | ILC_MASK;
|
|
hbmFlagMask = (HBITMAP)LoadImage(hinstCabinet, MAKEINTRESOURCE(IDB_START16MASK), IMAGE_BITMAP, 0, 0, LR_MONOCHROME);
|
|
}
|
|
|
|
if (IS_WINDOW_RTL_MIRRORED(_hwndStart))
|
|
{
|
|
dwFlags |= ILC_MIRROR;
|
|
}
|
|
biml.himl = _himlStartFlag = ImageList_Create(bm.bmWidth, bm.bmHeight, dwFlags, 1, 1);
|
|
ImageList_Add(_himlStartFlag, hbmFlag, hbmFlagMask);
|
|
|
|
if (hbmFlagMask)
|
|
{
|
|
DeleteObject(hbmFlagMask);
|
|
}
|
|
|
|
biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
|
|
|
|
Button_SetImageList(_hwndStart, &biml);
|
|
}
|
|
DeleteObject(hbmFlag);
|
|
}
|
|
|
|
if (_hFontStart)
|
|
{
|
|
SendMessage(_hwndStart, WM_SETFONT, (WPARAM)_hFontStart, TRUE);
|
|
_sizeStart.cx = 0;
|
|
}
|
|
|
|
_AlignStartButton();
|
|
|
|
}
|
|
|
|
void CTray::_OnNewSystemSizes()
|
|
{
|
|
TraceMsg(TF_TRAY, "Handling win ini change.");
|
|
_StartButtonReset();
|
|
VerifySize(TRUE);
|
|
}
|
|
|
|
//*** CheckWindowPositions -- flag which windows actually changed
|
|
// ENTRY/EXIT
|
|
// _pPositions->hdsaWP[*]->fRestore modified
|
|
// NOTES
|
|
// in order to correctly implement 'Undo Minimize-all(Cascade/Tile)',
|
|
// we need to tell which windows were changed by the 'Do' operation.
|
|
// (nt5:183421: we used to restore *every* top-level window).
|
|
int WINAPI CTray::CheckWndPosEnumProc(void *pItem, void *pData)
|
|
{
|
|
HWNDANDPLACEMENT *pI2 = (HWNDANDPLACEMENT *)pItem;
|
|
WINDOWPLACEMENT wp;
|
|
|
|
wp.length = sizeof(wp);
|
|
pI2->fRestore = TRUE;
|
|
if (GetWindowPlacement(pI2->hwnd, &wp)) {
|
|
if (memcmp(&pI2->wp, &wp, sizeof(wp)) == 0)
|
|
pI2->fRestore = FALSE;
|
|
}
|
|
|
|
TraceMsg(TF_TRAY, "cwp: (hwnd=0x%x) fRestore=%d", pI2->hwnd, pI2->fRestore);
|
|
|
|
return 1; // 1:continue enum
|
|
}
|
|
|
|
void CTray::CheckWindowPositions()
|
|
{
|
|
ENTERCRITICAL; // i think this is needed...
|
|
if (_pPositions) {
|
|
if (_pPositions->hdsaWP) {
|
|
DSA_EnumCallback(_pPositions->hdsaWP, CheckWndPosEnumProc, NULL);
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL BandSite_PermitAutoHide(IUnknown* punk)
|
|
{
|
|
OLECMD cmd = { DBID_PERMITAUTOHIDE, 0 };
|
|
if (SUCCEEDED(IUnknown_QueryStatus(punk, &IID_IDockingWindow, 1, &cmd, NULL)))
|
|
{
|
|
return !(cmd.cmdf & OLECMDF_SUPPORTED) || (cmd.cmdf & OLECMDF_ENABLED);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::_HandleTimer(WPARAM wTimerID)
|
|
{
|
|
switch (wTimerID)
|
|
{
|
|
case IDT_CHECKDISKSPACE:
|
|
CheckDiskSpace();
|
|
break;
|
|
|
|
case IDT_DESKTOPCLEANUP:
|
|
_CheckDesktopCleanup();
|
|
break;
|
|
|
|
case IDT_CHANGENOTIFY:
|
|
// did somebody send another notify since we last handled one?
|
|
if (_fUseChangeNotifyTimer)
|
|
{
|
|
// yep.
|
|
// all we do is check the staging area.
|
|
CheckStagingArea();
|
|
|
|
// kill it off the next time.
|
|
_fUseChangeNotifyTimer = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// nope.
|
|
KillTimer(_hwnd, IDT_CHANGENOTIFY);
|
|
_fChangeNotifyTimerRunning = FALSE;
|
|
}
|
|
break;
|
|
|
|
case IDT_HANDLEDELAYBOOTSTUFF:
|
|
KillTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF);
|
|
PostMessage(_hwnd, TM_HANDLEDELAYBOOTSTUFF, 0, 0);
|
|
break;
|
|
|
|
case IDT_STARTMENU:
|
|
SetForegroundWindow(_hwnd);
|
|
KillTimer(_hwnd, wTimerID);
|
|
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
|
|
SendMessage(_hwndStart, BM_SETSTATE, TRUE, 0);
|
|
UpdateWindow(_hwndStart);
|
|
DAD_ShowDragImage(TRUE); // restore the lock state.
|
|
break;
|
|
|
|
case IDT_SAVESETTINGS:
|
|
KillTimer(_hwnd, IDT_SAVESETTINGS);
|
|
_SaveTray();
|
|
break;
|
|
|
|
case IDT_ENABLEUNDO:
|
|
KillTimer(_hwnd, IDT_ENABLEUNDO);
|
|
CheckWindowPositions();
|
|
_fUndoEnabled = TRUE;
|
|
break;
|
|
|
|
case IDT_AUTOHIDE:
|
|
if (!_fSysSizing && (_uAutoHide & AH_ON))
|
|
{
|
|
POINT pt;
|
|
RECT rc;
|
|
|
|
// Don't hide if we're already hiding, a balloon tip is showing, or
|
|
// (on NT5) if any apps are flashing.
|
|
//
|
|
if (!(_uAutoHide & AH_HIDING) && BandSite_PermitAutoHide(_ptbs) && !_fBalloonUp)
|
|
{
|
|
// Get the cursor position.
|
|
GetCursorPos(&pt);
|
|
|
|
// Get the tray rect and inflate it a bit.
|
|
rc = _arStuckRects[_uStuckPlace];
|
|
InflateRect(&rc, g_cxEdge * 4, g_cyEdge*4);
|
|
|
|
// Don't hide if cursor is within inflated tray rect.
|
|
if (!PtInRect(&rc, pt))
|
|
{
|
|
// Don't hide if the tray is active
|
|
if (!_IsActive() && _uStartButtonState != SBSM_SPACTIVE)
|
|
{
|
|
// Don't hide if the view has a system menu up.
|
|
if (!SendMessage(_hwndTasks, TBC_SYSMENUCOUNT, 0, 0L))
|
|
{
|
|
// Phew! We made it. Hide the tray.
|
|
_Hide();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDT_AUTOUNHIDE:
|
|
if (!_fSysSizing && (_uAutoHide & AH_ON))
|
|
{
|
|
POINT pt;
|
|
RECT rc;
|
|
|
|
KillTimer(_hwnd, wTimerID);
|
|
_ptLastHittest.x = -0x0fff;
|
|
_ptLastHittest.y = -0x0fff;
|
|
GetWindowRect(_hwnd, &rc);
|
|
if (_uAutoHide & AH_HIDING)
|
|
{
|
|
GetCursorPos(&pt);
|
|
if (PtInRect(&rc, pt))
|
|
Unhide();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDT_STARTBUTTONBALLOON:
|
|
_DestroyStartButtonBalloon();
|
|
break;
|
|
|
|
case IDT_COFREEUNUSED:
|
|
CoFreeUnusedLibraries();
|
|
KillTimer(_hwnd, IDT_COFREEUNUSED);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
void CTray::_CheckStagingAreaOnTimer()
|
|
{
|
|
if (_fChangeNotifyTimerRunning)
|
|
{
|
|
// we're already running the timer, so force a check the next time it comes up
|
|
_fUseChangeNotifyTimer = TRUE;
|
|
}
|
|
else
|
|
{
|
|
_fChangeNotifyTimerRunning = TRUE;
|
|
|
|
// check once immediately
|
|
CheckStagingArea();
|
|
|
|
// check again in half a minute, but only if notifies have been happening in the meantime.
|
|
SetTimer(_hwnd, IDT_CHANGENOTIFY, 30 * 1000, NULL);
|
|
}
|
|
}
|
|
|
|
void CTray::_HandleChangeNotify(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPITEMIDLIST *ppidl;
|
|
LONG lEvent;
|
|
LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
|
|
if (pshcnl)
|
|
{
|
|
if (lEvent & SHCNE_STAGINGAREANOTIFICATIONS)
|
|
{
|
|
// something has changed within the staging area.
|
|
_CheckStagingAreaOnTimer();
|
|
}
|
|
SHChangeNotification_Unlock(pshcnl);
|
|
}
|
|
}
|
|
|
|
BOOL _ExecItemByPidls(HWND hwnd, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
|
|
{
|
|
BOOL fRes = FALSE;
|
|
|
|
if (pidlFolder && pidlItem)
|
|
{
|
|
IShellFolder *psf = BindToFolder(pidlFolder);
|
|
if (psf)
|
|
{
|
|
fRes = SUCCEEDED(SHInvokeDefaultCommand(hwnd, psf, pidlItem));
|
|
}
|
|
else
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
SHGetPathFromIDList(pidlFolder, szPath);
|
|
ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_CANTFINDSPECIALDIR),
|
|
NULL, MB_ICONEXCLAMATION, szPath);
|
|
}
|
|
}
|
|
return fRes;
|
|
}
|
|
|
|
void _DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions);
|
|
|
|
LRESULT CTray::_HandleDestroy()
|
|
{
|
|
MINIMIZEDMETRICS mm;
|
|
|
|
TraceMsg(DM_SHUTDOWN, "_HD: enter");
|
|
|
|
mm.cbSize = sizeof(mm);
|
|
SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
|
|
mm.iArrange &= ~ARW_HIDE;
|
|
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
|
|
|
|
_RevokeDropTargets();
|
|
_DestroyStartMenu();
|
|
Mixer_Shutdown();
|
|
|
|
// Tell the start menu to free all its cached darwin links
|
|
SHRegisterDarwinLink(NULL, NULL, TRUE);
|
|
|
|
_DestroySavedWindowPositions(_pPositions);
|
|
_pPositions = NULL;
|
|
|
|
if (_hTheme)
|
|
{
|
|
CloseThemeData(_hTheme);
|
|
_hTheme = NULL;
|
|
}
|
|
|
|
_UnregisterGlobalHotkeys();
|
|
|
|
if (_uNotify)
|
|
{
|
|
SHChangeNotifyDeregister(_uNotify);
|
|
_uNotify = 0;
|
|
}
|
|
|
|
ATOMICRELEASE(_ptbs);
|
|
ATOMICRELEASE(_pdbTasks);
|
|
_hwndTasks = NULL;
|
|
|
|
if (_hwndTrayTips)
|
|
{
|
|
DestroyWindow(_hwndTrayTips);
|
|
_hwndTrayTips = NULL;
|
|
}
|
|
|
|
_DestroyStartButtonBalloon();
|
|
|
|
// REVIEW
|
|
PostQuitMessage(0);
|
|
|
|
if (_hbmpStartBkg)
|
|
{
|
|
DeleteBitmap(_hbmpStartBkg);
|
|
}
|
|
|
|
if (_hFontStart)
|
|
{
|
|
DeleteObject(_hFontStart);
|
|
}
|
|
|
|
if (_himlStartFlag)
|
|
{
|
|
ImageList_Destroy(_himlStartFlag);
|
|
}
|
|
|
|
// clean up service objects
|
|
_ssomgr.Destroy();
|
|
|
|
if (_hShellReadyEvent)
|
|
{
|
|
ResetEvent(_hShellReadyEvent);
|
|
CloseHandle(_hShellReadyEvent);
|
|
_hShellReadyEvent = NULL;
|
|
}
|
|
|
|
if (_fHandledDelayBootStuff)
|
|
{
|
|
TBOOL(WinStationUnRegisterConsoleNotification(SERVERNAME_CURRENT, v_hwndTray));
|
|
}
|
|
|
|
DeleteCriticalSection(&_csHotkey);
|
|
|
|
// The order in which we shut down the HTTP key monitoring is important.
|
|
//
|
|
// We must close the key before closing the event handle because
|
|
// closing the key causes the event to be signalled and we don't
|
|
// want ADVAPI32 to try to signal an event after we closed its handle...
|
|
//
|
|
// To avoid a spurious trigger when the event fires, we unregister
|
|
// the wait before closing the key.
|
|
//
|
|
|
|
if (_hHTTPWait)
|
|
{
|
|
UnregisterWait(_hHTTPWait);
|
|
_hHTTPWait = NULL;
|
|
}
|
|
|
|
if (_hkHTTP)
|
|
{
|
|
RegCloseKey(_hkHTTP);
|
|
_hkHTTP = NULL;
|
|
}
|
|
|
|
if (_hHTTPEvent)
|
|
{
|
|
CloseHandle(_hHTTPEvent);
|
|
_hHTTPEvent = NULL;
|
|
}
|
|
|
|
// End of order-sensitive operations ----------------------------------
|
|
|
|
v_hwndTray = NULL;
|
|
_hwndStart = NULL;
|
|
|
|
|
|
TraceMsg(DM_SHUTDOWN, "_HD: leave");
|
|
return 0;
|
|
}
|
|
|
|
void CTray::_SetFocus(HWND hwnd)
|
|
{
|
|
IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
|
|
SetFocus(hwnd);
|
|
}
|
|
|
|
#define TRIEDTOOMANYTIMES 100
|
|
|
|
void CTray::_ActAsSwitcher()
|
|
{
|
|
if (_uModalMode)
|
|
{
|
|
if (_uModalMode != MM_SHUTDOWN)
|
|
{
|
|
SwitchToThisWindow(GetLastActivePopup(_hwnd), TRUE);
|
|
}
|
|
MessageBeep(0);
|
|
}
|
|
else
|
|
{
|
|
HWND hwndForeground;
|
|
HWND hwndActive;
|
|
|
|
static int s_iRecurse = 0;
|
|
s_iRecurse++;
|
|
|
|
ASSERT(s_iRecurse < TRIEDTOOMANYTIMES);
|
|
TraceMsg(TF_TRAY, "s_iRecurse = %d", s_iRecurse);
|
|
|
|
hwndForeground = GetForegroundWindow();
|
|
hwndActive = GetActiveWindow();
|
|
BOOL fIsTrayActive = (hwndForeground == _hwnd) && (hwndActive == _hwnd);
|
|
if (v_hwndStartPane && hwndForeground == v_hwndStartPane && hwndActive == v_hwndStartPane)
|
|
{
|
|
fIsTrayActive = TRUE;
|
|
}
|
|
// only do the button once we're the foreground dude.
|
|
if (fIsTrayActive)
|
|
{
|
|
// This code path causes the start button to do something because
|
|
// of the keyboard. So reflect that with the focus rect.
|
|
SendMessage(_hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
|
|
UISF_HIDEFOCUS), 0);
|
|
|
|
if (SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
|
|
{
|
|
ClosePopupMenus();
|
|
ForceStartButtonUp();
|
|
}
|
|
else
|
|
{
|
|
// This pushes the start button and causes the start menu to popup.
|
|
SendMessage(GetDlgItem(_hwnd, IDC_START), BM_SETSTATE, TRUE, 0);
|
|
}
|
|
s_iRecurse = 0;
|
|
}
|
|
else
|
|
{
|
|
// we don't want to loop endlessly trying to become
|
|
// foreground. With NT's new SetForegroundWindow rules, it would
|
|
// be pointless to try and hopefully we won't need to anyhow.
|
|
// Randomly, I picked a quarter as many times as the debug squirty would indicate
|
|
// as the number of times to try on NT.
|
|
// Hopefully that is enough on most machines.
|
|
if (s_iRecurse > (TRIEDTOOMANYTIMES / 4))
|
|
{
|
|
s_iRecurse = 0;
|
|
return;
|
|
}
|
|
// until then, try to come forward.
|
|
HandleFullScreenApp(NULL);
|
|
if (hwndForeground == v_hwndDesktop)
|
|
{
|
|
_SetFocus(_hwndStart);
|
|
if (GetFocus() != _hwndStart)
|
|
return;
|
|
}
|
|
|
|
SwitchToThisWindow(_hwnd, TRUE);
|
|
SetForegroundWindow(_hwnd);
|
|
Sleep(20); // give some time for other async activation messages to get posted
|
|
PostMessage(_hwnd, TM_ACTASTASKSW, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_OnWinIniChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
Cabinet_InitGlobalMetrics(wParam, (LPTSTR)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, TEXT("WindowMetrics")) == 0))))
|
|
{
|
|
#ifdef DEBUG
|
|
if (wParam == SPI_SETNONCLIENTMETRICS)
|
|
TraceMsg(TF_TRAY, "c.t_owic: Non-client metrics (probably) changed.");
|
|
else
|
|
TraceMsg(TF_TRAY, "c.t_owic: Window metrics changed.");
|
|
#endif
|
|
|
|
_OnNewSystemSizes();
|
|
}
|
|
|
|
// Handle old extensions.
|
|
if (!lParam || (lParam && (lstrcmpi((LPTSTR)lParam, TEXT("Extensions")) == 0)))
|
|
{
|
|
TraceMsg(TF_TRAY, "t_owic: Extensions section change.");
|
|
CheckWinIniForAssocs();
|
|
}
|
|
|
|
if (lParam && (0 == lstrcmpi((LPCTSTR)lParam, TEXT("TraySettings"))))
|
|
{
|
|
_Command(FCIDM_REFRESH);
|
|
}
|
|
|
|
// Tell shell32 to refresh its cache
|
|
SHSettingsChanged(wParam, lParam);
|
|
}
|
|
|
|
HWND CTray::_HotkeyInUse(WORD wHK)
|
|
{
|
|
HWND hwnd;
|
|
DWORD_PTR lrHKInUse = 0;
|
|
int nMod;
|
|
WORD wHKNew;
|
|
#ifdef DEBUG
|
|
TCHAR sz[MAX_PATH];
|
|
#endif
|
|
|
|
// Map the modifiers back.
|
|
nMod = 0;
|
|
if (HIBYTE(wHK) & MOD_SHIFT)
|
|
nMod |= HOTKEYF_SHIFT;
|
|
if (HIBYTE(wHK) & MOD_CONTROL)
|
|
nMod |= HOTKEYF_CONTROL;
|
|
if (HIBYTE(wHK) & MOD_ALT)
|
|
nMod |= HOTKEYF_ALT;
|
|
|
|
wHKNew = (WORD)((nMod*256)+LOBYTE(wHK));
|
|
|
|
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: Checking for %x"), wHKNew);
|
|
hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
|
|
while (hwnd)
|
|
{
|
|
SendMessageTimeout(hwnd, WM_GETHOTKEY, 0, 0, SMTO_ABORTIFHUNG| SMTO_BLOCK, 3000, &lrHKInUse);
|
|
if (wHKNew == (WORD)lrHKInUse)
|
|
{
|
|
#ifdef DEBUG
|
|
GetWindowText(hwnd, sz, ARRAYSIZE(sz));
|
|
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
|
|
#endif
|
|
return hwnd;
|
|
}
|
|
#ifdef DEBUG
|
|
else if (lrHKInUse)
|
|
{
|
|
GetWindowText(hwnd, sz, ARRAYSIZE(sz));
|
|
DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
|
|
}
|
|
#endif
|
|
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CTray::_HandleHotKey(int nID)
|
|
{
|
|
TraceMsg(TF_TRAY, "c.hkl_hh: Handling hotkey (%d).", nID);
|
|
|
|
// Find it in the list.
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
HOTKEYITEM *phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, nID);
|
|
if (phki && phki->wGHotkey)
|
|
{
|
|
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey listed.");
|
|
|
|
// Are global hotkeys enabled?
|
|
if (!_fGlobalHotkeyDisable)
|
|
{
|
|
// Yep.
|
|
HWND hwnd = _HotkeyInUse(phki->wGHotkey);
|
|
// Make sure this hotkey isn't already in use by someone.
|
|
if (hwnd)
|
|
{
|
|
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is already in use.");
|
|
// Activate it.
|
|
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
|
|
}
|
|
else
|
|
{
|
|
DECLAREWAITCURSOR;
|
|
// Exec the item.
|
|
SetWaitCursor();
|
|
TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is not in use, execing item.");
|
|
ASSERT(phki->pidlFolder && phki->pidlItem);
|
|
BOOL fRes = _ExecItemByPidls(_hwnd, 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."));
|
|
}
|
|
LeaveCriticalSection(&_csHotkey);
|
|
}
|
|
|
|
LRESULT CTray::_UnregisterHotkey(HWND hwnd, int i)
|
|
{
|
|
TraceMsg(TF_TRAY, "c.t_uh: Unregistering hotkey (%d).", i);
|
|
|
|
if (!UnregisterHotKey(hwnd, i))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("c.t_rh: Unable to unregister hotkey %d."), i);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Add hotkey to the shell's list of global hotkeys.
|
|
LRESULT CTray::_ShortcutRegisterHotkey(HWND hwnd, WORD wHotkey, ATOM atom)
|
|
{
|
|
int i;
|
|
LPITEMIDLIST pidl;
|
|
TCHAR szPath[MAX_PATH];
|
|
ASSERT(atom);
|
|
|
|
if (GlobalGetAtomName(atom, szPath, MAX_PATH))
|
|
{
|
|
TraceMsg(TF_TRAY, "c.t_srh: Hotkey %d for %s", wHotkey, szPath);
|
|
|
|
pidl = ILCreateFromPath(szPath);
|
|
if (pidl)
|
|
{
|
|
i = _HotkeyAddCached(_MapHotkeyToGlobalHotkey(wHotkey), pidl);
|
|
if (i != -1)
|
|
{
|
|
_RegisterHotkey(_hwnd, i);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Remove hotkey from shell's list.
|
|
LRESULT CTray::_ShortcutUnregisterHotkey(HWND hwnd, WORD wHotkey)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.t_suh: Hotkey %d", wHotkey);
|
|
int i = _HotkeyRemove(wHotkey);
|
|
if (i == -1)
|
|
i = _HotkeyRemoveCached(_MapHotkeyToGlobalHotkey(wHotkey));
|
|
|
|
if (i != -1)
|
|
_UnregisterHotkey(hwnd, i);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CTray::_RegisterHotkey(HWND hwnd, int i)
|
|
{
|
|
HOTKEYITEM *phki;
|
|
WORD wGHotkey = 0;
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
|
|
|
|
TraceMsg(TF_TRAY, "c.t_rh: Registering hotkey (%d).", i);
|
|
|
|
EnterCriticalSection(&_csHotkey);
|
|
|
|
phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, i);
|
|
ASSERT(phki);
|
|
if (phki)
|
|
{
|
|
wGHotkey = phki->wGHotkey;
|
|
}
|
|
|
|
LeaveCriticalSection(&_csHotkey);
|
|
|
|
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.
|
|
int iCached = _HotkeyRemoveCached(wGHotkey);
|
|
ASSERT(iCached != i);
|
|
if (iCached != -1)
|
|
{
|
|
// Free up the hotkey for us.
|
|
_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;
|
|
}
|
|
|
|
|
|
#define GetABDHWnd(pabd) ((HWND)ULongToPtr((pabd)->dwWnd))
|
|
|
|
void CTray::_AppBarGetTaskBarPos(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
APPBARDATA3264 *pabd;
|
|
|
|
pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
|
|
if (pabd)
|
|
{
|
|
pabd->rc = _arStuckRects[_uStuckPlace];
|
|
pabd->uEdge = _uStuckPlace; // compat: new to ie4
|
|
SHUnlockShared(pabd);
|
|
}
|
|
}
|
|
|
|
void CTray::_NukeAppBar(int i)
|
|
{
|
|
LocalFree(DPA_GetPtr(_hdpaAppBars, i));
|
|
DPA_DeletePtr(_hdpaAppBars, i);
|
|
}
|
|
|
|
void CTray::_AppBarRemove(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
int i;
|
|
|
|
if (!_hdpaAppBars)
|
|
return;
|
|
|
|
i = DPA_GetPtrCount(_hdpaAppBars);
|
|
|
|
while (i--)
|
|
{
|
|
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
|
|
|
|
if (GetABDHWnd(&ptabd->abd) == pab->hwnd)
|
|
{
|
|
RECT rcNuke = pab->rc;
|
|
|
|
_NukeAppBar(i);
|
|
_StuckAppChange(GetABDHWnd(&ptabd->abd), &rcNuke, NULL, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
PAPPBAR CTray::_FindAppBar(HWND hwnd)
|
|
{
|
|
if (_hdpaAppBars)
|
|
{
|
|
int i = DPA_GetPtrCount(_hdpaAppBars);
|
|
|
|
while (i--)
|
|
{
|
|
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
|
|
|
|
if (hwnd == pab->hwnd)
|
|
return pab;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CTray::_AppBarNotifyAll(HMONITOR hmon, UINT uMsg, HWND hwndExclude, LPARAM lParam)
|
|
{
|
|
if (!_hdpaAppBars)
|
|
return;
|
|
|
|
int i = DPA_GetPtrCount(_hdpaAppBars);
|
|
|
|
while (i--)
|
|
{
|
|
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
|
|
|
|
// We need to check pab here as an appbar can delete other
|
|
// appbars on the callback.
|
|
if (pab && (hwndExclude != pab->hwnd))
|
|
{
|
|
if (!IsWindow(pab->hwnd))
|
|
{
|
|
_NukeAppBar(i);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// if a monitor was specified only tell appbars on that display
|
|
//
|
|
if (hmon &&
|
|
(hmon != MonitorFromWindow(pab->hwnd, MONITOR_DEFAULTTONULL)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PostMessage(pab->hwnd, pab->uCallbackMessage, uMsg, lParam);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CTray::_AppBarNew(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
PAPPBAR pab;
|
|
if (!_hdpaAppBars)
|
|
{
|
|
_hdpaAppBars = DPA_Create(4);
|
|
if (!_hdpaAppBars)
|
|
return FALSE;
|
|
|
|
}
|
|
else if (_FindAppBar(GetABDHWnd(&ptabd->abd)))
|
|
{
|
|
// already have this hwnd
|
|
return FALSE;
|
|
}
|
|
|
|
pab = (PAPPBAR)LocalAlloc(LPTR, sizeof(APPBAR));
|
|
if (!pab)
|
|
return FALSE;
|
|
|
|
pab->hwnd = GetABDHWnd(&ptabd->abd);
|
|
pab->uCallbackMessage = ptabd->abd.uCallbackMessage;
|
|
pab->uEdge = (UINT)-1;
|
|
|
|
if (DPA_AppendPtr(_hdpaAppBars, pab) == -1)
|
|
{
|
|
// insertion failed
|
|
LocalFree(pab);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL CTray::_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 CTray::_AppBarQueryPos(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
int i;
|
|
PAPPBAR pabReq = _FindAppBar(GetABDHWnd(&ptabd->abd));
|
|
|
|
if (pabReq)
|
|
{
|
|
APPBARDATA3264 *pabd;
|
|
|
|
pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
|
|
if (pabd)
|
|
{
|
|
HMONITOR hmon;
|
|
|
|
pabd->rc = ptabd->abd.rc;
|
|
|
|
//
|
|
// default to the primary display for this call because old appbars
|
|
// sometimes pass a huge rect and let us pare it down. if they do
|
|
// something like that they don't support multiple displays anyway
|
|
// so just put them on the primary display...
|
|
//
|
|
hmon = MonitorFromRect(&pabd->rc, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
//
|
|
// always subtract off the tray if it's on the same display
|
|
//
|
|
if (!_uAutoHide && (hmon == _hmonStuck))
|
|
{
|
|
APPBAR ab;
|
|
|
|
ab.uEdge = _GetDockedRect(&ab.rc, FALSE);
|
|
_AppBarSubtractRect(&ab, &pabd->rc);
|
|
}
|
|
|
|
i = DPA_GetPtrCount(_hdpaAppBars);
|
|
|
|
while (i--)
|
|
{
|
|
PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
|
|
|
|
//
|
|
// give top and bottom preference
|
|
// ||
|
|
// if we're not changing edges,
|
|
// subtract anything currently on the outside of us
|
|
// ||
|
|
// if we are changing sides,
|
|
// subtract off everything on the new side.
|
|
//
|
|
// of course ignore appbars which are not on the same display...
|
|
//
|
|
if ((((pabReq->hwnd != pab->hwnd) &&
|
|
STUCK_HORIZONTAL(pab->uEdge) &&
|
|
!STUCK_HORIZONTAL(ptabd->abd.uEdge)) ||
|
|
((pabReq->hwnd != pab->hwnd) &&
|
|
(pabReq->uEdge == ptabd->abd.uEdge) &&
|
|
_AppBarOutsideOf(pabReq, pab)) ||
|
|
((pabReq->hwnd != pab->hwnd) &&
|
|
(pabReq->uEdge != ptabd->abd.uEdge) &&
|
|
(pab->uEdge == ptabd->abd.uEdge))) &&
|
|
(hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL)))
|
|
{
|
|
_AppBarSubtractRect(pab, &pabd->rc);
|
|
}
|
|
}
|
|
SHUnlockShared(pabd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_AppBarSetPos(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
PAPPBAR pab = _FindAppBar(GetABDHWnd(&ptabd->abd));
|
|
|
|
if (pab)
|
|
{
|
|
RECT rcOld;
|
|
APPBARDATA3264 *pabd;
|
|
BOOL fChanged = FALSE;
|
|
|
|
_AppBarQueryPos(ptabd);
|
|
|
|
pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
|
|
if (pabd)
|
|
{
|
|
if (!EqualRect(&pab->rc, &pabd->rc)) {
|
|
rcOld = pab->rc;
|
|
pab->rc = pabd->rc;
|
|
pab->uEdge = ptabd->abd.uEdge;
|
|
fChanged = TRUE;
|
|
}
|
|
SHUnlockShared(pabd);
|
|
}
|
|
|
|
if (fChanged)
|
|
_StuckAppChange(GetABDHWnd(&ptabd->abd), &rcOld, &pab->rc, FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// FEATURE: need to get rid of this array-based implementation to allow autohide
|
|
// appbars on secondary display (or a/h tray on 2nd with a/h appbar on primary)
|
|
// change it to an _AppBarFindAutoHideBar that keeps flags on the appbardata...
|
|
//
|
|
HWND CTray::_AppBarGetAutoHideBar(UINT uEdge)
|
|
{
|
|
if (uEdge >= ABE_MAX)
|
|
return FALSE;
|
|
else
|
|
{
|
|
HWND hwndAutoHide = _aHwndAutoHide[uEdge];
|
|
if (!IsWindow(hwndAutoHide))
|
|
{
|
|
_aHwndAutoHide[uEdge] = NULL;
|
|
}
|
|
return _aHwndAutoHide[uEdge];
|
|
}
|
|
}
|
|
|
|
BOOL CTray::_AppBarSetAutoHideBar2(HWND hwnd, BOOL fAutoHide, UINT uEdge)
|
|
{
|
|
HWND hwndAutoHide = _aHwndAutoHide[uEdge];
|
|
if (!IsWindow(hwndAutoHide))
|
|
{
|
|
_aHwndAutoHide[uEdge] = NULL;
|
|
}
|
|
|
|
if (fAutoHide)
|
|
{
|
|
// register
|
|
if (!_aHwndAutoHide[uEdge])
|
|
{
|
|
_aHwndAutoHide[uEdge] = hwnd;
|
|
}
|
|
|
|
return _aHwndAutoHide[uEdge] == hwnd;
|
|
}
|
|
else
|
|
{
|
|
// unregister
|
|
if (_aHwndAutoHide[uEdge] == hwnd)
|
|
{
|
|
_aHwndAutoHide[uEdge] = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL CTray::_AppBarSetAutoHideBar(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
UINT uEdge = ptabd->abd.uEdge;
|
|
if (uEdge >= ABE_MAX)
|
|
return FALSE;
|
|
else {
|
|
return _AppBarSetAutoHideBar2(GetABDHWnd(&ptabd->abd), BOOLFROMPTR(ptabd->abd.lParam), uEdge);
|
|
}
|
|
}
|
|
|
|
void CTray::_AppBarActivationChange2(HWND hwnd, UINT uEdge)
|
|
{
|
|
//
|
|
// FEATURE: make this multi-monitor cool
|
|
//
|
|
HWND hwndAutoHide = _AppBarGetAutoHideBar(uEdge);
|
|
|
|
if (hwndAutoHide && (hwndAutoHide != hwnd))
|
|
{
|
|
//
|
|
// the _AppBar got this notification inside a SendMessage from USER
|
|
// and is now in a SendMessage to us. don't try to do a SetWindowPos
|
|
// right now...
|
|
//
|
|
PostMessage(_hwnd, TM_BRINGTOTOP, (WPARAM)hwndAutoHide, uEdge);
|
|
}
|
|
}
|
|
|
|
void CTray::_AppBarActivationChange(PTRAYAPPBARDATA ptabd)
|
|
{
|
|
PAPPBAR pab = _FindAppBar(GetABDHWnd(&ptabd->abd));
|
|
if (pab)
|
|
{
|
|
// if this is an autohide bar and they're claiming to be on an edge not the same as their autohide edge,
|
|
// we don't do any activation of other autohides
|
|
for (UINT i = 0; i < ABE_MAX; i++)
|
|
{
|
|
if (_aHwndAutoHide[i] == GetABDHWnd(&ptabd->abd) &&
|
|
i != pab->uEdge)
|
|
return;
|
|
}
|
|
_AppBarActivationChange2(GetABDHWnd(&ptabd->abd), pab->uEdge);
|
|
}
|
|
}
|
|
|
|
LRESULT CTray::_OnAppBarMessage(PCOPYDATASTRUCT pcds)
|
|
{
|
|
PTRAYAPPBARDATA ptabd = (PTRAYAPPBARDATA)pcds->lpData;
|
|
|
|
ASSERT(pcds->cbData == sizeof(TRAYAPPBARDATA));
|
|
ASSERT(ptabd->abd.cbSize == sizeof(APPBARDATA3264));
|
|
|
|
switch (ptabd->dwMessage) {
|
|
case ABM_NEW:
|
|
return _AppBarNew(ptabd);
|
|
|
|
case ABM_REMOVE:
|
|
_AppBarRemove(ptabd);
|
|
break;
|
|
|
|
case ABM_QUERYPOS:
|
|
_AppBarQueryPos(ptabd);
|
|
break;
|
|
|
|
case ABM_SETPOS:
|
|
_AppBarSetPos(ptabd);
|
|
break;
|
|
|
|
case ABM_GETSTATE:
|
|
return _desktray.AppBarGetState();
|
|
|
|
case ABM_SETSTATE:
|
|
_AppBarSetState((UINT)ptabd->abd.lParam);
|
|
break;
|
|
|
|
case ABM_GETTASKBARPOS:
|
|
_AppBarGetTaskBarPos(ptabd);
|
|
break;
|
|
|
|
case ABM_WINDOWPOSCHANGED:
|
|
case ABM_ACTIVATE:
|
|
_AppBarActivationChange(ptabd);
|
|
break;
|
|
|
|
case ABM_GETAUTOHIDEBAR:
|
|
return (LRESULT)_AppBarGetAutoHideBar(ptabd->abd.uEdge);
|
|
|
|
case ABM_SETAUTOHIDEBAR:
|
|
return _AppBarSetAutoHideBar(ptabd);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// EA486701-7F92-11cf-9E05-444553540000
|
|
const GUID CLSID_HIJACKINPROC = {0xEA486701, 0x7F92, 0x11cf, 0x9E, 0x05, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00};
|
|
|
|
HRESULT CTray::_LoadInProc(PCOPYDATASTRUCT pcds)
|
|
{
|
|
ASSERT(pcds->cbData == sizeof(LOADINPROCDATA));
|
|
|
|
PLOADINPROCDATA plipd = (PLOADINPROCDATA)pcds->lpData;
|
|
|
|
// Hack to allow us to kill W95 shell extensions that do reall hacky things that
|
|
// we can not support. In this case Hijack pro
|
|
if (IsEqualIID(plipd->clsid, CLSID_HIJACKINPROC))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return _ssomgr.EnableObject(&plipd->clsid, plipd->dwFlags);
|
|
}
|
|
|
|
// Allow the trays global hotkeys to be disabled for a while.
|
|
LRESULT CTray::_SetHotkeyEnable(HWND hwnd, BOOL fEnable)
|
|
{
|
|
_fGlobalHotkeyDisable = !fEnable;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL IsPosInHwnd(LPARAM lParam, HWND hwnd)
|
|
{
|
|
RECT r1;
|
|
POINT pt;
|
|
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
GetWindowRect(hwnd, &r1);
|
|
return PtInRect(&r1, pt);
|
|
}
|
|
|
|
void CTray::_HandleWindowPosChanging(LPWINDOWPOS lpwp)
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc"));
|
|
|
|
if (_uMoveStuckPlace != (UINT)-1)
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc handling pending move"));
|
|
_DoneMoving(lpwp);
|
|
}
|
|
else if (_fSysSizing || !_fSelfSizing)
|
|
{
|
|
RECT rc;
|
|
|
|
if (_fSysSizing)
|
|
{
|
|
GetWindowRect(_hwnd, &rc);
|
|
if (!(lpwp->flags & SWP_NOMOVE))
|
|
{
|
|
rc.left = lpwp->x;
|
|
rc.top = lpwp->y;
|
|
}
|
|
if (!(lpwp->flags & SWP_NOSIZE))
|
|
{
|
|
rc.right = rc.left + lpwp->cx;
|
|
rc.bottom = rc.top + lpwp->cy;
|
|
}
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc sys sizing to rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
_uStuckPlace = _RecalcStuckPos(&rc);
|
|
_UpdateVertical(_uStuckPlace);
|
|
}
|
|
|
|
_GetDockedRect(&rc, _fSysSizing);
|
|
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc using rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
lpwp->x = rc.left;
|
|
lpwp->y = rc.top;
|
|
lpwp->cx = RECTWIDTH(rc);
|
|
lpwp->cy = RECTHEIGHT(rc);
|
|
|
|
lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
|
|
}
|
|
|
|
lpwp->flags |= SWP_FRAMECHANGED;
|
|
}
|
|
|
|
void CTray::_HandlePowerStatus(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fResetDisplay = FALSE;
|
|
|
|
//
|
|
// always reset the display when the machine wakes up from a
|
|
// suspend. NOTE: we don't need this for a standby suspend.
|
|
//
|
|
// a critical resume does not generate a WM_POWERBROADCAST
|
|
// to windows for some reason, but it does generate an old
|
|
// WM_POWER message.
|
|
//
|
|
switch (uMsg)
|
|
{
|
|
case WM_POWER:
|
|
fResetDisplay = (wParam == PWR_CRITICALRESUME);
|
|
break;
|
|
|
|
case WM_POWERBROADCAST:
|
|
switch (wParam)
|
|
{
|
|
case PBT_APMRESUMECRITICAL:
|
|
fResetDisplay = TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fResetDisplay)
|
|
ChangeDisplaySettings(NULL, CDS_RESET);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
//
|
|
// This function checks whether we need to run the cleaner
|
|
// We will not run if user is guest, user has forced us not to, or if the requisite
|
|
// number of days have not yet elapsed
|
|
//
|
|
// We execute a great deal of code to decide whether to run or not that logically should be
|
|
// in fldrclnr.dll, but we execute it here so that we don't have to load fldrclnr.dll unless
|
|
// we absolutely have to, since we execute this path on every logon of explorer.exe
|
|
//
|
|
|
|
#define REGSTR_PATH_CLEANUPWIZ REGSTR_PATH_EXPLORER TEXT("\\Desktop\\CleanupWiz")
|
|
#define REGSTR_OEM_PATH REGSTR_PATH_SETUP TEXT("\\OemStartMenuData")
|
|
#define REGSTR_VAL_TIME TEXT("Last used time")
|
|
#define REGSTR_VAL_DELTA_DAYS TEXT("Days between clean up")
|
|
#define REGSTR_VAL_DONTRUN TEXT("NoRun")
|
|
#define REGSTR_OEM_SEVENDAY_DISABLE TEXT("OemDesktopCleanupDisable")
|
|
|
|
//
|
|
// iDays can be negative or positive, indicating time in the past or future
|
|
//
|
|
//
|
|
#define FTsPerDayOver1000 (10000*60*60*24) // we've got (1000 x 10,000) 100ns intervals per second
|
|
|
|
void CTray::_DesktopCleanup_GetFileTimeNDaysFromGivenTime(const FILETIME *pftGiven, FILETIME * pftReturn, int iDays)
|
|
{
|
|
__int64 i64 = *((__int64 *) pftGiven);
|
|
i64 += Int32x32To64(iDays*1000,FTsPerDayOver1000);
|
|
|
|
*pftReturn = *((FILETIME *) &i64);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
BOOL CTray::_DesktopCleanup_ShouldRun()
|
|
{
|
|
BOOL fRetVal = FALSE;
|
|
|
|
if (!IsOS(OS_ANYSERVER) &&
|
|
_fIsDesktopConnected &&
|
|
!SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS) &&
|
|
!SHRestricted(REST_NODESKTOPCLEANUP))
|
|
{
|
|
fRetVal = TRUE;
|
|
|
|
FILETIME ftNow, ftLast;
|
|
SYSTEMTIME st;
|
|
GetLocalTime(&st);
|
|
SystemTimeToFileTime(&st, &ftNow);
|
|
|
|
DWORD cb = sizeof(ftLast);
|
|
DWORD dwData;
|
|
if (ERROR_SUCCESS != SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_TIME,
|
|
NULL, &ftLast, &cb, FALSE, NULL, 0))
|
|
{
|
|
cb = sizeof(dwData);
|
|
if ((ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_OEM_PATH, REGSTR_OEM_SEVENDAY_DISABLE, NULL, &dwData, &cb)) &&
|
|
(dwData != 0))
|
|
{
|
|
_DesktopCleanup_GetFileTimeNDaysFromGivenTime(&ftNow, &ftLast, -53); // to get the timer to kick in 7 days from now, set last to be 53 days ago
|
|
}
|
|
else
|
|
{
|
|
ftLast = ftNow;
|
|
}
|
|
SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_TIME, NULL, &ftLast, sizeof(ftLast), SHREGSET_FORCE_HKCU);
|
|
}
|
|
|
|
HUSKEY hkey = NULL;
|
|
if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_CLEANUPWIZ, KEY_READ, NULL, &hkey, FALSE))
|
|
{
|
|
//
|
|
// if we're in normal mode and the DONT RUN flag is set, we return immediately
|
|
// (the user checked the "don't run automatically" box)
|
|
//
|
|
cb = sizeof (DWORD);
|
|
if ((ERROR_SUCCESS == SHRegQueryUSValue(hkey, REGSTR_VAL_DONTRUN, NULL, &dwData, &cb, FALSE, NULL, 0)) &&
|
|
(dwData != 0))
|
|
{
|
|
fRetVal = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we need to figure out if if we are within the (last run time + delta days)
|
|
// time period
|
|
//
|
|
int iDays = 60;
|
|
if (ERROR_SUCCESS == (SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_DELTA_DAYS,
|
|
NULL, &dwData, &cb,FALSE, NULL, 0)))
|
|
{
|
|
iDays = dwData;
|
|
}
|
|
|
|
// if (iDays == 0), run every time!
|
|
if (iDays > 0)
|
|
{
|
|
FILETIME ftRange;
|
|
|
|
_DesktopCleanup_GetFileTimeNDaysFromGivenTime(&ftLast, &ftRange, iDays);
|
|
if (!(CompareFileTime(&ftNow, &ftRange) > 0))
|
|
{
|
|
fRetVal = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
SHRegCloseUSKey(hkey);
|
|
}
|
|
}
|
|
|
|
return fRetVal;
|
|
}
|
|
|
|
void CTray::_CheckDesktopCleanup()
|
|
{
|
|
if (_DesktopCleanup_ShouldRun())
|
|
{
|
|
PROCESS_INFORMATION pi = {0};
|
|
TCHAR szRunDll32[MAX_PATH];
|
|
|
|
GetSystemDirectory(szRunDll32, ARRAYSIZE(szRunDll32));
|
|
PathAppend(szRunDll32, TEXT("rundll32.exe"));
|
|
|
|
if (CreateProcessWithArgs(szRunDll32, TEXT("fldrclnr.dll,Wizard_RunDLL"), NULL, &pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Try tacking a 1, 2, 3 or whatever on to a file or
|
|
// directory name until it is unique. When on a file,
|
|
// stick it before the extension.
|
|
//
|
|
void _MakeBetterUniqueName(LPTSTR pszPathName, int cchPathName)
|
|
{
|
|
TCHAR szNewPath[MAX_PATH];
|
|
int i = 1;
|
|
|
|
if (PathIsDirectory(pszPathName))
|
|
{
|
|
do
|
|
{
|
|
StringCchPrintf(szNewPath, ARRAYSIZE(szNewPath), TEXT("%s%d"), pszPathName, i++);
|
|
} while (-1 != GetFileAttributes(szNewPath));
|
|
|
|
StringCchCopy(pszPathName, cchPathName, szNewPath);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szExt[MAX_PATH];
|
|
LPTSTR pszExt;
|
|
|
|
pszExt = PathFindExtension(pszPathName);
|
|
if (pszExt)
|
|
{
|
|
StringCchCopy(szExt, ARRAYSIZE(szExt), pszExt);
|
|
*pszExt = 0;
|
|
|
|
do
|
|
{
|
|
StringCchPrintf(szNewPath, ARRAYSIZE(szNewPath), TEXT("%s%d%s"), pszPathName, i++,szExt);
|
|
} while (-1 != GetFileAttributes(szNewPath));
|
|
|
|
StringCchCopy(pszPathName, cchPathName, szNewPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL_PTR WINAPI CTray::RogueProgramFileDlgProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TCHAR szBuffer[MAX_PATH*2];
|
|
TCHAR szBuffer2[MAX_PATH*2];
|
|
static TCHAR szBetterPath[MAX_PATH];
|
|
static TCHAR *pszPath = NULL;
|
|
|
|
switch (iMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
pszPath = (TCHAR *)lParam;
|
|
StringCchCopy(szBetterPath, ARRAYSIZE(szBetterPath), pszPath);
|
|
_MakeBetterUniqueName(szBetterPath, ARRAYSIZE(szBetterPath));
|
|
SendDlgItemMessage(hWnd, IDC_MSG, WM_GETTEXT, (WPARAM)(MAX_PATH*2), (LPARAM)szBuffer);
|
|
StringCchPrintf(szBuffer2, ARRAYSIZE(szBuffer2), szBuffer, pszPath, szBetterPath);
|
|
SendDlgItemMessage(hWnd, IDC_MSG, WM_SETTEXT, (WPARAM)0, (LPARAM)szBuffer2);
|
|
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_RENAME:
|
|
//rename and fall through
|
|
if (pszPath)
|
|
{
|
|
MoveFile(pszPath, szBetterPath);
|
|
}
|
|
EndDialog(hWnd, IDC_RENAME);
|
|
return TRUE;
|
|
|
|
case IDIGNORE:
|
|
EndDialog(hWnd, IDIGNORE);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check to see if there are any files or folders that could interfere
|
|
// with the fact that Program Files has a space in it.
|
|
//
|
|
// An example would be a directory called: "C:\Program" or a file called"C:\Program.exe".
|
|
//
|
|
// This can prevent apps that dont quote strings in the registry or call CreateProcess with
|
|
// unquoted strings from working properly since CreateProcess wont know what the real exe is.
|
|
//
|
|
void CTray::_CheckForRogueProgramFile()
|
|
{
|
|
TCHAR szProgramFilesPath[MAX_PATH];
|
|
TCHAR szProgramFilesShortName[MAX_PATH];
|
|
|
|
if (SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS)
|
|
&& S_OK == SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szProgramFilesPath))
|
|
{
|
|
LPTSTR pszRoguePattern;
|
|
int cchRoguePattern;
|
|
|
|
pszRoguePattern = StrChr(szProgramFilesPath, TEXT(' '));
|
|
cchRoguePattern = ARRAYSIZE(szProgramFilesPath) - (pszRoguePattern - szProgramFilesPath);
|
|
|
|
if (pszRoguePattern)
|
|
{
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA wfd;
|
|
|
|
// Remember short name for folder name comparison below
|
|
*pszRoguePattern = TEXT('\0');
|
|
StringCchCopy(szProgramFilesShortName, ARRAYSIZE(szProgramFilesShortName), szProgramFilesPath);
|
|
|
|
// turn "C:\program files" into "C:\program.*"
|
|
StringCchCopy(pszRoguePattern, cchRoguePattern, TEXT(".*"));
|
|
pszRoguePattern = szProgramFilesPath;
|
|
|
|
hFind = FindFirstFile(pszRoguePattern, &wfd);
|
|
|
|
while (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
int iRet = 0;
|
|
TCHAR szRogueFileName[MAX_PATH];
|
|
|
|
// we found a file (eg "c:\Program.txt")
|
|
StringCchCopy(szRogueFileName, ARRAYSIZE(szRogueFileName), pszRoguePattern);
|
|
PathRemoveFileSpec(szRogueFileName);
|
|
StringCchCat(szRogueFileName, ARRAYSIZE(szRogueFileName), wfd.cFileName);
|
|
|
|
// don't worry about folders unless they are called "Program"
|
|
if (!((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
lstrcmpi(szProgramFilesShortName, szRogueFileName) != 0))
|
|
{
|
|
|
|
iRet = SHMessageBoxCheckEx(GetDesktopWindow(),
|
|
hinstCabinet,
|
|
MAKEINTRESOURCE(DLG_PROGRAMFILECONFLICT),
|
|
RogueProgramFileDlgProc,
|
|
(void *)szRogueFileName,
|
|
IDIGNORE,
|
|
TEXT("RogueProgramName"));
|
|
}
|
|
|
|
if ((iRet == IDIGNORE) || !FindNextFile(hFind, &wfd))
|
|
{
|
|
// user hit ignore or we are done, so don't keep going
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(hFind);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_OnWaitCursorNotify(LPNMHDR pnm)
|
|
{
|
|
_iWaitCount += (pnm->code == NM_STARTWAIT ? 1 :-1);
|
|
ASSERT(_iWaitCount >= 0);
|
|
// Don't let it go negative or we'll never get rid of it.
|
|
if (_iWaitCount < 0)
|
|
_iWaitCount = 0;
|
|
// what we really want is for user to simulate a mouse move/setcursor
|
|
SetCursor(LoadCursor(NULL, _iWaitCount ? IDC_APPSTARTING : IDC_ARROW));
|
|
}
|
|
|
|
void CTray::_HandlePrivateCommand(LPARAM lParam)
|
|
{
|
|
LPSTR psz = (LPSTR) lParam; // lParam always ansi.
|
|
|
|
if (!lstrcmpiA(psz, "ToggleDesktop"))
|
|
{
|
|
_RaiseDesktop(!g_fDesktopRaised, TRUE);
|
|
}
|
|
else if (!lstrcmpiA(psz, "Explorer"))
|
|
{
|
|
// Fast way to bring up explorer window on root of
|
|
// windows dir.
|
|
SHELLEXECUTEINFO shei = {0};
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
if (GetWindowsDirectory(szPath, ARRAYSIZE(szPath)) != 0)
|
|
{
|
|
PathStripToRoot(szPath);
|
|
|
|
shei.lpIDList = ILCreateFromPath(szPath);
|
|
if (shei.lpIDList)
|
|
{
|
|
shei.cbSize = sizeof(shei);
|
|
shei.fMask = SEE_MASK_IDLIST;
|
|
shei.nShow = SW_SHOWNORMAL;
|
|
shei.lpVerb = TEXT("explore");
|
|
ShellExecuteEx(&shei);
|
|
ILFree((LPITEMIDLIST)shei.lpIDList);
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(psz);
|
|
}
|
|
|
|
//***
|
|
//
|
|
void CTray::_OnFocusMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fActivate = (BOOL) wParam;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case TM_UIACTIVATEIO:
|
|
{
|
|
#ifdef DEBUG
|
|
{
|
|
int dtb = (int) lParam;
|
|
|
|
TraceMsg(DM_FOCUS, "tiois: TM_UIActIO fAct=%d dtb=%d", fActivate, dtb);
|
|
|
|
ASSERT(dtb == 1 || dtb == -1);
|
|
}
|
|
#endif
|
|
|
|
if (fActivate)
|
|
{
|
|
// Since we are tabbing into the tray, turn the focus rect on.
|
|
SendMessage(_hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
|
|
UISF_HIDEFOCUS), 0);
|
|
|
|
SendMessage(v_hwndDesktop, DTM_ONFOCUSCHANGEIS, TRUE, (LPARAM) _hwnd);
|
|
SetForegroundWindow(_hwnd);
|
|
|
|
// fake an IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
|
|
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
|
{
|
|
_SetFocus(_hwndNotify);
|
|
}
|
|
else
|
|
{
|
|
_SetFocus(_hwndStart);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ldeact:
|
|
IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
|
|
SetForegroundWindow(v_hwndDesktop);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TM_ONFOCUSCHANGEIS:
|
|
{
|
|
HWND hwnd = (HWND) lParam;
|
|
|
|
TraceMsg(DM_FOCUS, "tiois: TM_OnFocChgIS hwnd=%x fAct=%d", hwnd, fActivate);
|
|
|
|
if (fActivate)
|
|
{
|
|
// someone else is activating, so we need to deactivate
|
|
goto Ldeact;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#define TSVC_NTIMER (IDT_SERVICELAST - IDT_SERVICE0 + 1)
|
|
|
|
struct {
|
|
#ifdef DEBUG
|
|
UINT_PTR idtWin;
|
|
#endif
|
|
TIMERPROC pfnSvc;
|
|
} g_timerService[TSVC_NTIMER];
|
|
|
|
#define TSVC_IDToIndex(id) ((id) - IDT_SERVICE0)
|
|
#define TSVC_IndexToID(i) ((i) + IDT_SERVICE0)
|
|
|
|
int CTray::_OnTimerService(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int i;
|
|
UINT_PTR idt;
|
|
TIMERPROC pfn;
|
|
BOOL b;
|
|
|
|
switch (uMsg) {
|
|
case TM_SETTIMER:
|
|
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: wP=0x%x lP=%x", wParam, lParam);
|
|
ASSERT(IS_VALID_CODE_PTR(lParam, TIMERPROC));
|
|
for (i = 0; i < TSVC_NTIMER; i++) {
|
|
if (g_timerService[i].pfnSvc == 0) {
|
|
g_timerService[i].pfnSvc = (TIMERPROC)lParam;
|
|
idt = SetTimer(_hwnd, TSVC_IndexToID(i), (UINT)wParam, 0);
|
|
if (idt == 0) {
|
|
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ST()=%d (!)", idt);
|
|
break;
|
|
}
|
|
ASSERT(idt == (UINT_PTR)TSVC_IndexToID(i));
|
|
DBEXEC(TRUE, (g_timerService[i].idtWin = idt));
|
|
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0x%x", TSVC_IndexToID(i));
|
|
return TSVC_IndexToID(i); // idtWin
|
|
}
|
|
}
|
|
TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0 (!)");
|
|
return 0;
|
|
|
|
case TM_KILLTIMER: // lP=idtWin
|
|
TraceMsg(DM_UEMTRACE, "e.TM_KILLTIMER: wP=0x%x lP=%x", wParam, lParam);
|
|
if (EVAL(IDT_SERVICE0 <= lParam && lParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
|
|
i = (int)TSVC_IDToIndex(lParam);
|
|
if (g_timerService[i].pfnSvc) {
|
|
ASSERT(g_timerService[i].idtWin == (UINT)lParam);
|
|
b = KillTimer(_hwnd, lParam);
|
|
ASSERT(b);
|
|
g_timerService[i].pfnSvc = 0;
|
|
DBEXEC(TRUE, (g_timerService[i].idtWin = 0));
|
|
return TRUE;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case WM_TIMER: // wP=idtWin lP=0
|
|
TraceMsg(DM_UEMTRACE, "e.TM_TIMER: wP=0x%x lP=%x", wParam, lParam);
|
|
if (EVAL(IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
|
|
i = (int)TSVC_IDToIndex(wParam);
|
|
pfn = g_timerService[i].pfnSvc;
|
|
if (EVAL(IS_VALID_CODE_PTR(pfn, TIMERPROC)))
|
|
(*pfn)(_hwnd, WM_TIMER, wParam, GetTickCount());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ASSERT(0); /*NOTREACHED*/
|
|
return 0;
|
|
}
|
|
|
|
void CTray::RealityCheck()
|
|
{
|
|
//
|
|
// Make sure that the tray's actual z-order position agrees with what we think
|
|
// it is. We need to do this because there's a recurring bug where the tray
|
|
// gets bumped out of TOPMOST position. (Lots of things, like a tray-owned
|
|
// window moving itself to non-TOPMOST or a random app messing with the tray
|
|
// window position, can cause this.)
|
|
//
|
|
_ResetZorder();
|
|
}
|
|
|
|
#define DELAY_STARTUPTROUBLESHOOT (15 * 1000)
|
|
|
|
void CTray::LogFailedStartupApp()
|
|
{
|
|
if (_hwnd)
|
|
{
|
|
PostMessage(_hwnd, TM_HANDLESTARTUPFAILED, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
_fEarlyStartupFailure = TRUE;
|
|
}
|
|
}
|
|
|
|
void WINAPI CTray::TroubleShootStartupCB(HWND hwnd, UINT uMsg, UINT_PTR idTimer, DWORD dwTime)
|
|
{
|
|
KillTimer(hwnd, idTimer);
|
|
|
|
if (!c_tray._fStartupTroubleshooterLaunched)
|
|
{
|
|
TCHAR szCmdLine[MAX_PATH];
|
|
DWORD cb;
|
|
|
|
c_tray._fStartupTroubleshooterLaunched = TRUE;
|
|
cb = sizeof(szCmdLine);
|
|
if (SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER,
|
|
TEXT("StartupTroubleshoot"), NULL,
|
|
szCmdLine, &cb) == ERROR_SUCCESS)
|
|
{
|
|
ShellExecuteRegApp(szCmdLine, RRA_NOUI | RRA_DELETE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_OnHandleStartupFailed()
|
|
{
|
|
/*
|
|
* Don't launch the troubleshooter until we have gone
|
|
* DELAY_STARTUPTROUBLESHOOT milliseconds without a startup problem.
|
|
* This gives time for the system to settle down before starting to
|
|
* annoy the user all over again.
|
|
*
|
|
* (And, of course, don't launch it more than once.)
|
|
*/
|
|
if (!_fStartupTroubleshooterLaunched)
|
|
{
|
|
SetTimer(_hwnd, IDT_STARTUPFAILED, DELAY_STARTUPTROUBLESHOOT, TroubleShootStartupCB);
|
|
}
|
|
}
|
|
|
|
void CTray::_HandleDelayBootStuff()
|
|
{
|
|
// This posted message is the last one processed by the primary
|
|
// thread (tray thread) when we boot. At this point we will
|
|
// want to load the shell services (which usually create threads)
|
|
// and resume both the background start menu thread and the fs_notfiy
|
|
// thread.
|
|
|
|
if (!_fHandledDelayBootStuff)
|
|
{
|
|
if (GetShellWindow() == NULL)
|
|
{
|
|
// The desktop browser hasn't finished navigating yet.
|
|
SetTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF, 3 * 1000, NULL);
|
|
return;
|
|
}
|
|
|
|
_fHandledDelayBootStuff = TRUE;
|
|
|
|
if (g_dwStopWatchMode)
|
|
{
|
|
StopWatch_StartTimed(SWID_STARTUP, TEXT("_DelayedBootStuff"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
|
|
}
|
|
|
|
PostMessage(_hwnd, TM_SHELLSERVICEOBJECTS, 0, 0);
|
|
|
|
BandSite_HandleDelayBootStuff(_ptbs);
|
|
|
|
//check to see if there are any files or folders that could interfere
|
|
//with the fact that Program Files has a space in it. An example would
|
|
//be a folder called "C:\Program" or a file called "C:\Program.exe"
|
|
_CheckForRogueProgramFile();
|
|
|
|
// Create a named event and fire it so that the services can
|
|
// go to work, reducing contention during boot.
|
|
_hShellReadyEvent = CreateEvent(0, TRUE, TRUE, TEXT("ShellReadyEvent"));
|
|
if (_hShellReadyEvent)
|
|
{
|
|
// Set the event in case it was already created and our "create
|
|
// signaled" parameter to CreateEvent got ignored.
|
|
SetEvent(_hShellReadyEvent);
|
|
}
|
|
|
|
// Check whether we should launch Desktop Cleanup Wizard
|
|
_CheckDesktopCleanup();
|
|
|
|
TBOOL(WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd, NOTIFY_FOR_THIS_SESSION));
|
|
|
|
if (g_dwStopWatchMode)
|
|
{
|
|
StopWatch_StopTimed(SWID_STARTUP, TEXT("_DelayedBootStuff"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
LRESULT CTray::_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case DBT_CONFIGCHANGED:
|
|
// We got an update. Refresh.
|
|
_RefreshStartMenu();
|
|
break;
|
|
|
|
case DBT_QUERYCHANGECONFIG:
|
|
//
|
|
// change to registry settings
|
|
//
|
|
ChangeDisplaySettings(NULL, 0);
|
|
break;
|
|
|
|
case DBT_MONITORCHANGE:
|
|
//
|
|
// handle monitor change
|
|
//
|
|
HandleDisplayChange(LOWORD(lParam), HIWORD(lParam), TRUE);
|
|
break;
|
|
|
|
case DBT_CONFIGCHANGECANCELED:
|
|
//
|
|
// if the config change was canceled go back
|
|
//
|
|
HandleDisplayChange(0, 0, FALSE);
|
|
break;
|
|
}
|
|
|
|
Mixer_DeviceChange(wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// The "resizable" edge of the taskbar is the edge adjacent to
|
|
// the desktop, i.e. opposite the stuck place.
|
|
//
|
|
// returns HTXXX if on a resizable edge, else HTBORDER
|
|
//
|
|
DWORD CTray::_PtOnResizableEdge(POINT pt, LPRECT prcClient)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(_hwnd, &rc);
|
|
|
|
DWORD dwHit = HTBORDER;
|
|
|
|
switch (_uStuckPlace)
|
|
{
|
|
case STICK_LEFT: rc.left = prcClient->right; dwHit = HTRIGHT; break;
|
|
case STICK_TOP: rc.top = prcClient->bottom; dwHit = HTBOTTOM; break;
|
|
case STICK_RIGHT: rc.right = prcClient->left; dwHit = HTLEFT; break;
|
|
case STICK_BOTTOM: rc.bottom = prcClient->top; dwHit = HTTOP; break;
|
|
}
|
|
|
|
return PtInRect(&rc, pt) ? dwHit : HTBORDER;
|
|
}
|
|
|
|
//
|
|
// _OnFactoryMessage
|
|
//
|
|
// The OPK "factory.exe" tool sends us this message to tell us that
|
|
// it has dorked some setting or other and we should refresh so the
|
|
// OEM can see the effect immediately and feel confident that it
|
|
// actually worked. This is not technically necessary but it cuts
|
|
// down on OEM support calls when they ask us why their setting didn't
|
|
// work. (It did work, they just have to log off and back on to see
|
|
// it.)
|
|
//
|
|
|
|
int CTray::_OnFactoryMessage(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case 0: // FACTORY_OEMLINK: factory.exe has dorked the OEM link
|
|
ClosePopupMenus();
|
|
_BuildStartMenu(); // Force a rebuild
|
|
return 1;
|
|
|
|
case 1: // FACTORY_MFU: factory.exe has written a new MFU
|
|
HandleFirstTime(); // Rebuild the default MFU
|
|
ClosePopupMenus();
|
|
_BuildStartMenu(); // Force a rebuild
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define CX_OFFSET g_cxEdge
|
|
#define CY_OFFSET g_cyEdge
|
|
|
|
//
|
|
// _MapNCToClient
|
|
//
|
|
// see comments in _TryForwardNCToClient
|
|
//
|
|
BOOL CTray::_MapNCToClient(LPARAM* plParam)
|
|
{
|
|
POINT pt = { GET_X_LPARAM(*plParam), GET_Y_LPARAM(*plParam) };
|
|
|
|
RECT rcClient;
|
|
GetClientRect(_hwnd, &rcClient);
|
|
MapWindowPoints(_hwnd, NULL, (LPPOINT)&rcClient, 2);
|
|
|
|
//
|
|
// point must be outside the client area and not on the
|
|
// resizable edge of the taskbar
|
|
//
|
|
|
|
if (!PtInRect(&rcClient, pt) && _PtOnResizableEdge(pt, &rcClient) == HTBORDER)
|
|
{
|
|
//
|
|
// fudge it over onto the client edge and return TRUE
|
|
//
|
|
if (pt.x < rcClient.left)
|
|
pt.x = rcClient.left + CX_OFFSET;
|
|
else if (pt.x > rcClient.right)
|
|
pt.x = rcClient.right - CX_OFFSET;
|
|
|
|
if (pt.y < rcClient.top)
|
|
pt.y = rcClient.top + CY_OFFSET;
|
|
else if (pt.y > rcClient.bottom)
|
|
pt.y = rcClient.bottom - CY_OFFSET;
|
|
|
|
*plParam = MAKELONG(pt.x, pt.y);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// didn't pass the test. leave the point alone and return FALSE.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
HWND _TopChildWindowFromPoint(HWND hwnd, POINT pt)
|
|
{
|
|
HWND hwndLast = NULL;
|
|
hwnd = ChildWindowFromPoint(hwnd, pt);
|
|
while (hwnd && hwnd != hwndLast)
|
|
{
|
|
hwndLast = hwnd;
|
|
hwnd = ChildWindowFromPoint(hwnd, pt);
|
|
}
|
|
return hwndLast;
|
|
}
|
|
|
|
//
|
|
// _TryForwardNCToClient
|
|
//
|
|
// Hack! This exists to solve a usability problem. When you slam your
|
|
// mouse into the bottom corner of the screen and click, we want that to
|
|
// activate the start button. Similarly, when you slam your mouse below
|
|
// a Quick Launch button or task button and click, we want that to
|
|
// activate the button.
|
|
//
|
|
// We hack this by remapping the coordinate of NC mouse messages and
|
|
// manually forwarding to the appropriate window. We only do this for
|
|
// clicks on the edges non-resizable edge of the taskbar.
|
|
//
|
|
// We also warp the mouse cursor over to the new position. This is needed
|
|
// because e.g. if toolbar is the client window we're forwarding
|
|
// to, it will set capture and receive subsequent mouse messages. (And
|
|
// once it gets a mouse message outside the window, it will deselect the
|
|
// button and so the button won't get activated.)
|
|
//
|
|
// _MapNCToClient has the rules for figuring out if the click is on
|
|
// one of the edges we want and for remapping the coordinate into the
|
|
// client area.
|
|
//
|
|
BOOL CTray::_TryForwardNCToClient(UINT uMsg, LPARAM lParam)
|
|
{
|
|
if (_MapNCToClient(&lParam))
|
|
{
|
|
// see if this is over one of our windows
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
MapWindowPoints(NULL, _hwnd, &pt, 1);
|
|
HWND hwnd = _TopChildWindowFromPoint(_hwnd, pt);
|
|
|
|
if (hwnd)
|
|
{
|
|
// warp the mouse cursor to this screen coord
|
|
SetCursorPos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
|
|
// map to window coords
|
|
MapWindowPoints(_hwnd, hwnd, &pt, 1);
|
|
|
|
// set lparam to window coords
|
|
lParam = MAKELONG(pt.x, pt.y);
|
|
|
|
// map to client message
|
|
ASSERT(InRange(uMsg, WM_NCMOUSEFIRST, WM_NCMOUSELAST));
|
|
uMsg += (WM_LBUTTONDOWN - WM_NCLBUTTONDOWN);
|
|
|
|
// forward it
|
|
SendMessage(hwnd, uMsg, 0, lParam);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CTray::CountOfRunningPrograms
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: DWORD
|
|
//
|
|
// Purpose: Iterates the window list. Looks for windows that are visible
|
|
// with a non-zero length window title. Gets that window process
|
|
// ID and keeps the IDs in a list. For each window iterated it
|
|
// checks against the list to see if the process is already
|
|
// tagged and if so doesn't add it again. Finally it returns the
|
|
// count of the unique processes handling open visible windows
|
|
// in the user's desktop.
|
|
//
|
|
// The list is fixed at 1000 processes (using stack space).
|
|
//
|
|
// History: 2000-06-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
static const int MAXIMUM_PROCESS_COUNT = 1000;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwCount;
|
|
DWORD dwProcessIDs[MAXIMUM_PROCESS_COUNT];
|
|
} tProcessIDList;
|
|
|
|
bool FoundProcessID (tProcessIDList *pProcessIDList, DWORD dwProcessID)
|
|
|
|
{
|
|
bool fFound;
|
|
DWORD dwIndex;
|
|
|
|
for (fFound = false, dwIndex = 0; !fFound && (dwIndex < pProcessIDList->dwCount); ++dwIndex)
|
|
{
|
|
fFound = (pProcessIDList->dwProcessIDs[dwIndex] == dwProcessID);
|
|
}
|
|
return(fFound);
|
|
}
|
|
|
|
void AddProcessID (tProcessIDList *pProcessIDList, DWORD dwProcessID)
|
|
|
|
{
|
|
if (pProcessIDList->dwCount < MAXIMUM_PROCESS_COUNT)
|
|
{
|
|
pProcessIDList->dwProcessIDs[pProcessIDList->dwCount++] = dwProcessID;
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK CountOfRunningProgramsEnumWindowsProc (HWND hwnd, LPARAM lParam)
|
|
|
|
{
|
|
if ((GetShellWindow() != hwnd) && IsWindowVisible(hwnd))
|
|
{
|
|
DWORD dwThreadID, dwProcessID;
|
|
TCHAR szWindowTitle[256];
|
|
|
|
dwThreadID = GetWindowThreadProcessId(hwnd, &dwProcessID);
|
|
if ((InternalGetWindowText(hwnd, szWindowTitle, ARRAYSIZE(szWindowTitle)) > 0) &&
|
|
(szWindowTitle[0] != TEXT('\0')))
|
|
{
|
|
if (!FoundProcessID(reinterpret_cast<tProcessIDList*>(lParam), dwProcessID))
|
|
{
|
|
AddProcessID(reinterpret_cast<tProcessIDList*>(lParam), dwProcessID);
|
|
}
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
DWORD CTray::CountOfRunningPrograms()
|
|
{
|
|
tProcessIDList processIDList = {0};
|
|
TBOOL(EnumWindows(CountOfRunningProgramsEnumWindowsProc, reinterpret_cast<LPARAM>(&processIDList)));
|
|
return processIDList.dwCount;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CTray::_OnSessionChange
|
|
//
|
|
// Arguments: wParam = WTS_xxx notification.
|
|
// lParam = WTS_SESSION_NOTIFICATION struct pointer.
|
|
//
|
|
// Returns: LRESULT
|
|
//
|
|
// Purpose: Handles console/remote dis/reconnects.
|
|
//
|
|
// History: 2000-07-12 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
LRESULT CTray::_OnSessionChange(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
ASSERTMSG(DWORD(lParam) == NtCurrentPeb()->SessionId, "Session ID mismatch in CTray::_OnSessionChange");
|
|
|
|
if ((WTS_CONSOLE_CONNECT == wParam) || (WTS_REMOTE_CONNECT == wParam) || (WTS_SESSION_UNLOCK == wParam))
|
|
{
|
|
_fIsDesktopConnected = TRUE;
|
|
}
|
|
else if ((WTS_CONSOLE_DISCONNECT == wParam) || (WTS_REMOTE_DISCONNECT == wParam) || (WTS_SESSION_LOCK == wParam))
|
|
{
|
|
_fIsDesktopConnected = FALSE;
|
|
}
|
|
|
|
if ((WTS_CONSOLE_CONNECT == wParam) || (WTS_REMOTE_CONNECT == wParam))
|
|
{
|
|
_RefreshStartMenu();
|
|
SHUpdateRecycleBinIcon();
|
|
}
|
|
else if ((WTS_SESSION_LOCK == wParam) || (WTS_SESSION_UNLOCK == wParam))
|
|
{
|
|
if (IsOS(OS_FASTUSERSWITCHING))
|
|
{
|
|
if (wParam == WTS_SESSION_LOCK)
|
|
{
|
|
ExplorerPlaySound(TEXT("WindowsLogoff"));
|
|
}
|
|
else if (wParam == WTS_SESSION_UNLOCK)
|
|
{
|
|
ExplorerPlaySound(TEXT("WindowsLogon"));
|
|
}
|
|
}
|
|
|
|
PostMessage(_hwnd, TM_WORKSTATIONLOCKED, (WTS_SESSION_LOCK == wParam), 0);
|
|
}
|
|
else if (WTS_SESSION_REMOTE_CONTROL == wParam)
|
|
{
|
|
// optimization not needed on remote sessions
|
|
if (!GetSystemMetrics(SM_REMOTESESSION)) {
|
|
_BuildStartMenu();
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
LRESULT CTray::_NCPaint(HRGN hrgn)
|
|
{
|
|
ASSERT(_hTheme);
|
|
|
|
if (_fCanSizeMove || _fShowSizingBarAlways)
|
|
{
|
|
if ((INT_PTR)hrgn == 1)
|
|
hrgn = NULL;
|
|
|
|
HDC hdc = GetDCEx( _hwnd, hrgn, DCX_USESTYLE|DCX_WINDOW|DCX_LOCKWINDOWUPDATE|
|
|
((hrgn != NULL) ? DCX_INTERSECTRGN|DCX_NODELETERGN : 0));
|
|
|
|
if (hdc)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(_hwnd, &rc);
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
|
|
_AdjustRectForSizingBar(_uStuckPlace, &rc, 0);
|
|
DrawThemeBackground(_hTheme, hdc, _GetPart(TRUE, _uStuckPlace), 0, &rc, 0);
|
|
ReleaseDC(_hwnd, hdc);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CTray::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static UINT uDDEExec = 0;
|
|
LRESULT lres = 0;
|
|
MSG msg;
|
|
|
|
msg.hwnd = hwnd;
|
|
msg.message = uMsg;
|
|
msg.wParam = wParam;
|
|
msg.lParam = lParam;
|
|
|
|
if (_pmbStartMenu &&
|
|
_pmbStartMenu->TranslateMenuMessage(&msg, &lres) == S_OK)
|
|
return lres;
|
|
|
|
if (_pmbStartPane &&
|
|
_pmbStartPane->TranslateMenuMessage(&msg, &lres) == S_OK)
|
|
return lres;
|
|
|
|
if (_pmbTasks &&
|
|
_pmbTasks->TranslateMenuMessage(&msg, &lres) == S_OK)
|
|
return lres;
|
|
|
|
wParam = msg.wParam;
|
|
lParam = msg.lParam;
|
|
|
|
INSTRUMENT_WNDPROC(SHCNFI_TRAY_WNDPROC, hwnd, uMsg, wParam, lParam);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WMTRAY_REGISTERHOTKEY:
|
|
return _RegisterHotkey(hwnd, (int)wParam);
|
|
|
|
case WMTRAY_UNREGISTERHOTKEY:
|
|
return _UnregisterHotkey(hwnd, (int)wParam);
|
|
|
|
case WMTRAY_SCREGISTERHOTKEY:
|
|
return _ShortcutRegisterHotkey(hwnd, (WORD)wParam, (ATOM)lParam);
|
|
|
|
case WMTRAY_SCUNREGISTERHOTKEY:
|
|
return _ShortcutUnregisterHotkey(hwnd, (WORD)wParam);
|
|
|
|
case WMTRAY_SETHOTKEYENABLE:
|
|
return _SetHotkeyEnable(hwnd, (BOOL)wParam);
|
|
|
|
case WMTRAY_QUERY_MENU:
|
|
return (LRESULT)_hmenuStart;
|
|
|
|
case WMTRAY_QUERY_VIEW:
|
|
return (LRESULT)_hwndTasks;
|
|
|
|
case WMTRAY_TOGGLEQL:
|
|
return _ToggleQL((int)lParam);
|
|
|
|
case WM_COPYDATA:
|
|
// Check for NULL it can happen if user runs out of selectors or memory...
|
|
if (lParam)
|
|
{
|
|
switch (((PCOPYDATASTRUCT)lParam)->dwData) {
|
|
case TCDM_NOTIFY:
|
|
{
|
|
BOOL bRefresh = FALSE;
|
|
|
|
lres = _trayNotify.TrayNotify(_hwndNotify, (HWND)wParam, (PCOPYDATASTRUCT)lParam, &bRefresh);
|
|
if (bRefresh)
|
|
{
|
|
SizeWindows();
|
|
}
|
|
return(lres);
|
|
}
|
|
|
|
case TCDM_APPBAR:
|
|
return _OnAppBarMessage((PCOPYDATASTRUCT)lParam);
|
|
|
|
case TCDM_LOADINPROC:
|
|
return (UINT)_LoadInProc((PCOPYDATASTRUCT)lParam);
|
|
}
|
|
}
|
|
return FALSE;
|
|
|
|
case WM_NCCALCSIZE:
|
|
if (_hTheme)
|
|
{
|
|
if ((_fCanSizeMove || _fShowSizingBarAlways) && lParam)
|
|
{
|
|
_AdjustRectForSizingBar(_uStuckPlace, (LPRECT)lParam, -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
goto L_default;
|
|
}
|
|
break;
|
|
|
|
case WM_NCLBUTTONDBLCLK:
|
|
if (!_TryForwardNCToClient(uMsg, lParam))
|
|
{
|
|
if (IsPosInHwnd(lParam, _hwndNotify))
|
|
{
|
|
_Command(IDM_SETTIME);
|
|
|
|
// Hack! If you click on the tray clock, this tells the tooltip
|
|
// "Hey, I'm using this thing; stop putting up a tip for me."
|
|
// You can get the tooltip to lose track of when it needs to
|
|
// reset the "stop it!" flag and you get stuck in "stop it!" mode.
|
|
// It's particularly easy to make happen on Terminal Server.
|
|
//
|
|
// So let's assume that the only reason people click on the
|
|
// tray clock is to change the time. when they change the time,
|
|
// kick the tooltip in the head to reset the "stop it!" flag.
|
|
|
|
SendMessage(_hwndTrayTips, TTM_POP, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCLBUTTONUP:
|
|
if (!_TryForwardNCToClient(uMsg, lParam))
|
|
{
|
|
goto L_WM_NCMOUSEMOVE;
|
|
}
|
|
break;
|
|
|
|
case WM_NCMOUSEMOVE:
|
|
L_WM_NCMOUSEMOVE:
|
|
if (IsPosInHwnd(lParam, _hwndNotify))
|
|
{
|
|
MSG msgInner;
|
|
msgInner.lParam = lParam;
|
|
msgInner.wParam = wParam;
|
|
msgInner.message = uMsg;
|
|
msgInner.hwnd = hwnd;
|
|
SendMessage(_hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msgInner);
|
|
if (uMsg == WM_NCLBUTTONDOWN)
|
|
_SetFocus(_hwndNotify);
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_CREATE:
|
|
return _OnCreate(hwnd);
|
|
|
|
case WM_DESTROY:
|
|
return _HandleDestroy();
|
|
|
|
#ifdef DEBUG
|
|
case WM_QUERYENDSESSION:
|
|
TraceMsg(DM_SHUTDOWN, "Tray.wp WM_QUERYENDSESSION");
|
|
goto DoDefault;
|
|
#endif
|
|
|
|
case WM_ENDSESSION:
|
|
// save our settings if we are shutting down
|
|
if (wParam)
|
|
{
|
|
if (lParam | ENDSESSION_LOGOFF)
|
|
{
|
|
_fIsLogoff = TRUE;
|
|
_RecomputeAllWorkareas();
|
|
}
|
|
|
|
_SaveTrayAndDesktop();
|
|
|
|
ShowWindow(_hwnd, SW_HIDE);
|
|
ShowWindow(v_hwndDesktop, SW_HIDE);
|
|
|
|
DestroyWindow(_hwnd);
|
|
}
|
|
break;
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
{
|
|
RECT rc;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = (HDC)wParam;
|
|
if (hdc == 0)
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
|
|
if (_hTheme)
|
|
{
|
|
RECT rcClip;
|
|
if (GetClipBox(hdc, &rcClip) == NULLREGION)
|
|
rcClip = rc;
|
|
|
|
DrawThemeBackground(_hTheme, hdc, _GetPart(FALSE, _uStuckPlace), 0, &rc, &rcClip);
|
|
}
|
|
else
|
|
{
|
|
FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE + 1));
|
|
|
|
// draw etched line around on either side of the bandsite
|
|
MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rc, 2);
|
|
InflateRect(&rc, g_cxEdge, g_cyEdge);
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOPLEFT);
|
|
}
|
|
|
|
if (wParam == 0)
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
if (_hTheme)
|
|
{
|
|
if (!_fSkipErase)
|
|
{
|
|
RECT rc;
|
|
GetClientRect(hwnd, &rc);
|
|
DrawThemeBackground(_hTheme, (HDC)wParam, _GetPart(FALSE, _uStuckPlace), 0, &rc, NULL);
|
|
|
|
// Only draw the first time to prevent piece-mail taskbar painting
|
|
_fSkipErase = TRUE;
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
goto DoDefault;
|
|
}
|
|
break;
|
|
|
|
case WM_NCPAINT:
|
|
if (_hTheme)
|
|
{
|
|
return _NCPaint((HRGN)wParam);
|
|
}
|
|
else
|
|
{
|
|
goto DoDefault;
|
|
}
|
|
break;
|
|
|
|
case WM_POWER:
|
|
case WM_POWERBROADCAST:
|
|
_PropagateMessage(hwnd, uMsg, wParam, lParam);
|
|
_HandlePowerStatus(uMsg, wParam, lParam);
|
|
goto DoDefault;
|
|
|
|
case WM_DEVICECHANGE:
|
|
lres = _OnDeviceChange(hwnd, wParam, lParam);
|
|
if (lres == 0)
|
|
{
|
|
goto DoDefault;
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *pnm = (NMHDR*)lParam;
|
|
if (!BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres)) {
|
|
switch (pnm->code)
|
|
{
|
|
case SEN_DDEEXECUTE:
|
|
if (((LPNMHDR)lParam)->idFrom == 0)
|
|
{
|
|
LPNMVIEWFOLDER pnmPost = DDECreatePostNotify((LPNMVIEWFOLDER)pnm);
|
|
|
|
if (pnmPost)
|
|
{
|
|
PostMessage(hwnd, GetDDEExecMsg(), 0, (LPARAM)pnmPost);
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NM_STARTWAIT:
|
|
case NM_ENDWAIT:
|
|
_OnWaitCursorNotify((NMHDR *)lParam);
|
|
PostMessage(v_hwndDesktop, ((NMHDR*)lParam)->code == NM_STARTWAIT ? DTM_STARTWAIT : DTM_ENDWAIT,
|
|
0, 0); // forward it along
|
|
break;
|
|
|
|
case NM_THEMECHANGED:
|
|
// Force the start button to recalc its size
|
|
_sizeStart.cx = 0;
|
|
SizeWindows();
|
|
break;
|
|
|
|
case TTN_NEEDTEXT:
|
|
//
|
|
// Make the clock manage its own tooltip.
|
|
//
|
|
return SendMessage(_GetClockWindow(), WM_NOTIFY, wParam, lParam);
|
|
|
|
case TTN_SHOW:
|
|
SetWindowZorder(_hwndTrayTips, HWND_TOP);
|
|
break;
|
|
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CLOSE:
|
|
_DoExitWindows(v_hwndDesktop);
|
|
break;
|
|
|
|
case WM_NCHITTEST:
|
|
{
|
|
RECT r1;
|
|
POINT pt;
|
|
|
|
GetClientRect(hwnd, &r1);
|
|
MapWindowPoints(hwnd, NULL, (LPPOINT)&r1, 2);
|
|
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
|
|
_SetUnhideTimer(pt.x, pt.y);
|
|
|
|
// If the user can't size or move the taskbar, then just say
|
|
// they hit something useless
|
|
if (!_fCanSizeMove)
|
|
{
|
|
return HTBORDER;
|
|
}
|
|
else if (PtInRect(&r1, pt))
|
|
{
|
|
// allow dragging if mouse is in client area of _hwnd
|
|
return HTCAPTION;
|
|
}
|
|
else
|
|
{
|
|
return _PtOnResizableEdge(pt, &r1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
_HandleWindowPosChanging((LPWINDOWPOS)lParam);
|
|
break;
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_ENTERSIZEMOVE"));
|
|
g_fInSizeMove = TRUE;
|
|
GetCursorPos((LPPOINT)&_rcSizeMoveIgnore);
|
|
_rcSizeMoveIgnore.right = _rcSizeMoveIgnore.left;
|
|
_rcSizeMoveIgnore.bottom = _rcSizeMoveIgnore.top;
|
|
InflateRect(&_rcSizeMoveIgnore, GetSystemMetrics(SM_CXICON),
|
|
GetSystemMetrics(SM_CYICON));
|
|
|
|
//
|
|
// unclip the tray from the current monitor.
|
|
// keeping the tray properly clipped in the MoveSize loop is extremely
|
|
// hairy and provides almost no benefit. we'll re-clip when it lands.
|
|
//
|
|
_ClipWindow(FALSE);
|
|
|
|
// Remember the old monitor we were on
|
|
_hmonOld = _hmonStuck;
|
|
|
|
// set up for WM_MOVING/WM_SIZING messages
|
|
_uMoveStuckPlace = (UINT)-1;
|
|
_fSysSizing = TRUE;
|
|
|
|
if (!g_fDragFullWindows)
|
|
{
|
|
SendMessage(_hwndRebar, WM_SETREDRAW, FALSE, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_EXITSIZEMOVE"));
|
|
|
|
// done sizing
|
|
_fSysSizing = FALSE;
|
|
_fDeferedPosRectChange = FALSE;
|
|
|
|
if (!g_fDragFullWindows)
|
|
{
|
|
SendMessage(_hwndRebar, WM_SETREDRAW, TRUE, 0);
|
|
}
|
|
|
|
//
|
|
// kick the size code one last time after the loop is done.
|
|
// NOTE: we rely on the WM_SIZE code re-clipping the tray.
|
|
//
|
|
PostMessage(hwnd, WM_SIZE, 0, 0L);
|
|
g_fInSizeMove = FALSE;
|
|
break;
|
|
|
|
case WM_MOVING:
|
|
_HandleMoving(wParam, (LPRECT)lParam);
|
|
break;
|
|
|
|
case WM_ENTERMENULOOP:
|
|
// DebugMsg(DM_TRACE, "c.twp: Enter menu loop.");
|
|
_HandleEnterMenuLoop();
|
|
break;
|
|
|
|
case WM_EXITMENULOOP:
|
|
// DebugMsg(DM_TRACE, "c.twp: Exit menu loop.");
|
|
_HandleExitMenuLoop();
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICELAST)
|
|
return _OnTimerService(uMsg, wParam, lParam);
|
|
_HandleTimer(wParam);
|
|
break;
|
|
|
|
case WM_SIZING:
|
|
_HandleSizing(wParam, (LPRECT)lParam, _uStuckPlace);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
_HandleSize();
|
|
break;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
// NOTE: we get WM_DISPLAYCHANGE in the below two situations
|
|
// 1. a display size changes (HMON will not change in USER)
|
|
// 2. a display goes away or gets added (HMON will change even if
|
|
// the monitor that went away has nothing to do with our hmonStuck)
|
|
|
|
// In the above two situations we actually need to do different things
|
|
// because in 1, we do not want to update our hmonStuck because we might
|
|
// end up on another monitor, but in 2 we do want to update hmonStuck because
|
|
// our hmon is invalid
|
|
|
|
// The way we handle this is to call GetMonitorInfo on our old HMONITOR
|
|
// and see if it's still valid, if not, we update it by calling _SetStuckMonitor
|
|
// all these code is in _ScreenSizeChange;
|
|
|
|
_ScreenSizeChange(hwnd);
|
|
|
|
// Force the Start Pane to rebuild because a change in color depth
|
|
// causes themes to run around destroying fonts (ruining the OOBE
|
|
// text) and we need to reload our bitmaps for the new color depth
|
|
// anyway.
|
|
::PostMessage(_hwnd, SBM_REBUILDMENU, 0, 0);
|
|
|
|
break;
|
|
|
|
// Don't go to default wnd proc for this one...
|
|
case WM_INPUTLANGCHANGEREQUEST:
|
|
return(LRESULT)0L;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.x = g_cxFrame;
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.y = g_cyFrame;
|
|
break;
|
|
|
|
case WM_WININICHANGE:
|
|
if (lParam && (0 == lstrcmpi((LPCTSTR)lParam, TEXT("SaveTaskbar"))))
|
|
{
|
|
_SaveTrayAndDesktop();
|
|
}
|
|
else
|
|
{
|
|
BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, NULL);
|
|
_PropagateMessage(hwnd, uMsg, wParam, lParam);
|
|
_OnWinIniChange(hwnd, wParam, lParam);
|
|
}
|
|
|
|
if (lParam)
|
|
TraceMsg(TF_TRAY, "Tray Got: lParam=%s", (LPCSTR)lParam);
|
|
|
|
break;
|
|
|
|
case WM_TIMECHANGE:
|
|
_PropagateMessage(hwnd, uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
_OnNewSystemSizes();
|
|
BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, NULL);
|
|
_PropagateMessage(hwnd, uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_SETCURSOR:
|
|
if (_iWaitCount) {
|
|
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
|
|
return TRUE;
|
|
} else
|
|
goto DoDefault;
|
|
|
|
case WM_SETFOCUS:
|
|
IUnknown_UIActivateIO(_ptbs, TRUE, NULL);
|
|
break;
|
|
|
|
case WM_SYSCHAR:
|
|
if (wParam == TEXT(' ')) {
|
|
HMENU hmenu;
|
|
int idCmd;
|
|
|
|
SHSetWindowBits(hwnd, GWL_STYLE, 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);
|
|
EnableMenuItem(hmenu, SC_MOVE, (_fCanSizeMove ? MFS_ENABLED : MFS_GRAYED) | MF_BYCOMMAND);
|
|
EnableMenuItem(hmenu, SC_SIZE, (_fCanSizeMove ? MFS_ENABLED : MFS_GRAYED) | MF_BYCOMMAND);
|
|
|
|
idCmd = _TrackMenu(hmenu);
|
|
if (idCmd)
|
|
SendMessage(_hwnd, WM_SYSCOMMAND, idCmd, 0L);
|
|
}
|
|
SHSetWindowBits(hwnd, GWL_STYLE, WS_SYSMENU, 0L);
|
|
}
|
|
break;
|
|
|
|
case WM_SYSCOMMAND:
|
|
|
|
// if we are sizing, make the full screen accessible
|
|
switch (wParam & 0xFFF0) {
|
|
case SC_CLOSE:
|
|
_DoExitWindows(v_hwndDesktop);
|
|
break;
|
|
|
|
default:
|
|
goto DoDefault;
|
|
}
|
|
break;
|
|
|
|
|
|
case TM_DESKTOPSTATE:
|
|
_OnDesktopState(lParam);
|
|
break;
|
|
|
|
case TM_RAISEDESKTOP:
|
|
_RaiseDesktop((BOOL)wParam, FALSE);
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
case TM_NEXTCTL:
|
|
#endif
|
|
case TM_UIACTIVATEIO:
|
|
case TM_ONFOCUSCHANGEIS:
|
|
_OnFocusMsg(uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case TM_MARSHALBS: // wParam=IID lRes=pstm
|
|
return BandSite_OnMarshallBS(wParam, lParam);
|
|
|
|
case TM_SETTIMER:
|
|
case TM_KILLTIMER:
|
|
return _OnTimerService(uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case TM_FACTORY:
|
|
return _OnFactoryMessage(wParam, lParam);
|
|
|
|
case TM_ACTASTASKSW:
|
|
_ActAsSwitcher();
|
|
break;
|
|
|
|
case TM_RELAYPOSCHANGED:
|
|
_AppBarNotifyAll((HMONITOR)lParam, ABN_POSCHANGED, (HWND)wParam, 0);
|
|
break;
|
|
|
|
case TM_BRINGTOTOP:
|
|
SetWindowZorder((HWND)wParam, HWND_TOP);
|
|
break;
|
|
|
|
case TM_WARNNOAUTOHIDE:
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp collision UI request"));
|
|
|
|
//
|
|
// this may look a little funny but what we do is post this message all
|
|
// over the place and ignore it when we think it is a bad time to put
|
|
// up a message (like the middle of a fulldrag...)
|
|
//
|
|
// wParam tells us if we need to try to clear the state
|
|
// the lowword of _SetAutoHideState's return tells if anything changed
|
|
//
|
|
if ((!_fSysSizing || !g_fDragFullWindows) &&
|
|
(!wParam || LOWORD(_SetAutoHideState(FALSE))))
|
|
{
|
|
ShellMessageBox(hinstCabinet, hwnd,
|
|
MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
|
|
MAKEINTRESOURCE(IDS_TASKBAR), MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp blowing off extraneous collision UI request"));
|
|
}
|
|
break;
|
|
|
|
case TM_PRIVATECOMMAND:
|
|
_HandlePrivateCommand(lParam);
|
|
break;
|
|
|
|
case TM_HANDLEDELAYBOOTSTUFF:
|
|
_HandleDelayBootStuff();
|
|
break;
|
|
|
|
case TM_SHELLSERVICEOBJECTS:
|
|
_ssomgr.LoadRegObjects();
|
|
break;
|
|
|
|
case TM_CHANGENOTIFY:
|
|
_HandleChangeNotify(wParam, lParam);
|
|
break;
|
|
|
|
case TM_GETHMONITOR:
|
|
*((HMONITOR *)lParam) = _hmonStuck;
|
|
break;
|
|
|
|
case TM_DOTRAYPROPERTIES:
|
|
DoProperties(TPF_TASKBARPAGE);
|
|
break;
|
|
|
|
case TM_STARTUPAPPSLAUNCHED:
|
|
PostMessage(_hwndNotify, TNM_STARTUPAPPSLAUNCHED, 0, 0);
|
|
break;
|
|
|
|
case TM_LANGUAGEBAND:
|
|
return _ToggleLanguageBand(lParam);
|
|
|
|
case WM_NCRBUTTONUP:
|
|
uMsg = WM_CONTEXTMENU;
|
|
wParam = (WPARAM)_hwndTasks;
|
|
goto L_WM_CONTEXTMENU;
|
|
|
|
case WM_CONTEXTMENU:
|
|
L_WM_CONTEXTMENU:
|
|
|
|
if (!SHRestricted(REST_NOTRAYCONTEXTMENU))
|
|
{
|
|
if (((HWND)wParam) == _hwndStart)
|
|
{
|
|
// Don't display of the Start Menu is up.
|
|
if (SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
|
|
break;
|
|
|
|
_fFromStart = TRUE;
|
|
|
|
StartMenuContextMenu(_hwnd, (DWORD)lParam);
|
|
|
|
_fFromStart = FALSE;
|
|
}
|
|
else if (IsPosInHwnd(lParam, _hwndNotify) || SHIsChildOrSelf(_hwndNotify, GetFocus()) == S_OK)
|
|
{
|
|
// if click was inthe clock, include
|
|
// the time
|
|
_ContextMenu((DWORD)lParam, TRUE);
|
|
}
|
|
else
|
|
{
|
|
BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
case WM_MEASUREITEM:
|
|
case WM_DRAWITEM:
|
|
case WM_MENUCHAR:
|
|
// Don't call bandsite message handler when code path started via the start button context menu
|
|
if (!_fFromStart)
|
|
{
|
|
BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres);
|
|
}
|
|
break;
|
|
|
|
case TM_DOEXITWINDOWS:
|
|
_DoExitWindows(v_hwndDesktop);
|
|
break;
|
|
|
|
case TM_HANDLESTARTUPFAILED:
|
|
_OnHandleStartupFailed();
|
|
break;
|
|
|
|
case WM_HOTKEY:
|
|
if (wParam < GHID_FIRST)
|
|
{
|
|
_HandleHotKey((WORD)wParam);
|
|
}
|
|
else
|
|
{
|
|
_HandleGlobalHotkey(wParam);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
if (!BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres))
|
|
_Command(GET_WM_COMMAND_ID(wParam, lParam));
|
|
break;
|
|
|
|
case SBM_CANCELMENU:
|
|
ClosePopupMenus();
|
|
break;
|
|
|
|
case SBM_REBUILDMENU:
|
|
_BuildStartMenu();
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
_AppBarActivationChange2(hwnd, _uStuckPlace);
|
|
SendMessage(_hwndNotify, TNM_TRAYPOSCHANGED, 0, 0);
|
|
goto DoDefault;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
if (_hwndStartBalloon)
|
|
{
|
|
RECT rc;
|
|
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
|
|
GetWindowRect(_hwndStartBalloon, &rc);
|
|
MapWindowRect(HWND_DESKTOP, _hwnd, &rc);
|
|
|
|
if (PtInRect(&rc, pt))
|
|
{
|
|
ShowWindow(_hwndStartBalloon, SW_HIDE);
|
|
_DontShowTheStartButtonBalloonAnyMore();
|
|
_DestroyStartButtonBalloon();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TM_SETPUMPHOOK:
|
|
ATOMICRELEASE(_pmbTasks);
|
|
ATOMICRELEASE(_pmpTasks);
|
|
if (wParam && lParam)
|
|
{
|
|
_pmbTasks = (IMenuBand*)wParam;
|
|
_pmbTasks->AddRef();
|
|
_pmpTasks = (IMenuPopup*)lParam;
|
|
_pmpTasks->AddRef();
|
|
}
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
_AppBarActivationChange2(hwnd, _uStuckPlace);
|
|
if (wParam != WA_INACTIVE)
|
|
{
|
|
Unhide();
|
|
}
|
|
else
|
|
{
|
|
// When tray is deactivated, remove our keyboard cues:
|
|
//
|
|
SendMessage(hwnd, WM_CHANGEUISTATE,
|
|
MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
|
|
|
|
IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
|
|
}
|
|
//
|
|
// Tray activation is a good time to do a reality check
|
|
// (make sure "always-on-top" agrees with actual window
|
|
// position, make sure there are no ghost buttons, etc).
|
|
//
|
|
RealityCheck();
|
|
goto L_default;
|
|
|
|
case WM_WTSSESSION_CHANGE:
|
|
{
|
|
lres = _OnSessionChange(wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
case WM_THEMECHANGED:
|
|
{
|
|
if (_hTheme)
|
|
{
|
|
CloseThemeData(_hTheme);
|
|
_hTheme = NULL;
|
|
}
|
|
if (wParam)
|
|
{
|
|
_hTheme = OpenThemeData(_hwnd, c_wzTaskbarTheme);
|
|
_fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
|
|
if (_hTheme)
|
|
{
|
|
GetThemeBool(_hTheme, 0, 0, TMT_ALWAYSSHOWSIZINGBAR, &_fShowSizingBarAlways);
|
|
}
|
|
_UpdateVertical(_uStuckPlace, TRUE);
|
|
// Force Refresh of frame
|
|
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
// Force the start button to recalc its size
|
|
_sizeStart.cx = 0;
|
|
_StartButtonReset();
|
|
InvalidateRect(_hwnd, NULL, TRUE);
|
|
|
|
// Force the Start Pane to rebuild with new theme
|
|
::PostMessage(_hwnd, SBM_REBUILDMENU, 0, 0);
|
|
|
|
SetWindowStyle(_hwnd, WS_BORDER | WS_THICKFRAME, !_hTheme);
|
|
}
|
|
break;
|
|
|
|
case TM_WORKSTATIONLOCKED:
|
|
{
|
|
// Desktop locked status changed...
|
|
BOOL fIsDesktopLocked = (BOOL) wParam;
|
|
if (_fIsDesktopLocked != fIsDesktopLocked)
|
|
{
|
|
_fIsDesktopLocked = fIsDesktopLocked;
|
|
_fIsLogoff = FALSE;
|
|
_RecomputeAllWorkareas();
|
|
PostMessage(_hwndNotify, TNM_WORKSTATIONLOCKED, wParam, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TM_SHOWTRAYBALLOON:
|
|
PostMessage(_hwndNotify, TNM_SHOWTRAYBALLOON, wParam, 0);
|
|
break;
|
|
|
|
case TM_STARTMENUDISMISSED:
|
|
// 107561 - call CoFreeUnusedLibraries() peridically to free up dlls - ZekeL - 4-MAY-2001
|
|
// specifically to support MSONSEXT (webfolders) being used in RecentDocs
|
|
// after a file has been opened via webfolders. we get the icon via
|
|
// their namespace but then COM holds on to the DLL for a while (forever?)
|
|
// calling CoFreeUnusedLibraries() does the trick
|
|
SetTimer(_hwnd, IDT_COFREEUNUSED, 3 * 60 * 1000, NULL);
|
|
break;
|
|
|
|
case MM_MIXM_CONTROL_CHANGE:
|
|
Mixer_ControlChange(wParam, lParam);
|
|
break;
|
|
|
|
default:
|
|
L_default:
|
|
if (uMsg == GetDDEExecMsg())
|
|
{
|
|
ASSERT(lParam && 0 == ((LPNMHDR)lParam)->idFrom);
|
|
DDEHandleViewFolderNotify(NULL, _hwnd, (LPNMVIEWFOLDER)lParam);
|
|
LocalFree((LPNMVIEWFOLDER)lParam);
|
|
return TRUE;
|
|
}
|
|
else if (uMsg == _uStartButtonBalloonTip)
|
|
{
|
|
_ShowStartButtonToolTip();
|
|
}
|
|
else if (uMsg == _uLogoffUser)
|
|
{
|
|
// Log off the current user (message from U&P control panel)
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
}
|
|
else if (uMsg == _uMsgEnableUserTrackedBalloonTips)
|
|
{
|
|
PostMessage(_hwndNotify, TNM_ENABLEUSERTRACKINGINFOTIPS, wParam, 0);
|
|
}
|
|
else if (uMsg == _uWinMM_DeviceChange)
|
|
{
|
|
Mixer_MMDeviceChange();
|
|
}
|
|
|
|
|
|
DoDefault:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
return lres;
|
|
}
|
|
|
|
void CTray::_DoExitWindows(HWND hwnd)
|
|
{
|
|
static BOOL s_fShellShutdown = FALSE;
|
|
|
|
if (!s_fShellShutdown)
|
|
{
|
|
|
|
if (_Restricted(hwnd, REST_NOCLOSE))
|
|
return;
|
|
|
|
{
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, FALSE, -1);
|
|
// really #ifdef DEBUG, but want for testing
|
|
// however can't do unconditionally due to perf
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuForceRefresh"),
|
|
NULL, NULL, NULL) || GetAsyncKeyState(VK_SHIFT) < 0)
|
|
{
|
|
_RefreshStartMenu();
|
|
}
|
|
}
|
|
|
|
_SaveTrayAndDesktop();
|
|
|
|
_uModalMode = MM_SHUTDOWN;
|
|
ExitWindowsDialog(hwnd);
|
|
|
|
// NB User can have problems if the focus is forcebly changed to the desktop while
|
|
// shutting down since it tries to serialize the whole process by making windows sys-modal.
|
|
// If we hit this code at just the wrong moment (ie just after the sharing dialog appears)
|
|
// the desktop will become sys-modal so you can't switch back to the sharing dialog
|
|
// and you won't be able to shutdown.
|
|
// SetForegroundWindow(hwnd);
|
|
// SetFocus(hwnd);
|
|
|
|
_uModalMode = 0;
|
|
if ((GetKeyState(VK_SHIFT) < 0) && (GetKeyState(VK_CONTROL) < 0) && (GetKeyState(VK_MENU) < 0))
|
|
{
|
|
// User cancelled...
|
|
// The shift key means exit the tray...
|
|
// ??? - Used to destroy all cabinets...
|
|
// PostQuitMessage(0);
|
|
g_fFakeShutdown = TRUE; // Don't blow away session state; the session will survive
|
|
TraceMsg(TF_TRAY, "c.dew: Posting quit message for tid=%#08x hwndDesk=%x(IsWnd=%d) hwndTray=%x(IsWnd=%d)", GetCurrentThreadId(),
|
|
v_hwndDesktop,IsWindow(v_hwndDesktop), _hwnd,IsWindow(_hwnd));
|
|
// 1 means close all the shell windows too
|
|
PostMessage(v_hwndDesktop, WM_QUIT, 0, 1);
|
|
PostMessage(_hwnd, WM_QUIT, 0, 0);
|
|
|
|
s_fShellShutdown = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CTray::_SaveTray(void)
|
|
{
|
|
if (SHRestricted(REST_NOSAVESET))
|
|
return;
|
|
|
|
if (SHRestricted(REST_CLEARRECENTDOCSONEXIT))
|
|
ClearRecentDocumentsAndMRUStuff(FALSE);
|
|
|
|
//
|
|
// Don't persist tray stuff if in safe mode. We want this
|
|
// to be a temporary mode where the UI settings don't stick.
|
|
//
|
|
if (GetSystemMetrics(SM_CLEANBOOT) == 0)
|
|
{
|
|
_SaveTrayStuff();
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI CTray::PropertiesThreadProc(void* pv)
|
|
{
|
|
return c_tray._PropertiesThreadProc(PtrToUlong(pv));
|
|
}
|
|
|
|
DWORD CTray::_PropertiesThreadProc(DWORD dwFlags)
|
|
{
|
|
HWND hwnd;
|
|
RECT rc;
|
|
DWORD dwExStyle = WS_EX_TOOLWINDOW;
|
|
|
|
GetWindowRect(_hwndStart, &rc);
|
|
dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
|
|
|
|
_hwndProp = hwnd = CreateWindowEx(dwExStyle, TEXT("static"), NULL, 0 ,
|
|
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
|
|
|
|
#define IDI_STTASKBR 40 // stolen from shell32\ids.h
|
|
if (_hwndProp)
|
|
{
|
|
// Get the Alt+Tab icon right
|
|
HICON hicoStub = LoadIcon(GetModuleHandle(TEXT("SHELL32")), MAKEINTRESOURCE(IDI_STTASKBR));
|
|
SendMessage(_hwndProp, WM_SETICON, ICON_BIG, (LPARAM)hicoStub);
|
|
|
|
// SwitchToThisWindow(hwnd, TRUE);
|
|
// SetForegroundWindow(hwnd);
|
|
|
|
DoTaskBarProperties(hwnd, dwFlags);
|
|
|
|
_hwndProp = NULL;
|
|
DestroyWindow(hwnd);
|
|
if (hicoStub)
|
|
DestroyIcon(hicoStub);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define RUNWAITSECS 5
|
|
void CTray::DoProperties(DWORD dwFlags)
|
|
{
|
|
if (!_Restricted(_hwnd, REST_NOSETTASKBAR))
|
|
{
|
|
int i = RUNWAITSECS;
|
|
while (_hwndProp == ((HWND)-1) &&i--)
|
|
{
|
|
// we're in the process of coming up. wait
|
|
Sleep(1000);
|
|
}
|
|
|
|
// failed! blow it off.
|
|
if (_hwndProp == (HWND)-1)
|
|
{
|
|
_hwndProp = NULL;
|
|
}
|
|
|
|
if (_hwndProp)
|
|
{
|
|
// there's a window out there... activate it
|
|
SwitchToThisWindow(GetLastActivePopup(_hwndProp), TRUE);
|
|
}
|
|
else
|
|
{
|
|
_hwndProp = (HWND)-1;
|
|
if (!SHCreateThread(PropertiesThreadProc, IntToPtr(dwFlags), CTF_COINIT, NULL))
|
|
{
|
|
_hwndProp = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CTray::TileEnumProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
CTray* ptray = (CTray*)lParam;
|
|
|
|
if (IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
|
|
((GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) &&
|
|
(hwnd != ptray->_hwnd) && hwnd != v_hwndDesktop)
|
|
{
|
|
return FALSE; // we *can* tile this guy
|
|
}
|
|
return TRUE; // we *cannot* tile this guy
|
|
}
|
|
|
|
HMENU CTray::BuildContextMenu(BOOL fIncludeTime)
|
|
{
|
|
HMENU hmContext = LoadMenuPopup(MAKEINTRESOURCE(MENU_TRAYCONTEXT));
|
|
if (!hmContext)
|
|
return NULL;
|
|
|
|
if (fIncludeTime)
|
|
{
|
|
if (_trayNotify.GetIsNoTrayItemsDisplayPolicyEnabled())
|
|
{
|
|
// We know the position of IDM_NOTIFYCUST from the menu resource...
|
|
DeleteMenu(hmContext, 1, MF_BYPOSITION);
|
|
}
|
|
else
|
|
{
|
|
UINT uEnable = MF_BYCOMMAND;
|
|
if (_trayNotify.GetIsNoAutoTrayPolicyEnabled() || !_trayNotify.GetIsAutoTrayEnabledByUser())
|
|
{
|
|
uEnable |= MFS_DISABLED;
|
|
}
|
|
else
|
|
{
|
|
uEnable |= MFS_ENABLED;
|
|
}
|
|
EnableMenuItem(hmContext, IDM_NOTIFYCUST, uEnable);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT);
|
|
for (int i = 2; i >= 0; i--) // separator, IDM_SETTIME, IDM_NOTIFYCUST,
|
|
{
|
|
DeleteMenu(hmContext, i, MF_BYPOSITION);
|
|
}
|
|
}
|
|
|
|
CheckMenuItem(hmContext, IDM_LOCKTASKBAR,
|
|
MF_BYCOMMAND | (_fCanSizeMove ? MF_UNCHECKED : MF_CHECKED));
|
|
|
|
// Don't let users accidentally check lock the taskbar when the taskbar is zero height
|
|
RECT rc;
|
|
GetClientRect(_hwnd, &rc);
|
|
EnableMenuItem(hmContext, IDM_LOCKTASKBAR,
|
|
MF_BYCOMMAND | ((_IsSizeMoveRestricted() || (RECTHEIGHT(rc) == 0)) ? MFS_DISABLED : MFS_ENABLED));
|
|
|
|
if (!_fUndoEnabled || !_pPositions)
|
|
{
|
|
DeleteMenu(hmContext, IDM_UNDO, MF_BYCOMMAND);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szTemplate[30];
|
|
TCHAR szCommand[30];
|
|
TCHAR szMenu[64];
|
|
LoadString(hinstCabinet, IDS_UNDOTEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
|
|
LoadString(hinstCabinet, _pPositions->idRes, szCommand, ARRAYSIZE(szCommand));
|
|
StringCchPrintf(szMenu, ARRAYSIZE(szMenu), szTemplate, szCommand);
|
|
ModifyMenu(hmContext, IDM_UNDO, MF_BYCOMMAND | MF_STRING, IDM_UNDO, szMenu);
|
|
}
|
|
|
|
if (g_fDesktopRaised)
|
|
{
|
|
TCHAR szHideDesktop[64];
|
|
LoadString(hinstCabinet, IDS_HIDEDESKTOP, szHideDesktop, ARRAYSIZE(szHideDesktop));
|
|
ModifyMenu(hmContext, IDM_TOGGLEDESKTOP, MF_BYCOMMAND | MF_STRING, IDM_TOGGLEDESKTOP, szHideDesktop);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
HKEY hKeyPolicy;
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
|
|
0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType, dwData = 0, dwSize = sizeof(dwData);
|
|
RegQueryValueEx(hKeyPolicy, TEXT("DisableTaskMgr"), NULL,
|
|
&dwType, (LPBYTE) &dwData, &dwSize);
|
|
RegCloseKey(hKeyPolicy);
|
|
if (dwData)
|
|
EnableMenuItem(hmContext, IDM_SHOWTASKMAN, MFS_GRAYED | MF_BYCOMMAND);
|
|
}
|
|
|
|
return hmContext;
|
|
}
|
|
|
|
void CTray::ContextMenuInvoke(int idCmd)
|
|
{
|
|
if (idCmd)
|
|
{
|
|
if (idCmd < IDM_TRAYCONTEXTFIRST)
|
|
{
|
|
BandSite_HandleMenuCommand(_ptbs, idCmd);
|
|
}
|
|
else
|
|
{
|
|
_Command(idCmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// CTray::AsyncSaveSettings
|
|
//
|
|
// We need to save our tray settings, but there may be a bunch
|
|
// of these calls coming, (at startup time or when dragging
|
|
// items in the task bar) so gather them up into one save that
|
|
// will happen in at least 2 seconds.
|
|
//
|
|
void CTray::AsyncSaveSettings()
|
|
{
|
|
if (!_fHandledDelayBootStuff) // no point in saving if we're not done booting
|
|
return;
|
|
|
|
KillTimer(_hwnd, IDT_SAVESETTINGS);
|
|
SetTimer(_hwnd, IDT_SAVESETTINGS, 2000, NULL);
|
|
}
|
|
|
|
|
|
void CTray::_ContextMenu(DWORD dwPos, BOOL fIncludeTime)
|
|
{
|
|
POINT pt = {LOWORD(dwPos), HIWORD(dwPos)};
|
|
|
|
SwitchToThisWindow(_hwnd, TRUE);
|
|
SetForegroundWindow(_hwnd);
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
|
|
|
|
if (dwPos != (DWORD)-1 &&
|
|
IsChildOrHWND(_hwndRebar, WindowFromPoint(pt)))
|
|
{
|
|
// if the context menu came from below us, reflect down
|
|
BandSite_HandleMessage(_ptbs, _hwnd, WM_CONTEXTMENU, 0, dwPos, NULL);
|
|
|
|
}
|
|
else
|
|
{
|
|
HMENU hmenu;
|
|
|
|
if (dwPos == (DWORD)-1)
|
|
{
|
|
HWND hwnd = GetFocus();
|
|
pt.x = pt.y = 0;
|
|
ClientToScreen(hwnd, &pt);
|
|
dwPos = MAKELONG(pt.x, pt.y);
|
|
}
|
|
|
|
hmenu = BuildContextMenu(fIncludeTime);
|
|
if (hmenu)
|
|
{
|
|
int idCmd;
|
|
|
|
BandSite_AddMenus(_ptbs, hmenu, 0, 0, IDM_TRAYCONTEXTFIRST);
|
|
|
|
idCmd = TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
|
|
GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, _hwnd, NULL);
|
|
|
|
DestroyMenu(hmenu);
|
|
|
|
ContextMenuInvoke(idCmd);
|
|
}
|
|
}
|
|
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
|
|
}
|
|
|
|
void _RunFileDlg(HWND hwnd, UINT idIcon, LPCITEMIDLIST pidlWorkingDir, UINT idTitle, UINT idPrompt, DWORD dwFlags)
|
|
{
|
|
HICON hIcon;
|
|
LPCTSTR lpszTitle;
|
|
LPCTSTR lpszPrompt;
|
|
TCHAR szTitle[256];
|
|
TCHAR szPrompt[256];
|
|
TCHAR szWorkingDir[MAX_PATH];
|
|
|
|
dwFlags |= RFD_USEFULLPATHDIR;
|
|
szWorkingDir[0] = 0;
|
|
|
|
hIcon = idIcon ? LoadIcon(hinstCabinet, MAKEINTRESOURCE(idIcon)) : NULL;
|
|
|
|
if (!pidlWorkingDir || !SHGetPathFromIDList(pidlWorkingDir, szWorkingDir))
|
|
{
|
|
// This is either the Tray, or some non-file system folder, so
|
|
// we will "suggest" the Desktop as a working dir, but if the
|
|
// user types a full path, we will use that instead. This is
|
|
// what WIN31 Progman did (except they started in the Windows
|
|
// dir instead of the Desktop).
|
|
goto UseDesktop;
|
|
}
|
|
|
|
// if it's a removable dir, make sure it's still there
|
|
if (szWorkingDir[0])
|
|
{
|
|
int idDrive = PathGetDriveNumber(szWorkingDir);
|
|
if ((idDrive != -1))
|
|
{
|
|
UINT dtype = DriveType(idDrive);
|
|
if (((dtype == DRIVE_REMOVABLE) || (dtype == DRIVE_CDROM))
|
|
&& !PathFileExists(szWorkingDir))
|
|
{
|
|
goto UseDesktop;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if this is a directory. Notice that it could be a in-place
|
|
// navigated document.
|
|
//
|
|
if (PathIsDirectory(szWorkingDir)) {
|
|
goto UseWorkingDir;
|
|
}
|
|
|
|
UseDesktop:
|
|
SHGetSpecialFolderPath(hwnd, szWorkingDir, CSIDL_DESKTOPDIRECTORY, FALSE);
|
|
|
|
UseWorkingDir:
|
|
|
|
if (idTitle)
|
|
{
|
|
LoadString(hinstCabinet, idTitle, szTitle, ARRAYSIZE(szTitle));
|
|
lpszTitle = szTitle;
|
|
}
|
|
else
|
|
lpszTitle = NULL;
|
|
if (idPrompt)
|
|
{
|
|
LoadString(hinstCabinet, idPrompt, szPrompt, ARRAYSIZE(szPrompt));
|
|
lpszPrompt = szPrompt;
|
|
}
|
|
else
|
|
lpszPrompt = NULL;
|
|
|
|
RunFileDlg(hwnd, hIcon, szWorkingDir, lpszTitle, lpszPrompt, dwFlags);
|
|
}
|
|
|
|
BOOL CTray::SavePosEnumProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
// dont need to entercritical here since we are only ever
|
|
// called from SaveWindowPositions, which as already entered the critical secion
|
|
// for _pPositions
|
|
|
|
ASSERTCRITICAL;
|
|
|
|
CTray* ptray = (CTray*)lParam;
|
|
|
|
ASSERT(ptray->_pPositions);
|
|
|
|
if (IsWindowVisible(hwnd) &&
|
|
(hwnd != ptray->_hwnd) &&
|
|
(hwnd != v_hwndDesktop))
|
|
{
|
|
HWNDANDPLACEMENT hap;
|
|
|
|
hap.wp.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(hwnd, &hap.wp);
|
|
|
|
if (hap.wp.showCmd != SW_SHOWMINIMIZED)
|
|
{
|
|
hap.hwnd = hwnd;
|
|
hap.fRestore = TRUE;
|
|
|
|
DSA_AppendItem(ptray->_pPositions->hdsaWP, &hap);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::SaveWindowPositions(UINT idRes)
|
|
{
|
|
ENTERCRITICAL;
|
|
if (_pPositions)
|
|
{
|
|
if (_pPositions->hdsaWP)
|
|
DSA_DeleteAllItems(_pPositions->hdsaWP);
|
|
}
|
|
else
|
|
{
|
|
_pPositions = (LPWINDOWPOSITIONS)LocalAlloc(LPTR, sizeof(WINDOWPOSITIONS));
|
|
if (_pPositions)
|
|
{
|
|
_pPositions->hdsaWP = DSA_Create(sizeof(HWNDANDPLACEMENT), 4);
|
|
}
|
|
}
|
|
|
|
if (_pPositions)
|
|
{
|
|
_pPositions->idRes = idRes;
|
|
|
|
// CheckWindowPositions tested for these...
|
|
ASSERT(idRes == IDS_MINIMIZEALL || idRes == IDS_CASCADE || idRes == IDS_TILE);
|
|
EnumWindows(SavePosEnumProc, (LPARAM)this);
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
LPWINDOWPOSITIONS pPositions;
|
|
HWND hwndDesktop;
|
|
HWND hwndTray;
|
|
BOOL fPostLowerDesktop;
|
|
} RESTOREWNDDATA, *PRESTOREWNDDATA;
|
|
|
|
DWORD WINAPI RestoreWndPosThreadProc(void* pv)
|
|
{
|
|
PRESTOREWNDDATA pWndData = (PRESTOREWNDDATA)pv;
|
|
if (pWndData && pWndData->pPositions)
|
|
{
|
|
LPHWNDANDPLACEMENT phap;
|
|
LONG iAnimate;
|
|
ANIMATIONINFO ami;
|
|
|
|
ami.cbSize = sizeof(ANIMATIONINFO);
|
|
SystemParametersInfo(SPI_GETANIMATION, sizeof(ami), &ami, FALSE);
|
|
iAnimate = ami.iMinAnimate;
|
|
ami.iMinAnimate = FALSE;
|
|
SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
|
|
|
|
if (pWndData->pPositions->hdsaWP)
|
|
{
|
|
for (int i = DSA_GetItemCount(pWndData->pPositions->hdsaWP) - 1 ; i >= 0; i--)
|
|
{
|
|
phap = (LPHWNDANDPLACEMENT)DSA_GetItemPtr(pWndData->pPositions->hdsaWP, i);
|
|
if (IsWindow(phap->hwnd))
|
|
{
|
|
|
|
#ifndef WPF_ASYNCWINDOWPLACEMENT
|
|
#define WPF_ASYNCWINDOWPLACEMENT 0x0004
|
|
#endif
|
|
// pass this async.
|
|
if (!IsHungAppWindow(phap->hwnd))
|
|
{
|
|
phap->wp.length = sizeof(WINDOWPLACEMENT);
|
|
phap->wp.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
|
if (phap->fRestore)
|
|
{
|
|
// only restore those guys we've actually munged.
|
|
SetWindowPlacement(phap->hwnd, &phap->wp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ami.iMinAnimate = iAnimate;
|
|
SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
|
|
|
|
_DestroySavedWindowPositions(pWndData->pPositions);
|
|
|
|
if (pWndData->fPostLowerDesktop)
|
|
{
|
|
PostMessage(pWndData->hwndDesktop, DTM_RAISE, (WPARAM)pWndData->hwndTray, DTRF_LOWER);
|
|
}
|
|
|
|
delete pWndData;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
BOOL CTray::_RestoreWindowPositions(BOOL fPostLowerDesktop)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
ENTERCRITICAL;
|
|
if (_pPositions)
|
|
{
|
|
PRESTOREWNDDATA pWndData = new RESTOREWNDDATA;
|
|
if (pWndData)
|
|
{
|
|
pWndData->pPositions = _pPositions;
|
|
pWndData->fPostLowerDesktop = fPostLowerDesktop;
|
|
pWndData->hwndDesktop = v_hwndDesktop;
|
|
pWndData->hwndTray = _hwnd;
|
|
|
|
if (SHCreateThread(RestoreWndPosThreadProc, pWndData, 0, NULL))
|
|
{
|
|
fRet = TRUE;
|
|
_pPositions = NULL;
|
|
}
|
|
else
|
|
{
|
|
delete pWndData;
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
void _DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions)
|
|
{
|
|
ENTERCRITICAL;
|
|
if (pPositions)
|
|
{
|
|
// free the global struct
|
|
DSA_Destroy(pPositions->hdsaWP);
|
|
LocalFree(pPositions);
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void CTray::HandleWindowDestroyed(HWND hwnd)
|
|
{
|
|
// enter critical section so we dont corrupt the hdsaWP
|
|
ENTERCRITICAL;
|
|
if (_pPositions)
|
|
{
|
|
int i = DSA_GetItemCount(_pPositions->hdsaWP) - 1;
|
|
for (; i >= 0; i--) {
|
|
LPHWNDANDPLACEMENT phap = (LPHWNDANDPLACEMENT)DSA_GetItemPtr(_pPositions->hdsaWP, i);
|
|
if (phap->hwnd == hwnd || !IsWindow(phap->hwnd)) {
|
|
DSA_DeleteItem(_pPositions->hdsaWP, i);
|
|
}
|
|
}
|
|
|
|
if (!DSA_GetItemCount(_pPositions->hdsaWP))
|
|
{
|
|
_DestroySavedWindowPositions(_pPositions);
|
|
_pPositions = NULL;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
// Allow us to bump the activation of the run dlg hidden window.
|
|
// Certain 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 WINAPI RunDlgStaticSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_ACTIVATE:
|
|
if (wParam == WA_ACTIVE)
|
|
{
|
|
// Bump the activation to the desktop.
|
|
if (v_hwndDesktop)
|
|
{
|
|
SetForegroundWindow(v_hwndDesktop);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
// relay it to the tray
|
|
return SendMessage(v_hwndTray, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
DWORD WINAPI CTray::RunDlgThreadProc(void *pv)
|
|
{
|
|
return c_tray._RunDlgThreadProc((HANDLE)pv);
|
|
}
|
|
|
|
BOOL _IsBrowserWindow(HWND hwnd)
|
|
{
|
|
static const TCHAR* c_szClasses[] =
|
|
{
|
|
TEXT("ExploreWClass"),
|
|
TEXT("CabinetWClass"),
|
|
TEXT("IEFrame"),
|
|
};
|
|
|
|
TCHAR szClass[32];
|
|
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
|
|
|
|
for (int i = 0; i < ARRAYSIZE(c_szClasses); i++)
|
|
{
|
|
if (lstrcmpi(szClass, c_szClasses[i]) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD CTray::_RunDlgThreadProc(HANDLE hdata)
|
|
{
|
|
RECT rc, rcTemp;
|
|
HRESULT hrInit = SHCoInitialize();
|
|
|
|
// 99/04/12 #316424 vtan: Get the rectangle for the "Start" button.
|
|
// If this is off the screen the tray is probably in auto hide mode.
|
|
// In this case offset the rectangle into the monitor where it should
|
|
// belong. This may be up, down, left or right depending on the
|
|
// position of the tray.
|
|
|
|
// First thing to do is to establish the dimensions of the monitor on
|
|
// which the tray resides. If no monitor can be found then use the
|
|
// primary monitor.
|
|
|
|
MONITORINFO monitorInfo;
|
|
monitorInfo.cbSize = sizeof(monitorInfo);
|
|
if (GetMonitorInfo(_hmonStuck, &monitorInfo) == 0)
|
|
{
|
|
TBOOL(SystemParametersInfo(SPI_GETWORKAREA, 0, &monitorInfo.rcMonitor, 0));
|
|
}
|
|
|
|
// Get the co-ordinates of the "Start" button.
|
|
|
|
GetWindowRect(_hwndStart, &rc);
|
|
|
|
// Look for an intersection in the monitor.
|
|
|
|
if (IntersectRect(&rcTemp, &rc, &monitorInfo.rcMonitor) == 0)
|
|
{
|
|
LONG lDeltaX, lDeltaY;
|
|
|
|
// Does not exist in the monitor. Move the co-ordinates by the
|
|
// width or height of the tray so that it does.
|
|
|
|
// This bizarre arithmetic is used because _ComputeHiddenRect()
|
|
// takes into account the frame and that right/bottom of RECT
|
|
// is exclusive in GDI.
|
|
|
|
lDeltaX = _sStuckWidths.cx - g_cxFrame;
|
|
lDeltaY = _sStuckWidths.cy - g_cyFrame;
|
|
if (rc.left < monitorInfo.rcMonitor.left)
|
|
{
|
|
--lDeltaX;
|
|
lDeltaY = 0;
|
|
}
|
|
else if (rc.top < monitorInfo.rcMonitor.top)
|
|
{
|
|
lDeltaX = 0;
|
|
--lDeltaY;
|
|
}
|
|
else if (rc.right > monitorInfo.rcMonitor.right)
|
|
{
|
|
lDeltaX = -lDeltaX;
|
|
lDeltaY = 0;
|
|
}
|
|
else if (rc.bottom > monitorInfo.rcMonitor.bottom)
|
|
{
|
|
lDeltaX = 0;
|
|
lDeltaY = -lDeltaY;
|
|
}
|
|
TBOOL(OffsetRect(&rc, lDeltaX, lDeltaY));
|
|
}
|
|
|
|
HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("static"), NULL, 0 ,
|
|
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
|
|
if (hwnd)
|
|
{
|
|
BOOL fSimple = FALSE;
|
|
HANDLE hMemWorkDir = NULL;
|
|
LPITEMIDLIST pidlWorkingDir = NULL;
|
|
|
|
// Subclass it.
|
|
SubclassWindow(hwnd, RunDlgStaticSubclassWndProc);
|
|
|
|
if (hdata)
|
|
SetProp(hwnd, TEXT("WaitingThreadID"), hdata);
|
|
|
|
if (!SHRestricted(REST_STARTRUNNOHOMEPATH))
|
|
{
|
|
// On NT, we like to start apps in the HOMEPATH directory. This
|
|
// should be the current directory for the current process.
|
|
TCHAR szDir[MAX_PATH];
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
GetEnvironmentVariable(TEXT("HOMEDRIVE"), szDir, ARRAYSIZE(szDir));
|
|
GetEnvironmentVariable(TEXT("HOMEPATH"), szPath, ARRAYSIZE(szPath));
|
|
|
|
if (PathAppend(szDir, szPath) && PathIsDirectory(szDir))
|
|
{
|
|
pidlWorkingDir = SHSimpleIDListFromPath(szDir);
|
|
if (pidlWorkingDir)
|
|
{
|
|
// free it the "simple" way...
|
|
fSimple = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pidlWorkingDir)
|
|
{
|
|
// If the last active window was a folder/explorer window with the
|
|
// desktop as root, use its as the current dir
|
|
if (_hwndLastActive)
|
|
{
|
|
ENTERCRITICAL;
|
|
if (_hwndLastActive && !IsMinimized(_hwndLastActive) && _IsBrowserWindow(_hwndLastActive))
|
|
{
|
|
SendMessageTimeout(_hwndLastActive, CWM_CLONEPIDL, GetCurrentProcessId(), 0, SMTO_ABORTIFHUNG | SMTO_BLOCK, 500, (DWORD_PTR*)&hMemWorkDir);
|
|
pidlWorkingDir = (LPITEMIDLIST)SHLockShared(hMemWorkDir, GetCurrentProcessId());
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
}
|
|
|
|
_RunFileDlg(hwnd, 0, pidlWorkingDir, 0, 0, 0);
|
|
if (pidlWorkingDir)
|
|
{
|
|
if (fSimple)
|
|
{
|
|
ILFree(pidlWorkingDir);
|
|
}
|
|
else
|
|
{
|
|
SHUnlockShared(pidlWorkingDir);
|
|
}
|
|
}
|
|
|
|
if (hMemWorkDir)
|
|
{
|
|
ASSERT(fSimple == FALSE);
|
|
SHFreeShared(hMemWorkDir, GetCurrentProcessId());
|
|
}
|
|
|
|
if (hdata)
|
|
{
|
|
RemoveProp(hwnd, TEXT("WaitingThreadID"));
|
|
}
|
|
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
SHCoUninitialize(hrInit);
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::_RunDlg()
|
|
{
|
|
HANDLE hEvent;
|
|
void *pvThreadParam;
|
|
|
|
if (!_Restricted(_hwnd, REST_NORUN))
|
|
{
|
|
TCHAR szRunDlgTitle[MAX_PATH];
|
|
HWND hwndOldRun;
|
|
LoadString(hinstCabinet, IDS_RUNDLGTITLE, szRunDlgTitle, ARRAYSIZE(szRunDlgTitle));
|
|
|
|
// See if there is already a run dialog up, and if so, try to activate it
|
|
|
|
hwndOldRun = FindWindow(WC_DIALOG, szRunDlgTitle);
|
|
if (hwndOldRun)
|
|
{
|
|
DWORD dwPID;
|
|
|
|
GetWindowThreadProcessId(hwndOldRun, &dwPID);
|
|
if (dwPID == GetCurrentProcessId())
|
|
{
|
|
if (IsWindowVisible(hwndOldRun))
|
|
{
|
|
SetForegroundWindow(hwndOldRun);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create an event so we can wait for the run dlg to appear before
|
|
// continue - this allows it to capture any type-ahead.
|
|
hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("MSShellRunDlgReady"));
|
|
if (hEvent)
|
|
pvThreadParam = IntToPtr(GetCurrentThreadId());
|
|
else
|
|
pvThreadParam = NULL;
|
|
|
|
if (SHQueueUserWorkItem(RunDlgThreadProc, pvThreadParam, 0, 0, NULL, NULL, TPS_LONGEXECTIME | TPS_DEMANDTHREAD))
|
|
{
|
|
if (hEvent)
|
|
{
|
|
SHProcessMessagesUntilEvent(NULL, hEvent, 10 * 1000);
|
|
DebugMsg(DM_TRACE, TEXT("c.t_rd: Done waiting."));
|
|
}
|
|
}
|
|
|
|
if (hEvent)
|
|
CloseHandle(hEvent);
|
|
}
|
|
}
|
|
|
|
void CTray::_ExploreCommonStartMenu(BOOL bExplore)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
TCHAR szCmdLine[MAX_PATH + 50];
|
|
|
|
//
|
|
// Get the common start menu path.
|
|
//
|
|
// we want to force the directory to exist, but not on W95 machines
|
|
if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_STARTMENU, FALSE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we are starting in explorer view, then the command line
|
|
// has a "/e, " before the quoted diretory.
|
|
//
|
|
|
|
if (bExplore)
|
|
{
|
|
StringCchCopy(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("explorer.exe /e, \""));
|
|
}
|
|
else
|
|
{
|
|
StringCchCopy(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("explorer.exe \""));
|
|
}
|
|
|
|
StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), szPath);
|
|
StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("\""));
|
|
|
|
// Initialize process startup info
|
|
STARTUPINFO si = {0};
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
// Start explorer
|
|
PROCESS_INFORMATION pi = {0};
|
|
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);
|
|
}
|
|
}
|
|
|
|
int CTray::_GetQuickLaunchID()
|
|
{
|
|
int iQLBandID = -1;
|
|
|
|
DWORD dwBandID;
|
|
for (int i = 0; (iQLBandID == -1) && SUCCEEDED(_ptbs->EnumBands(i, &dwBandID)); i++)
|
|
{
|
|
if (BandSite_TestBandCLSID(_ptbs, dwBandID, CLSID_ISFBand) == S_OK)
|
|
{
|
|
IUnknown* punk;
|
|
if (SUCCEEDED(_ptbs->GetBandObject(dwBandID, IID_PPV_ARG(IUnknown, &punk))))
|
|
{
|
|
VARIANTARG v = {0};
|
|
v.vt = VT_I4;
|
|
if (SUCCEEDED(IUnknown_Exec(punk, &CLSID_ISFBand, 1, 0, NULL, &v)))
|
|
{
|
|
if ((v.vt == VT_I4) && (CSIDL_APPDATA == (DWORD)v.lVal))
|
|
{
|
|
iQLBandID = (int)dwBandID;
|
|
}
|
|
}
|
|
punk->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return iQLBandID;
|
|
}
|
|
|
|
int CTray::_ToggleQL(int iVisible)
|
|
{
|
|
int iQLBandID = _GetQuickLaunchID();
|
|
|
|
bool fOldVisible = (-1 != iQLBandID);
|
|
bool fNewVisible = (0 != iVisible);
|
|
|
|
if ((iVisible != -1) && (fNewVisible != fOldVisible))
|
|
{
|
|
if (fNewVisible)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl)))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
SHGetPathFromIDList(pidl, szPath);
|
|
PathCombine(szPath, szPath, L"Microsoft\\Internet Explorer\\Quick Launch");
|
|
ILFree(pidl);
|
|
pidl = ILCreateFromPath(szPath);
|
|
|
|
if (pidl)
|
|
{
|
|
IFolderBandPriv *pfbp;
|
|
// create an ISF band to show folders as hotlinks
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_ISFBand, NULL, CLSCTX_INPROC, IID_PPV_ARG(IFolderBandPriv, &pfbp))))
|
|
{
|
|
IShellFolderBand* psfb;
|
|
if (SUCCEEDED(pfbp->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb))))
|
|
{
|
|
if (SUCCEEDED(psfb->InitializeSFB(NULL, pidl)))
|
|
{
|
|
pfbp->SetNoText(TRUE);
|
|
|
|
VARIANTARG v;
|
|
v.vt = VT_I4;
|
|
v.lVal = CSIDL_APPDATA;
|
|
IUnknown_Exec(psfb, &CLSID_ISFBand, 1, 0, &v, NULL);
|
|
|
|
v.lVal = UEMIND_SHELL; // UEMIND_SHELL/BROWSER
|
|
IUnknown_Exec(psfb, &CGID_ShellDocView, SHDVID_UEMLOG, 0, &v, NULL);
|
|
|
|
IDeskBand* ptb;
|
|
if (SUCCEEDED(pfbp->QueryInterface(IID_PPV_ARG(IDeskBand, &ptb))))
|
|
{
|
|
HRESULT hr = _ptbs->AddBand(ptb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ptbs->SetBandState(ShortFromResult(hr), BSSF_NOTITLE, BSSF_NOTITLE);
|
|
}
|
|
ptb->Release();
|
|
}
|
|
}
|
|
psfb->Release();
|
|
}
|
|
pfbp->Release();
|
|
}
|
|
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iBandID;
|
|
do {
|
|
iBandID = _GetQuickLaunchID();
|
|
if (iBandID != -1)
|
|
{
|
|
_ptbs->RemoveBand(iBandID);
|
|
}
|
|
} while (iBandID != -1);
|
|
}
|
|
}
|
|
|
|
return iQLBandID;
|
|
}
|
|
|
|
void CTray::StartMenuContextMenu(HWND hwnd, DWORD dwPos)
|
|
{
|
|
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(hwnd, CSIDL_STARTMENU, TRUE);
|
|
INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT_START);
|
|
HandleFullScreenApp(NULL);
|
|
|
|
SetForegroundWindow(hwnd);
|
|
|
|
if (pidlStart)
|
|
{
|
|
LPITEMIDLIST pidlLast = ILClone(ILFindLastID(pidlStart));
|
|
ILRemoveLastID(pidlStart);
|
|
|
|
if (pidlLast)
|
|
{
|
|
IShellFolder *psf = BindToFolder(pidlStart);
|
|
if (psf)
|
|
{
|
|
HMENU hmenu = CreatePopupMenu();
|
|
if (hmenu)
|
|
{
|
|
IContextMenu *pcm;
|
|
HRESULT hr = psf->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidlLast, IID_X_PPV_ARG(IContextMenu, NULL, &pcm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pcm->QueryContextMenu(hmenu, 0, IDSYSPOPUP_FIRST, IDSYSPOPUP_LAST, CMF_VERBSONLY);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int idCmd;
|
|
TCHAR szCommon[MAX_PATH];
|
|
|
|
//Add the menu to invoke the "Start Menu Properties"
|
|
LoadString (hinstCabinet, IDS_STARTMENUPROP, szCommon, ARRAYSIZE(szCommon));
|
|
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_STARTMENUPROP, szCommon);
|
|
if (!SHRestricted(REST_NOCOMMONGROUPS))
|
|
{
|
|
// If the user has access to the Common Start Menu, then we can add those items. If not,
|
|
// then we should not.
|
|
BOOL fAddCommon = (S_OK == SHGetFolderPath(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, szCommon));
|
|
|
|
if (fAddCommon)
|
|
fAddCommon = IsUserAnAdmin();
|
|
|
|
|
|
// Since we don't show this on the start button when the user is not an admin, don't show it here... I guess...
|
|
if (fAddCommon)
|
|
{
|
|
AppendMenu (hmenu, MF_SEPARATOR, 0, NULL);
|
|
LoadString (hinstCabinet, IDS_OPENCOMMON, szCommon, ARRAYSIZE(szCommon));
|
|
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_OPENCOMMON, szCommon);
|
|
LoadString (hinstCabinet, IDS_EXPLORECOMMON, szCommon, ARRAYSIZE(szCommon));
|
|
AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_EXPLORECOMMON, szCommon);
|
|
}
|
|
}
|
|
|
|
if (dwPos == (DWORD)-1)
|
|
{
|
|
idCmd = _TrackMenu(hmenu);
|
|
}
|
|
else
|
|
{
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
|
|
idCmd = TrackPopupMenu(hmenu,
|
|
TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
|
|
GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, hwnd, NULL);
|
|
SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
|
|
}
|
|
|
|
|
|
switch(idCmd)
|
|
{
|
|
case 0: //User did not select a menu item; so, nothing to do!
|
|
break;
|
|
|
|
case IDSYSPOPUP_OPENCOMMON:
|
|
_ExploreCommonStartMenu(FALSE);
|
|
break;
|
|
|
|
case IDSYSPOPUP_EXPLORECOMMON:
|
|
_ExploreCommonStartMenu(TRUE);
|
|
break;
|
|
|
|
case IDSYSPOPUP_STARTMENUPROP:
|
|
DoProperties(TPF_STARTMENUPAGE);
|
|
break;
|
|
|
|
default:
|
|
TCHAR szPath[MAX_PATH];
|
|
CMINVOKECOMMANDINFOEX ici = {0};
|
|
#ifdef UNICODE
|
|
CHAR szPathAnsi[MAX_PATH];
|
|
#endif
|
|
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
|
|
ici.hwnd = hwnd;
|
|
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - IDSYSPOPUP_FIRST);
|
|
ici.nShow = SW_NORMAL;
|
|
#ifdef UNICODE
|
|
SHGetPathFromIDListA(pidlStart, szPathAnsi);
|
|
SHGetPathFromIDList(pidlStart, szPath);
|
|
ici.lpDirectory = szPathAnsi;
|
|
ici.lpDirectoryW = szPath;
|
|
ici.fMask |= CMIC_MASK_UNICODE;
|
|
#else
|
|
SHGetPathFromIDList(pidlStart, szPath);
|
|
ici.lpDirectory = szPath;
|
|
#endif
|
|
pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
|
|
|
|
break;
|
|
|
|
} // Switch(idCmd)
|
|
}
|
|
pcm->Release();
|
|
}
|
|
DestroyMenu(hmenu);
|
|
}
|
|
psf->Release();
|
|
}
|
|
ILFree(pidlLast);
|
|
}
|
|
ILFree(pidlStart);
|
|
}
|
|
}
|
|
|
|
void GiveDesktopFocus()
|
|
{
|
|
SetForegroundWindow(v_hwndDesktop);
|
|
SendMessage(v_hwndDesktop, DTM_UIACTIVATEIO, (WPARAM) TRUE, /*dtb*/0);
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: loads the given resource string and executes it.
|
|
The resource string should follow this format:
|
|
|
|
"program.exe>parameters"
|
|
|
|
If there are no parameters, the format should simply be:
|
|
|
|
"program.exe"
|
|
|
|
*/
|
|
void _ExecResourceCmd(UINT ids)
|
|
{
|
|
TCHAR szCmd[2*MAX_PATH];
|
|
|
|
if (LoadString(hinstCabinet, ids, szCmd, SIZECHARS(szCmd)))
|
|
{
|
|
SHELLEXECUTEINFO sei = {0};
|
|
|
|
// Find list of parameters (if any)
|
|
LPTSTR pszParam = StrChr(szCmd, TEXT('>'));
|
|
|
|
if (pszParam)
|
|
{
|
|
// Replace the '>' with a null terminator
|
|
*pszParam = 0;
|
|
pszParam++;
|
|
}
|
|
|
|
sei.cbSize = sizeof(sei);
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = szCmd;
|
|
sei.lpParameters = pszParam;
|
|
ShellExecuteEx(&sei);
|
|
}
|
|
}
|
|
|
|
void CTray::_RefreshStartMenu()
|
|
{
|
|
if (_pmbStartMenu)
|
|
{
|
|
IUnknown_Exec(_pmbStartMenu, &CLSID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
|
|
}
|
|
else if (_pmpStartPane)
|
|
{
|
|
IUnknown_Exec(_pmpStartPane, &CLSID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
|
|
}
|
|
_RefreshSettings();
|
|
_UpdateBandSiteStyle();
|
|
}
|
|
|
|
BOOL CTray::_CanMinimizeAll()
|
|
{
|
|
return (_hwndTasks && SendMessage(_hwndTasks, TBC_CANMINIMIZEALL, 0, 0));
|
|
}
|
|
|
|
BOOL CTray::_MinimizeAll(BOOL fPostRaiseDesktop)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (_hwndTasks)
|
|
{
|
|
fRet = (BOOL)SendMessage(_hwndTasks, TBC_MINIMIZEALL, (WPARAM)_hwnd, (LPARAM)fPostRaiseDesktop);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
extern void _UpdateNotifySetting(BOOL fNotifySetting);
|
|
|
|
//
|
|
// Due to the weirdness of PnP, if the eject request occurs on a thread
|
|
// that contains windows, the eject stalls for 15 seconds. So do it
|
|
// on its own thread.
|
|
//
|
|
DWORD CALLBACK _EjectThreadProc(LPVOID lpThreadParameter)
|
|
{
|
|
CM_Request_Eject_PC();
|
|
return 0;
|
|
}
|
|
|
|
void CTray::_Command(UINT idCmd)
|
|
{
|
|
INSTRUMENT_ONCOMMAND(SHCNFI_TRAYCOMMAND, _hwnd, idCmd);
|
|
|
|
switch (idCmd) {
|
|
|
|
case IDM_CONTROLS:
|
|
case IDM_PRINTERS:
|
|
_ShowFolder(_hwnd,
|
|
idCmd == IDM_CONTROLS ? CSIDL_CONTROLS : CSIDL_PRINTERS, COF_USEOPENSETTINGS);
|
|
break;
|
|
|
|
case IDM_EJECTPC:
|
|
// Must use SHCreateThread and not a queued workitem because
|
|
// a workitem might inherit a thread that has windows on it.
|
|
// CTF_INSIST: In emergency, eject synchronously. This stalls
|
|
// for 15 seconds but it's better than nothing.
|
|
SHCreateThread(_EjectThreadProc, NULL, CTF_INSIST, NULL);
|
|
break;
|
|
|
|
case IDM_LOGOFF:
|
|
// Let the desktop get a chance to repaint to get rid of the
|
|
// start menu bits before bringing up the logoff dialog box.
|
|
UpdateWindow(_hwnd);
|
|
Sleep(100);
|
|
|
|
_SaveTrayAndDesktop();
|
|
LogoffWindowsDialog(v_hwndDesktop);
|
|
break;
|
|
|
|
case IDM_MU_DISCONNECT:
|
|
// Do the same sleep as above for the same reason.
|
|
UpdateWindow(_hwnd);
|
|
Sleep(100);
|
|
DisconnectWindowsDialog(v_hwndDesktop);
|
|
break;
|
|
|
|
case IDM_EXITWIN:
|
|
// Do the same sleep as above for the same reason.
|
|
UpdateWindow(_hwnd);
|
|
Sleep(100);
|
|
|
|
_DoExitWindows(v_hwndDesktop);
|
|
break;
|
|
|
|
case IDM_TOGGLEDESKTOP:
|
|
_RaiseDesktop(!g_fDesktopRaised, TRUE);
|
|
break;
|
|
|
|
case IDM_FILERUN:
|
|
_RunDlg();
|
|
break;
|
|
|
|
case IDM_MINIMIZEALLHOTKEY:
|
|
_HandleGlobalHotkey(GHID_MINIMIZEALL);
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
case IDM_SIZEUP:
|
|
{
|
|
RECT rcView;
|
|
GetWindowRect(_hwndRebar, &rcView);
|
|
MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&rcView, 2);
|
|
rcView.bottom -= 18;
|
|
SetWindowPos(_hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
break;
|
|
|
|
case IDM_SIZEDOWN:
|
|
{
|
|
RECT rcView;
|
|
GetWindowRect(_hwndRebar, &rcView);
|
|
MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&rcView, 2);
|
|
rcView.bottom += 18;
|
|
SetWindowPos(_hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case IDM_MINIMIZEALL:
|
|
// minimize all window
|
|
_MinimizeAll(FALSE);
|
|
_fUndoEnabled = TRUE;
|
|
break;
|
|
|
|
case IDM_UNDO:
|
|
_RestoreWindowPositions(FALSE);
|
|
break;
|
|
|
|
case IDM_SETTIME:
|
|
// run the default applet in timedate.cpl
|
|
SHRunControlPanel(TEXT("timedate.cpl"), _hwnd);
|
|
break;
|
|
|
|
case IDM_NOTIFYCUST:
|
|
DoProperties(TPF_TASKBARPAGE | TPF_INVOKECUSTOMIZE);
|
|
break;
|
|
|
|
case IDM_LOCKTASKBAR:
|
|
{
|
|
BOOL fCanSizeMove = !_fCanSizeMove; // toggle
|
|
SHRegSetUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarSizeMove"),
|
|
REG_DWORD, &fCanSizeMove , sizeof(DWORD), SHREGSET_FORCE_HKCU);
|
|
_RefreshSettings();
|
|
_UpdateBandSiteStyle();
|
|
}
|
|
break;
|
|
|
|
case IDM_SHOWTASKMAN:
|
|
RunSystemMonitor();
|
|
break;
|
|
|
|
case IDM_CASCADE:
|
|
case IDM_VERTTILE:
|
|
case IDM_HORIZTILE:
|
|
if (_CanTileAnyWindows())
|
|
{
|
|
SaveWindowPositions((idCmd == IDM_CASCADE) ? IDS_CASCADE : IDS_TILE);
|
|
|
|
_AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
|
|
|
|
if (idCmd == IDM_CASCADE)
|
|
{
|
|
CascadeWindows(GetDesktopWindow(), 0, NULL, 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
TileWindows(GetDesktopWindow(), ((idCmd == IDM_VERTTILE)?
|
|
MDITILE_VERTICAL : MDITILE_HORIZONTAL), NULL, 0, NULL);
|
|
}
|
|
|
|
// do it *before* ABN_xxx so don't get 'indirect' moves
|
|
// REVIEW or should it be after?
|
|
// CheckWindowPositions();
|
|
_fUndoEnabled = FALSE;
|
|
SetTimer(_hwnd, IDT_ENABLEUNDO, 500, NULL);
|
|
|
|
_AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
|
|
}
|
|
break;
|
|
|
|
case IDM_TRAYPROPERTIES:
|
|
DoProperties(TPF_TASKBARPAGE);
|
|
break;
|
|
|
|
case IDM_SETTINGSASSIST:
|
|
SHCreateThread(SettingsUIThreadProc, NULL, 0, NULL);
|
|
break;
|
|
|
|
case IDM_HELPSEARCH:
|
|
_ExecResourceCmd(IDS_HELP_CMD);
|
|
break;
|
|
|
|
// NB The Alt-s comes in here.
|
|
case IDC_KBSTART:
|
|
SetForegroundWindow(_hwnd);
|
|
// This pushes the start button and causes the start menu to popup.
|
|
SendMessage(_hwndStart, BM_SETSTATE, TRUE, 0);
|
|
// This forces the button back up.
|
|
SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
|
|
break;
|
|
|
|
case IDC_ASYNCSTART:
|
|
#if 0 // (for testing UAssist locking code)
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_DBSLEEP, UEMF_XEVENT, -1, (LPARAM)10000);
|
|
#endif
|
|
#ifdef DEBUG
|
|
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
|
{
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, TRUE, -1);
|
|
_RefreshStartMenu();
|
|
}
|
|
#endif
|
|
|
|
// Make sure the button is down.
|
|
// DebugMsg(DM_TRACE, "c.twp: IDC_START.");
|
|
|
|
// Make sure the Start button is down.
|
|
if (!_bMainMenuInit && SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.twp: Start button down.");
|
|
// Set the focus.
|
|
_SetFocus(_hwndStart);
|
|
_ToolbarMenu();
|
|
}
|
|
break;
|
|
|
|
// NB LButtonDown on the Start button come in here.
|
|
// Space-bar stuff also comes in here.
|
|
case IDC_START:
|
|
// User gets a bit confused with space-bar tuff (the popup ends up
|
|
// getting the key-up and beeps).
|
|
PostMessage(_hwnd, WM_COMMAND, IDC_ASYNCSTART, 0);
|
|
break;
|
|
|
|
case FCIDM_FINDFILES:
|
|
SHFindFiles(NULL, NULL);
|
|
break;
|
|
|
|
case FCIDM_FINDCOMPUTER:
|
|
SHFindComputer(NULL, NULL);
|
|
break;
|
|
|
|
case FCIDM_REFRESH:
|
|
_RefreshStartMenu();
|
|
break;
|
|
|
|
case FCIDM_NEXTCTL:
|
|
{
|
|
MSG msg = { 0, WM_KEYDOWN, VK_TAB };
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
// Since we are Tab or Shift Tab we should turn the focus rect on.
|
|
//
|
|
// Note: we don't need to do this in the GiveDesktopFocus cases below,
|
|
// but in those cases we're probably already in the UIS_CLEAR UISF_HIDEFOCUS
|
|
// state so this message is cheap to send.
|
|
//
|
|
SendMessage(_hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
|
|
UISF_HIDEFOCUS), 0);
|
|
|
|
BOOL fShift = GetAsyncKeyState(VK_SHIFT) < 0;
|
|
|
|
if (hwndFocus && (IsChildOrHWND(_hwndStart, hwndFocus)))
|
|
{
|
|
if (fShift)
|
|
{
|
|
// gotta deactivate manually
|
|
GiveDesktopFocus();
|
|
}
|
|
else
|
|
{
|
|
IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
|
|
}
|
|
}
|
|
else if (hwndFocus && (IsChildOrHWND(_hwndNotify, hwndFocus)))
|
|
{
|
|
if (fShift)
|
|
{
|
|
IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
|
|
}
|
|
else
|
|
{
|
|
GiveDesktopFocus();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IUnknown_TranslateAcceleratorIO(_ptbs, &msg) != S_OK)
|
|
{
|
|
if (fShift)
|
|
{
|
|
_SetFocus(_hwndStart);
|
|
}
|
|
else
|
|
{
|
|
// if you tab forward out of the bands, the next focus guy is the tray notify set
|
|
_SetFocus(_hwndNotify);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDM_MU_SECURITY:
|
|
MuSecurity();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//// Start menu/Tray tab as a drop target
|
|
|
|
HRESULT CStartDropTarget::_GetStartMenuDropTarget(IDropTarget** pptgt)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*pptgt = NULL;
|
|
|
|
LPITEMIDLIST pidlStart = SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE);
|
|
|
|
if (pidlStart)
|
|
{
|
|
IShellFolder *psf = BindToFolder(pidlStart);
|
|
if (psf)
|
|
{
|
|
hr = psf->CreateViewObject(_ptray->_hwnd, IID_PPV_ARG(IDropTarget, pptgt));
|
|
psf->Release();
|
|
}
|
|
|
|
ILFree(pidlStart);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDropTargetBase::QueryInterface(REFIID riid, void ** ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CDropTargetBase, IDropTarget),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDropTargetBase::AddRef()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDropTargetBase::Release()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHODIMP CDropTargetBase::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
_ptray->_SetUnhideTimer(ptl.x, ptl.y);
|
|
|
|
HWND hwndLock = _ptray->_hwnd; // no clippy
|
|
|
|
_DragEnter(hwndLock, ptl, pdtobj);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDropTargetBase::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
_ptray->_SetUnhideTimer(ptl.x, ptl.y);
|
|
_DragMove(_ptray->_hwndStart, ptl);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDropTargetBase::DragLeave()
|
|
{
|
|
DAD_DragLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDropTargetBase::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
DAD_DragLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// There are two different policies for the Start Button depending on
|
|
// whether we are in Classic mode or Personal (New Start Pane) mode.
|
|
//
|
|
// Classic mode: Drops onto the Start Button are treated as if they
|
|
// were drops into the CSIDL_STARTMENU folder.
|
|
//
|
|
// Personal mode: Drops onto the Start Button are treated as if they
|
|
// were drops into the pin list.
|
|
//
|
|
|
|
CStartDropTarget::CStartDropTarget() : CDropTargetBase(IToClass(CTray, _dtStart, this))
|
|
{
|
|
}
|
|
|
|
CTrayDropTarget::CTrayDropTarget() : CDropTargetBase(IToClass(CTray, _dtTray, this))
|
|
{
|
|
}
|
|
|
|
STDMETHODIMP CTrayDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
return CDropTargetBase::DragEnter(pdtobj, grfKeyState, ptl, pdwEffect);
|
|
}
|
|
|
|
STDMETHODIMP CTrayDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
return CDropTargetBase::DragOver(grfKeyState, ptl, pdwEffect);
|
|
}
|
|
|
|
void CStartDropTarget::_StartAutoOpenTimer(POINTL *pptl)
|
|
{
|
|
POINT pt = { pptl->x, pptl->y };
|
|
RECT rc;
|
|
//Make sure it really is in the start menu..
|
|
GetWindowRect(_ptray->_hwndStart, &rc);
|
|
if (PtInRect(&rc,pt))
|
|
{
|
|
SetTimer(_ptray->_hwnd, IDT_STARTMENU, 1000, NULL);
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CStartDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// default to not allowing drops
|
|
_dwEffectsAllowed = DROPEFFECT_NONE;
|
|
|
|
if (Tray_StartPanelEnabled())
|
|
{
|
|
// if we've disabled dragging and dropping, don't do anything, but if only the pin list is restricted, then still start the timer
|
|
if (!IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU, TEXT("Advanced"), TEXT("Start_EnableDragDrop"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW))
|
|
{
|
|
// Personal mode: Treat it as an add to the pin list.
|
|
// IsPinnable checks REST_NOSMPINNEDLIST
|
|
if (_ptray->_psmpin && _ptray->_psmpin->IsPinnable(pdtobj, SMPINNABLE_REJECTSLOWMEDIA, NULL) == S_OK)
|
|
{
|
|
_dwEffectsAllowed = DROPEFFECT_LINK;
|
|
}
|
|
|
|
*pdwEffect &= _dwEffectsAllowed;
|
|
|
|
// Always start the AutoOpen timer because once we open, the user
|
|
// can drop onto other things which may have different drop policies
|
|
// from the pin list.
|
|
_StartAutoOpenTimer(&ptl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU, TEXT("Advanced"), TEXT("StartMenuChange"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW))
|
|
{
|
|
// Classic mode: Treat it as a drop on the Start Menu folder.
|
|
IDropTarget* ptgt;
|
|
_dwEffectsAllowed = DROPEFFECT_LINK;
|
|
|
|
hr = _GetStartMenuDropTarget(&ptgt);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Check to make sure that we're going to accept the drop before we expand the start menu.
|
|
ptgt->DragEnter(pdtobj, grfKeyState, ptl,
|
|
pdwEffect);
|
|
|
|
// DROPEFFECT_NONE means it ain't gonna work, so don't popup the Start Menu.
|
|
if (*pdwEffect != DROPEFFECT_NONE)
|
|
{
|
|
_StartAutoOpenTimer(&ptl);
|
|
}
|
|
|
|
ptgt->DragLeave();
|
|
ptgt->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
CDropTargetBase::DragEnter(pdtobj, grfKeyState, ptl, pdwEffect);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CStartDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
*pdwEffect = (_dwEffectsAllowed & DROPEFFECT_LINK);
|
|
return CDropTargetBase::DragOver(grfKeyState, pt, pdwEffect);
|
|
}
|
|
|
|
STDMETHODIMP CStartDropTarget::DragLeave()
|
|
{
|
|
KillTimer(_ptray->_hwnd, IDT_STARTMENU);
|
|
return CDropTargetBase::DragLeave();
|
|
}
|
|
|
|
STDMETHODIMP CStartDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
KillTimer(_ptray->_hwnd, IDT_STARTMENU);
|
|
|
|
HRESULT hr;
|
|
|
|
if (Tray_StartPanelEnabled())
|
|
{
|
|
// Personal mode: Treat it as an add to the pin list.
|
|
LPITEMIDLIST pidl;
|
|
if (_ptray->_psmpin && _ptray->_psmpin->IsPinnable(pdtobj, SMPINNABLE_REJECTSLOWMEDIA, &pidl) == S_OK)
|
|
{
|
|
// Delete it from the pin list if it's already there because
|
|
// we want to move it to the bottom.
|
|
_ptray->_psmpin->Modify(pidl, NULL);
|
|
// Now add it to the bottom.
|
|
_ptray->_psmpin->Modify(NULL, pidl);
|
|
ILFree(pidl);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IDropTarget* pdrop;
|
|
hr = _GetStartMenuDropTarget(&pdrop);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!Tray_StartPanelEnabled())
|
|
{
|
|
POINTL ptDrop = { 0, 0 };
|
|
DWORD grfKeyStateDrop = 0;
|
|
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
|
|
pdrop->DragEnter(pdtobj, grfKeyStateDrop, ptDrop, pdwEffect);
|
|
hr = pdrop->Drop(pdtobj, grfKeyStateDrop, ptDrop, pdwEffect);
|
|
|
|
pdrop->DragLeave();
|
|
}
|
|
pdrop->Release();
|
|
}
|
|
}
|
|
|
|
DAD_DragLeave();
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CTray::_RegisterDropTargets()
|
|
{
|
|
THR(RegisterDragDrop(_hwndStart, &_dtStart));
|
|
THR(RegisterDragDrop(_hwnd, &_dtTray));
|
|
|
|
// It is not a serious error if this fails; it just means that
|
|
// drag/drop to the Start Button will not add to the pin list
|
|
CoCreateInstance(CLSID_StartMenuPin, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARG(IStartMenuPin, &_psmpin));
|
|
}
|
|
|
|
void CTray::_RevokeDropTargets()
|
|
{
|
|
RevokeDragDrop(_hwndStart);
|
|
RevokeDragDrop(_hwnd);
|
|
ATOMICRELEASET(_psmpin, IStartMenuPin);
|
|
}
|
|
|
|
void CTray::_HandleGlobalHotkey(WPARAM wParam)
|
|
{
|
|
INSTRUMENT_HOTKEY(SHCNFI_GLOBALHOTKEY, wParam);
|
|
|
|
switch(wParam)
|
|
{
|
|
case GHID_RUN:
|
|
_RunDlg();
|
|
break;
|
|
|
|
case GHID_MINIMIZEALL:
|
|
if (_CanMinimizeAll())
|
|
_MinimizeAll(FALSE);
|
|
SetForegroundWindow(v_hwndDesktop);
|
|
break;
|
|
|
|
case GHID_UNMINIMIZEALL:
|
|
_RestoreWindowPositions(FALSE);
|
|
break;
|
|
|
|
case GHID_HELP:
|
|
_Command(IDM_HELPSEARCH);
|
|
break;
|
|
|
|
case GHID_DESKTOP:
|
|
_RaiseDesktop(!g_fDesktopRaised, TRUE);
|
|
break;
|
|
|
|
case GHID_TRAYNOTIFY:
|
|
SwitchToThisWindow(_hwnd, TRUE);
|
|
SetForegroundWindow(_hwnd);
|
|
_SetFocus(_hwndNotify);
|
|
break;
|
|
|
|
case GHID_EXPLORER:
|
|
_ShowFolder(_hwnd, CSIDL_DRIVES, COF_CREATENEWWINDOW | COF_EXPLORE);
|
|
break;
|
|
|
|
case GHID_FINDFILES:
|
|
if (!SHRestricted(REST_NOFIND))
|
|
_Command(FCIDM_FINDFILES);
|
|
break;
|
|
|
|
case GHID_FINDCOMPUTER:
|
|
if (!SHRestricted(REST_NOFIND))
|
|
_Command(FCIDM_FINDCOMPUTER);
|
|
break;
|
|
|
|
case GHID_TASKTAB:
|
|
case GHID_TASKSHIFTTAB:
|
|
if (GetForegroundWindow() != _hwnd)
|
|
SetForegroundWindow(_hwnd);
|
|
SendMessage(_hwndTasks, TBC_TASKTAB, wParam == GHID_TASKTAB ? 1 : -1, 0L);
|
|
break;
|
|
|
|
case GHID_SYSPROPERTIES:
|
|
#define IDS_SYSDMCPL 0x2334 // from shelldll
|
|
SHRunControlPanel(MAKEINTRESOURCE(IDS_SYSDMCPL), _hwnd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CTray::_UnregisterGlobalHotkeys()
|
|
{
|
|
for (int i = GHID_FIRST; i < GHID_MAX; i++)
|
|
{
|
|
UnregisterHotKey(_hwnd, i);
|
|
}
|
|
}
|
|
|
|
void CTray::_RegisterGlobalHotkeys()
|
|
{
|
|
int i;
|
|
// Are the Windows keys restricted?
|
|
DWORD dwRestricted = SHRestricted(REST_NOWINKEYS);
|
|
|
|
for (i = GHID_FIRST ; i < GHID_MAX; i++)
|
|
{
|
|
// If the Windows Keys are Not restricted or it's not a Windows key
|
|
if (!((HIWORD(GlobalKeylist[i - GHID_FIRST]) & MOD_WIN) && dwRestricted))
|
|
{
|
|
// Then register it.
|
|
RegisterHotKey(_hwnd, i, HIWORD(GlobalKeylist[i - GHID_FIRST]), LOWORD(GlobalKeylist[i - GHID_FIRST]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTray::_RaiseDesktop(BOOL fRaise, BOOL fRestoreWindows)
|
|
{
|
|
if (v_hwndDesktop && (fRaise == !g_fDesktopRaised) && !_fProcessingDesktopRaise)
|
|
{
|
|
_fProcessingDesktopRaise = TRUE;
|
|
BOOL fPostMessage = TRUE;
|
|
|
|
if (fRaise)
|
|
{
|
|
HWND hwndFG = GetForegroundWindow();
|
|
// If no window has focus then set focus to the tray
|
|
if (hwndFG)
|
|
{
|
|
hwndFG = _hwnd;
|
|
}
|
|
|
|
if (!_hwndFocusBeforeRaise)
|
|
{
|
|
// See if the Foreground Window had a popup window
|
|
_hwndFocusBeforeRaise = GetLastActivePopup(hwndFG);
|
|
}
|
|
if (!IsWindowVisible(_hwndFocusBeforeRaise))
|
|
{
|
|
_hwndFocusBeforeRaise = hwndFG;
|
|
}
|
|
|
|
// _MinimizeAll will save the windows positions synchronously, and will minimize the
|
|
// the windows on a background thread
|
|
_fMinimizedAllBeforeRaise = _CanMinimizeAll();
|
|
if (_fMinimizedAllBeforeRaise)
|
|
{
|
|
fPostMessage = !_MinimizeAll(TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fRestoreWindows)
|
|
{
|
|
HWND hwnd = _hwndFocusBeforeRaise;
|
|
if (_fMinimizedAllBeforeRaise)
|
|
{
|
|
// Since the windows are restored on a seperate thread, I want the make that the
|
|
// desktop is not raised until they are done, so the window restore thread will
|
|
// actually post the message for raising the desktop
|
|
fPostMessage = !_RestoreWindowPositions(TRUE);
|
|
}
|
|
|
|
SetForegroundWindow(hwnd);
|
|
if (hwnd == _hwnd)
|
|
{
|
|
_SetFocus(_hwndStart);
|
|
}
|
|
}
|
|
|
|
_hwndFocusBeforeRaise = NULL;
|
|
}
|
|
|
|
if (fPostMessage)
|
|
PostMessage(v_hwndDesktop, DTM_RAISE, (WPARAM)_hwnd, fRaise ? DTRF_RAISE : DTRF_LOWER);
|
|
}
|
|
}
|
|
|
|
void CTray::_OnDesktopState(LPARAM lParam)
|
|
{
|
|
g_fDesktopRaised = (!(lParam & DTRF_LOWER));
|
|
|
|
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
|
|
|
|
if (!g_fDesktopRaised)
|
|
{
|
|
HandleFullScreenApp(NULL);
|
|
}
|
|
else
|
|
{
|
|
// if the desktop is raised, we need to force the tray to be always on top
|
|
// until it's lowered again
|
|
_ResetZorder();
|
|
}
|
|
|
|
DAD_ShowDragImage(TRUE); // unlock the drag sink if we are dragging.
|
|
_fProcessingDesktopRaise = FALSE;
|
|
}
|
|
|
|
BOOL CTray::_ToggleLanguageBand(BOOL fShowIt)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DWORD dwBandID;
|
|
BOOL fFound = FALSE;
|
|
for (int i = 0; !fFound && SUCCEEDED(_ptbs->EnumBands(i, &dwBandID)); i++)
|
|
{
|
|
if (BandSite_TestBandCLSID(_ptbs, dwBandID, CLSID_MSUTBDeskBand) == S_OK)
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL fShow = fFound;
|
|
|
|
if (fShowIt && !fFound)
|
|
{
|
|
IDeskBand* pdb;
|
|
HRESULT hr = CoCreateInstance(CLSID_MSUTBDeskBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDeskBand, &pdb));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _ptbs->AddBand(pdb);
|
|
fShow = TRUE;
|
|
pdb->Release();
|
|
}
|
|
}
|
|
else if (!fShowIt && fFound)
|
|
{
|
|
hr = _ptbs->RemoveBand(dwBandID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fShow = FALSE;
|
|
}
|
|
}
|
|
|
|
return fShow;
|
|
}
|
|
|
|
// Process the message by propagating it to all of our child windows
|
|
typedef struct
|
|
{
|
|
UINT uMsg;
|
|
WPARAM wP;
|
|
LPARAM lP;
|
|
CTray* ptray;
|
|
} CABPM;
|
|
|
|
BOOL CTray::PropagateEnumProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
CABPM *ppm = (CABPM *)lParam;
|
|
|
|
if (SHIsChildOrSelf(ppm->ptray->_hwndRebar, hwnd) == S_OK)
|
|
{
|
|
return TRUE;
|
|
}
|
|
SendMessage(hwnd, ppm->uMsg, ppm->wP, ppm->lP);
|
|
return TRUE;
|
|
}
|
|
|
|
void CTray::_PropagateMessage(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CABPM pm = {uMessage, wParam, lParam, this};
|
|
|
|
ASSERT(hwnd != _hwndRebar);
|
|
EnumChildWindows(hwnd, PropagateEnumProc, (LPARAM)&pm);
|
|
}
|
|
|
|
//
|
|
// Called from SETTINGS.DLL when the tray property sheet needs
|
|
// to be activated. See SettingsUIThreadProc below.
|
|
//
|
|
// Also used by desktop2\deskhost.cpp to get to the tray properties.
|
|
//
|
|
void WINAPI Tray_DoProperties(DWORD dwFlags)
|
|
{
|
|
c_tray.DoProperties(dwFlags);
|
|
}
|
|
|
|
|
|
DWORD WINAPI CTray::SettingsUIThreadProc(void *pv)
|
|
{
|
|
//
|
|
// Open up the "Settings Wizards" UI.
|
|
//
|
|
HMODULE hmodSettings = LoadLibrary(TEXT("settings.dll"));
|
|
if (NULL != hmodSettings)
|
|
{
|
|
//
|
|
// Entry point in SETTINGS.DLL is ordinal 1.
|
|
// Don't want to export this entry point by name.
|
|
//
|
|
PSETTINGSUIENTRY pfDllEntry = (PSETTINGSUIENTRY)GetProcAddress(hmodSettings, (LPCSTR)1);
|
|
if (NULL != pfDllEntry)
|
|
{
|
|
//
|
|
// This call will open and run the UI.
|
|
// The thread's message loop is inside settings.dll.
|
|
// This call will not return until the settings UI has been closed.
|
|
//
|
|
(*pfDllEntry)(Tray_DoProperties);
|
|
}
|
|
|
|
FreeLibrary(hmodSettings);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// This function is called whenever we detect a change to the default
|
|
// browser registration in HKCR\http\shell\open\command.
|
|
//
|
|
// For compatibility with old browsers, if the default browser (URL handler)
|
|
// is not XP-aware, then auto-generate a StartMenuInternet client
|
|
// registration and set it as the default.
|
|
//
|
|
|
|
void CTray::_MigrateOldBrowserSettings()
|
|
{
|
|
// We want only one person to do this work per machine (though it doesn't
|
|
// hurt to have more than one person do it; it's just pointless), so try
|
|
// to filter out people who clearly didn't instigate the key change.
|
|
//
|
|
if (!_fIsDesktopLocked && _fIsDesktopConnected)
|
|
{
|
|
// If the user does not have write access then we can't migrate the
|
|
// setting... (In which case there was nothing to migrate anyway
|
|
// since you need to be administrator to change the default browser...)
|
|
|
|
HKEY hkBrowser;
|
|
DWORD dwDisposition;
|
|
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Clients\\StartMenuInternet"),
|
|
0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE, NULL, &hkBrowser, &dwDisposition) == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szCommand[MAX_PATH];
|
|
DWORD cch = ARRAYSIZE(szCommand);
|
|
|
|
// It is important that we use AssocQueryString to parse the
|
|
// executable, because Netscape has a habit of registering
|
|
// their path incorrectly (they forget to quote the space in
|
|
// "Program Files") but AssocQueryString has special recovery
|
|
// code to detect and repair that case...
|
|
if (SUCCEEDED(AssocQueryString(ASSOCF_NOUSERSETTINGS,
|
|
ASSOCSTR_EXECUTABLE, L"http",
|
|
L"open", szCommand, &cch)) &&
|
|
szCommand[0])
|
|
{
|
|
TCHAR szAppName[MAX_PATH];
|
|
StringCchCopy(szAppName, ARRAYSIZE(szAppName), PathFindFileName(szCommand));
|
|
|
|
// You might think that we need to special-case MSN Explorer,
|
|
// since they shipped before XP RTM'd, and convert MSN6.EXE
|
|
// to "MSN Explorer", but that's not true because
|
|
// they never take over as the default http handler, so we
|
|
// will never see them here!
|
|
|
|
// Create a registration for the new default browser if necessary.
|
|
// We keep our hands off once we see a DefaultIcon key, since that
|
|
// proves that the application is XP-aware.
|
|
|
|
// When IE Access is turned off, StartMenuInternet\IExplore.exe is
|
|
// also removed so that IE will not appear in "Customize Start Menu"
|
|
// dialog box. Do not migrate IE.
|
|
|
|
HKEY hkClient;
|
|
|
|
if ( 0 != lstrcmpi(szAppName, TEXT("IEXPLORE.EXE")) &&
|
|
RegCreateKeyEx(hkBrowser, szAppName, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE, NULL, &hkClient, &dwDisposition) == ERROR_SUCCESS)
|
|
{
|
|
if (dwDisposition == REG_CREATED_NEW_KEY)
|
|
{
|
|
TCHAR szFriendly[MAX_PATH];
|
|
cch = ARRAYSIZE(szFriendly);
|
|
if (SUCCEEDED(AssocQueryString(ASSOCF_NOUSERSETTINGS | ASSOCF_INIT_BYEXENAME | ASSOCF_VERIFY,
|
|
ASSOCSTR_FRIENDLYAPPNAME, szCommand,
|
|
NULL, szFriendly, &cch)))
|
|
{
|
|
// Set the friendly name
|
|
RegSetValueEx(hkClient, TEXT("LocalizedString"), 0, REG_SZ, (BYTE*)szFriendly, sizeof(TCHAR) * (cch + 1));
|
|
|
|
// Set the command string (properly quoted)
|
|
PathQuoteSpaces(szCommand);
|
|
SHSetValue(hkClient, TEXT("shell\\open\\command"), NULL,
|
|
REG_SZ, szCommand, sizeof(TCHAR) * (1 + lstrlen(szCommand)));
|
|
}
|
|
}
|
|
|
|
LONG l = 0;
|
|
if (RegQueryValue(hkClient, TEXT("DefaultIcon"), NULL, &l) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
// Set it as the system default
|
|
RegSetValueEx(hkBrowser, NULL, 0, REG_SZ, (BYTE*)szAppName, sizeof(TCHAR) * (lstrlen(szAppName) + 1));
|
|
|
|
// Now tell everybody about the change
|
|
SHSendMessageBroadcast(WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Software\\Clients\\StartMenuInternet"));
|
|
}
|
|
RegCloseKey(hkClient);
|
|
}
|
|
|
|
}
|
|
RegCloseKey(hkBrowser);
|
|
}
|
|
}
|
|
|
|
// Restart the monitoring of the registry...
|
|
// (RegNotifyChangeKeyValue is good for only one shot.)
|
|
// Some apps (like Opera) delete the key as part of their registration,
|
|
// which causes our HKEY to go bad, so close it and make a new one.
|
|
|
|
if (_hkHTTP)
|
|
{
|
|
RegCloseKey(_hkHTTP);
|
|
_hkHTTP = NULL;
|
|
}
|
|
|
|
//
|
|
// Note! We have to register on HKCR\http\shell recursively
|
|
// even though we only care about HKCR\http\shell\open\command.
|
|
// The reason is that shell\open\command might not exist (IE
|
|
// deletes it as part of its uninstall) and you can't register
|
|
// a wait on a key that doesn't exist. We don't want to create
|
|
// a blank key on our own, because that means "To launch a web
|
|
// browser, run the null string as a command," which doesn't work
|
|
// too great.
|
|
//
|
|
if (RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("http\\shell"),
|
|
0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ENUMERATE_SUB_KEYS |
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_NOTIFY,
|
|
NULL, &_hkHTTP, NULL) == ERROR_SUCCESS)
|
|
{
|
|
RegNotifyChangeKeyValue(_hkHTTP, TRUE,
|
|
REG_NOTIFY_CHANGE_NAME |
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
_hHTTPEvent, TRUE);
|
|
}
|
|
}
|
|
|
|
void CTray::_MigrateOldBrowserSettingsCB(PVOID lpParameter, BOOLEAN)
|
|
{
|
|
//
|
|
// Sleep a little while so the app can finish installing all the
|
|
// registry keys it wants before we start cleaning up behind it.
|
|
//
|
|
Sleep(1000);
|
|
|
|
CTray *self = (CTray *)lpParameter;
|
|
self->_MigrateOldBrowserSettings();
|
|
}
|
|
|
|
//
|
|
// *** WARNING ***
|
|
//
|
|
// This is a private interface EXPLORER.EXE exposes to SHDOCVW, which
|
|
// allows SHDOCVW (mostly desktop) to access tray. All member must be
|
|
// thread safe!
|
|
//
|
|
|
|
CDeskTray::CDeskTray()
|
|
{
|
|
_ptray = IToClass(CTray, _desktray, this);
|
|
}
|
|
|
|
HRESULT CDeskTray::QueryInterface(REFIID riid, void ** ppvObj)
|
|
{
|
|
#if 0 // no IID_IDeskTray yet defined
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CDeskTray, IDeskTray),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
#else
|
|
return E_NOTIMPL;
|
|
#endif
|
|
}
|
|
|
|
ULONG CDeskTray::AddRef()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
ULONG CDeskTray::Release()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
HRESULT CDeskTray::GetTrayWindow(HWND* phwndTray)
|
|
{
|
|
ASSERT(_ptray->_hwnd);
|
|
*phwndTray = _ptray->_hwnd;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDeskTray::SetDesktopWindow(HWND hwndDesktop)
|
|
{
|
|
ASSERT(v_hwndDesktop == NULL);
|
|
v_hwndDesktop = hwndDesktop;
|
|
return S_OK;
|
|
}
|
|
|
|
UINT CDeskTray::AppBarGetState()
|
|
{
|
|
return (_ptray->_uAutoHide ? ABS_AUTOHIDE : 0) |
|
|
(_ptray->_fAlwaysOnTop ? ABS_ALWAYSONTOP : 0);
|
|
}
|
|
|
|
//*** CDeskTray::SetVar -- set an explorer variable (var#i := value)
|
|
// ENTRY/EXIT
|
|
// var id# of variable to be changed
|
|
// value value to be assigned
|
|
// NOTES
|
|
// WARNING: thread safety is up to caller!
|
|
// notes: currently only called in 1 place, but extra generality is cheap
|
|
// minimal cost
|
|
HRESULT CDeskTray::SetVar(int var, DWORD value)
|
|
{
|
|
extern BOOL g_fExitExplorer;
|
|
|
|
TraceMsg(DM_TRACE, "c.cdt_sv: set var(%d):=%d", var, value);
|
|
switch (var) {
|
|
case SVTRAY_EXITEXPLORER:
|
|
TraceMsg(DM_TRACE, "c.cdt_sv: set g_fExitExplorer:=%d", value);
|
|
g_fExitExplorer = value;
|
|
WriteCleanShutdown(1);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
return S_FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|