|
|
#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; }
|