Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentraywnd.c
Go to the documentation of this file.
00001 /* 00002 * ReactOS Explorer 00003 * 00004 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 00021 #include <precomp.h> 00022 00023 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu; 00024 00025 #define WM_APP_TRAYDESTROY (WM_APP + 0x100) 00026 00027 static LONG TrayWndCount = 0; 00028 00029 static const TCHAR szTrayWndClass[] = TEXT("Shell_TrayWnd"); 00030 00031 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl; 00032 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl; 00033 00034 /* 00035 * ITrayWindow 00036 */ 00037 00038 const GUID IID_IShellDesktopTray = {0x213e2df9, 0x9a14, 0x4328, {0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9}}; 00039 00040 typedef struct 00041 { 00042 const ITrayWindowVtbl *lpVtbl; 00043 const IShellDesktopTrayVtbl *lpVtblShellDesktopTray; 00044 LONG Ref; 00045 00046 HWND hWnd; 00047 HWND hWndDesktop; 00048 00049 HWND hwndStart; 00050 HIMAGELIST himlStartBtn; 00051 SIZE StartBtnSize; 00052 HFONT hStartBtnFont; 00053 HFONT hCaptionFont; 00054 00055 ITrayBandSite *TrayBandSite; 00056 HWND hwndRebar; 00057 HWND hwndTaskSwitch; 00058 HWND hwndTrayNotify; 00059 00060 DWORD Position; 00061 HMONITOR Monitor; 00062 HMONITOR PreviousMonitor; 00063 DWORD DraggingPosition; 00064 HMONITOR DraggingMonitor; 00065 00066 RECT rcTrayWnd[4]; 00067 RECT rcNewPosSize; 00068 SIZE TraySize; 00069 union 00070 { 00071 DWORD Flags; 00072 struct 00073 { 00074 DWORD AutoHide : 1; 00075 DWORD AlwaysOnTop : 1; 00076 DWORD SmSmallIcons : 1; 00077 DWORD HideClock : 1; 00078 DWORD Locked : 1; 00079 00080 /* UI Status */ 00081 DWORD InSizeMove : 1; 00082 DWORD IsDragging : 1; 00083 DWORD NewPosSize : 1; 00084 }; 00085 }; 00086 00087 NONCLIENTMETRICS ncm; 00088 HFONT hFont; 00089 00090 IMenuBand *StartMenuBand; 00091 IMenuPopup *StartMenuPopup; 00092 HBITMAP hbmStartMenu; 00093 00094 HWND hWndTrayProperties; 00095 } ITrayWindowImpl; 00096 00097 BOOL LaunchCPanel(HWND hwnd, LPCTSTR applet) 00098 { 00099 TCHAR szParams[MAX_PATH]; 00100 _tcscpy(szParams, TEXT("shell32.dll,Control_RunDLL ")); 00101 _tcscat(szParams, applet); 00102 return (ShellExecute(hwnd, TEXT("open"), TEXT("rundll32.exe"), szParams, NULL, SW_SHOWDEFAULT) > (HINSTANCE)32); 00103 } 00104 00105 static IUnknown * 00106 IUnknown_from_impl(ITrayWindowImpl *This) 00107 { 00108 return (IUnknown *)&This->lpVtbl; 00109 } 00110 00111 static ITrayWindow * 00112 ITrayWindow_from_impl(ITrayWindowImpl *This) 00113 { 00114 return (ITrayWindow *)&This->lpVtbl; 00115 } 00116 00117 static IShellDesktopTray * 00118 IShellDesktopTray_from_impl(ITrayWindowImpl *This) 00119 { 00120 return (IShellDesktopTray *)&This->lpVtblShellDesktopTray; 00121 } 00122 00123 static ITrayWindowImpl * 00124 impl_from_ITrayWindow(ITrayWindow *iface) 00125 { 00126 return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl, 00127 lpVtbl)); 00128 } 00129 00130 static ITrayWindowImpl * 00131 impl_from_IShellDesktopTray(IShellDesktopTray *iface) 00132 { 00133 return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl, 00134 lpVtblShellDesktopTray)); 00135 } 00136 00137 /* 00138 * ITrayWindow 00139 */ 00140 00141 static BOOL 00142 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl *This) 00143 { 00144 This->ncm.cbSize = sizeof(This->ncm); 00145 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 00146 sizeof(This->ncm), 00147 &This->ncm, 00148 0)) 00149 { 00150 if (This->hFont != NULL) 00151 DeleteObject(This->hFont); 00152 00153 This->hFont = CreateFontIndirect(&This->ncm.lfMessageFont); 00154 return TRUE; 00155 } 00156 00157 return FALSE; 00158 } 00159 00160 static VOID 00161 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl *This) 00162 { 00163 if (This->hwndTrayNotify != NULL) 00164 { 00165 SendMessage(This->hwndTrayNotify, 00166 WM_SETFONT, 00167 (WPARAM)This->hFont, 00168 TRUE); 00169 } 00170 } 00171 00172 static HMONITOR 00173 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl *This, 00174 IN OUT RECT *pRect, 00175 IN DWORD dwFlags) 00176 { 00177 MONITORINFO mi; 00178 HMONITOR hMon; 00179 00180 mi.cbSize = sizeof(mi); 00181 hMon = MonitorFromRect(pRect, 00182 dwFlags); 00183 if (hMon != NULL && 00184 GetMonitorInfo(hMon, 00185 &mi)) 00186 { 00187 *pRect = mi.rcMonitor; 00188 } 00189 else 00190 { 00191 pRect->left = 0; 00192 pRect->top = 0; 00193 pRect->right = GetSystemMetrics(SM_CXSCREEN); 00194 pRect->bottom = GetSystemMetrics(SM_CYSCREEN); 00195 00196 hMon = NULL; 00197 } 00198 00199 return hMon; 00200 } 00201 00202 static HMONITOR 00203 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl *This, 00204 IN const RECT *pRect) 00205 { 00206 HMONITOR hMon; 00207 00208 /* In case the monitor sizes or saved sizes differ a bit (probably 00209 not a lot, only so the tray window overlaps into another monitor 00210 now), minimize the risk that we determine a wrong monitor by 00211 using the center point of the tray window if we can't determine 00212 it using the rectangle. */ 00213 hMon = MonitorFromRect(pRect, 00214 MONITOR_DEFAULTTONULL); 00215 if (hMon == NULL) 00216 { 00217 POINT pt; 00218 00219 pt.x = pRect->left + ((pRect->right - pRect->left) / 2); 00220 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); 00221 00222 /* be less error-prone, find the nearest monitor */ 00223 hMon = MonitorFromPoint(pt, 00224 MONITOR_DEFAULTTONEAREST); 00225 } 00226 00227 return hMon; 00228 } 00229 00230 static HMONITOR 00231 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl *This, 00232 IN HMONITOR hMonitor, 00233 IN OUT RECT *pRect) 00234 { 00235 HMONITOR hMon = NULL; 00236 00237 if (hMonitor != NULL) 00238 { 00239 MONITORINFO mi; 00240 00241 mi.cbSize = sizeof(mi); 00242 if (!GetMonitorInfo(hMonitor, 00243 &mi)) 00244 { 00245 /* Hm, the monitor is gone? Try to find a monitor where it 00246 could be located now */ 00247 hMon = ITrayWindowImpl_GetMonitorFromRect(This, 00248 pRect); 00249 if (hMon == NULL || 00250 !GetMonitorInfo(hMon, 00251 &mi)) 00252 { 00253 hMon = NULL; 00254 goto GetPrimaryRect; 00255 } 00256 } 00257 00258 *pRect = mi.rcMonitor; 00259 } 00260 else 00261 { 00262 GetPrimaryRect: 00263 pRect->left = 0; 00264 pRect->top = 0; 00265 pRect->right = GetSystemMetrics(SM_CXSCREEN); 00266 pRect->bottom = GetSystemMetrics(SM_CYSCREEN); 00267 } 00268 00269 return hMon; 00270 } 00271 00272 static VOID 00273 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position, 00274 IN const SIZE *pTraySize, 00275 IN OUT RECT *pRect) 00276 { 00277 switch (Position) 00278 { 00279 case ABE_LEFT: 00280 pRect->right = pRect->left + pTraySize->cx; 00281 break; 00282 00283 case ABE_TOP: 00284 pRect->bottom = pRect->top + pTraySize->cy; 00285 break; 00286 00287 case ABE_RIGHT: 00288 pRect->left = pRect->right - pTraySize->cx; 00289 break; 00290 00291 case ABE_BOTTOM: 00292 default: 00293 pRect->top = pRect->bottom - pTraySize->cy; 00294 break; 00295 } 00296 } 00297 00298 static VOID 00299 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl *This, 00300 IN DWORD Position, 00301 IN const RECT *pScreen, 00302 IN const SIZE *pTraySize OPTIONAL, 00303 OUT RECT *pRect) 00304 { 00305 if (pTraySize == NULL) 00306 pTraySize = &This->TraySize; 00307 00308 *pRect = *pScreen; 00309 00310 /* Move the border outside of the screen */ 00311 InflateRect(pRect, 00312 GetSystemMetrics(SM_CXEDGE), 00313 GetSystemMetrics(SM_CYEDGE)); 00314 00315 ITrayWindowImpl_MakeTrayRectWithSize(Position, 00316 pTraySize, 00317 pRect); 00318 } 00319 00320 BOOL 00321 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl *This) 00322 { 00323 return This->Position == ABE_TOP || This->Position == ABE_BOTTOM; 00324 } 00325 00326 static HMONITOR 00327 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl *This, 00328 IN DWORD Position, 00329 IN OUT RECT *pRect) 00330 { 00331 RECT rcScreen; 00332 //BOOL Horizontal; 00333 HMONITOR hMon; 00334 SIZE szMax, szWnd; 00335 00336 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This); 00337 00338 szWnd.cx = pRect->right - pRect->left; 00339 szWnd.cy = pRect->bottom - pRect->top; 00340 00341 rcScreen = *pRect; 00342 hMon = ITrayWindowImpl_GetScreenRectFromRect(This, 00343 &rcScreen, 00344 MONITOR_DEFAULTTONEAREST); 00345 00346 /* Calculate the maximum size of the tray window and limit the window 00347 size to half of the screen's size. */ 00348 szMax.cx = (rcScreen.right - rcScreen.left) / 2; 00349 szMax.cy = (rcScreen.bottom - rcScreen.top) / 2; 00350 if (szWnd.cx > szMax.cx) 00351 szWnd.cx = szMax.cx; 00352 if (szWnd.cy > szMax.cy) 00353 szWnd.cy = szMax.cy; 00354 00355 /* FIXME - calculate */ 00356 00357 ITrayWindowImpl_GetTrayRectFromScreenRect(This, 00358 Position, 00359 &rcScreen, 00360 &szWnd, 00361 pRect); 00362 00363 return hMon; 00364 } 00365 00366 #if 0 00367 static VOID 00368 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl *This, 00369 OUT RECT *pRect) 00370 { 00371 RECT rcMin = {0}; 00372 00373 AdjustWindowRectEx(&rcMin, 00374 GetWindowLongPtr(This->hWnd, 00375 GWL_STYLE), 00376 FALSE, 00377 GetWindowLongPtr(This->hWnd, 00378 GWL_EXSTYLE)); 00379 00380 *pRect = rcMin; 00381 } 00382 #endif 00383 00384 00385 static DWORD 00386 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl *This, 00387 IN POINT pt, 00388 OUT RECT *pRect, 00389 OUT HMONITOR *phMonitor) 00390 { 00391 HMONITOR hMon, hMonNew; 00392 DWORD PosH, PosV, Pos; 00393 SIZE DeltaPt, ScreenOffset; 00394 RECT rcScreen; 00395 00396 rcScreen.left = 0; 00397 rcScreen.top = 0; 00398 00399 /* Determine the screen rectangle */ 00400 hMon = MonitorFromPoint(pt, 00401 MONITOR_DEFAULTTONULL); 00402 00403 if (hMon != NULL) 00404 { 00405 MONITORINFO mi; 00406 00407 mi.cbSize = sizeof(mi); 00408 if (!GetMonitorInfo(hMon, 00409 &mi)) 00410 { 00411 hMon = NULL; 00412 goto GetPrimaryScreenRect; 00413 } 00414 00415 /* make left top corner of the screen zero based to 00416 make calculations easier */ 00417 pt.x -= mi.rcMonitor.left; 00418 pt.y -= mi.rcMonitor.top; 00419 00420 ScreenOffset.cx = mi.rcMonitor.left; 00421 ScreenOffset.cy = mi.rcMonitor.top; 00422 rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left; 00423 rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top; 00424 } 00425 else 00426 { 00427 GetPrimaryScreenRect: 00428 ScreenOffset.cx = 0; 00429 ScreenOffset.cy = 0; 00430 rcScreen.right = GetSystemMetrics(SM_CXSCREEN); 00431 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); 00432 } 00433 00434 /* Calculate the nearest screen border */ 00435 if (pt.x < rcScreen.right / 2) 00436 { 00437 DeltaPt.cx = pt.x; 00438 PosH = ABE_LEFT; 00439 } 00440 else 00441 { 00442 DeltaPt.cx = rcScreen.right - pt.x; 00443 PosH = ABE_RIGHT; 00444 } 00445 00446 if (pt.y < rcScreen.bottom / 2) 00447 { 00448 DeltaPt.cy = pt.y; 00449 PosV = ABE_TOP; 00450 } 00451 else 00452 { 00453 DeltaPt.cy = rcScreen.bottom - pt.y; 00454 PosV = ABE_BOTTOM; 00455 } 00456 00457 Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV; 00458 00459 /* Fix the screen origin to be relative to the primary monitor again */ 00460 OffsetRect(&rcScreen, 00461 ScreenOffset.cx, 00462 ScreenOffset.cy); 00463 00464 hMonNew = ITrayWindowImpl_GetMonitorFromRect(This, 00465 &This->rcTrayWnd[Pos]); 00466 if (hMon != hMonNew) 00467 { 00468 SIZE szTray; 00469 00470 /* Recalculate the rectangle, we're dragging to another monitor. 00471 We don't need to recalculate the rect on single monitor systems. */ 00472 szTray.cx = This->rcTrayWnd[Pos].right - This->rcTrayWnd[Pos].left; 00473 szTray.cy = This->rcTrayWnd[Pos].bottom - This->rcTrayWnd[Pos].top; 00474 00475 ITrayWindowImpl_GetTrayRectFromScreenRect(This, 00476 Pos, 00477 &rcScreen, 00478 &szTray, 00479 pRect); 00480 00481 hMon = hMonNew; 00482 } 00483 else 00484 { 00485 /* The user is dragging the tray window on the same monitor. We don't need 00486 to recalculate the rectangle */ 00487 *pRect = This->rcTrayWnd[Pos]; 00488 } 00489 00490 *phMonitor = hMon; 00491 00492 return Pos; 00493 } 00494 00495 static DWORD 00496 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl *This, 00497 IN OUT RECT *pRect, 00498 OUT HMONITOR *phMonitor) 00499 { 00500 POINT pt; 00501 00502 /* Calculate the center of the rectangle. We call 00503 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid 00504 dragging rectangle */ 00505 pt.x = pRect->left + ((pRect->right - pRect->left) / 2); 00506 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); 00507 00508 return ITrayWindowImpl_GetDraggingRectFromPt(This, 00509 pt, 00510 pRect, 00511 phMonitor); 00512 } 00513 00514 static VOID 00515 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl *This, 00516 IN OUT LPWINDOWPOS pwp) 00517 { 00518 RECT rcTray; 00519 00520 if (This->IsDragging) 00521 { 00522 rcTray.left = pwp->x; 00523 rcTray.top = pwp->y; 00524 rcTray.right = rcTray.left + pwp->cx; 00525 rcTray.bottom = rcTray.top + pwp->cy; 00526 00527 if (!EqualRect(&rcTray, 00528 &This->rcTrayWnd[This->DraggingPosition])) 00529 { 00530 /* Recalculate the rectangle, the user dragged the tray 00531 window to another monitor or the window was somehow else 00532 moved or resized */ 00533 This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromRect(This, 00534 &rcTray, 00535 &This->DraggingMonitor); 00536 //This->rcTrayWnd[This->DraggingPosition] = rcTray; 00537 } 00538 00539 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This, 00540 // This->DraggingPosition, 00541 // &rcTray); 00542 00543 This->Monitor = This->DraggingMonitor; 00544 This->Position = This->DraggingPosition; 00545 This->IsDragging = FALSE; 00546 00547 This->rcTrayWnd[This->Position] = rcTray; 00548 goto ChangePos; 00549 } 00550 else if (GetWindowRect(This->hWnd, 00551 &rcTray)) 00552 { 00553 if (This->InSizeMove) 00554 { 00555 if (!(pwp->flags & SWP_NOMOVE)) 00556 { 00557 rcTray.left = pwp->x; 00558 rcTray.top = pwp->y; 00559 } 00560 00561 if (!(pwp->flags & SWP_NOSIZE)) 00562 { 00563 rcTray.right = rcTray.left + pwp->cx; 00564 rcTray.bottom = rcTray.top + pwp->cy; 00565 } 00566 00567 This->Position = ITrayWindowImpl_GetDraggingRectFromRect(This, 00568 &rcTray, 00569 &This->Monitor); 00570 00571 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE))) 00572 { 00573 SIZE szWnd; 00574 00575 szWnd.cx = pwp->cx; 00576 szWnd.cy = pwp->cy; 00577 00578 ITrayWindowImpl_MakeTrayRectWithSize(This->Position, 00579 &szWnd, 00580 &rcTray); 00581 } 00582 00583 This->rcTrayWnd[This->Position] = rcTray; 00584 } 00585 else 00586 { 00587 /* If the user isn't resizing the tray window we need to make sure the 00588 new size or position is valid. This is to prevent changes to the window 00589 without user interaction. */ 00590 rcTray = This->rcTrayWnd[This->Position]; 00591 } 00592 00593 ChangePos: 00594 This->TraySize.cx = rcTray.right - rcTray.left; 00595 This->TraySize.cy = rcTray.bottom - rcTray.top; 00596 00597 pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE); 00598 pwp->x = rcTray.left; 00599 pwp->y = rcTray.top; 00600 pwp->cx = This->TraySize.cx; 00601 pwp->cy = This->TraySize.cy; 00602 } 00603 } 00604 00605 static VOID 00606 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl *This, 00607 IN BOOL Clip) 00608 { 00609 RECT rcClip, rcWindow; 00610 HRGN hClipRgn; 00611 00612 if (GetWindowRect(This->hWnd, 00613 &rcWindow)) 00614 { 00615 /* Disable clipping on systems with only one monitor */ 00616 if (GetSystemMetrics(SM_CMONITORS) <= 1) 00617 Clip = FALSE; 00618 00619 if (Clip) 00620 { 00621 rcClip = rcWindow; 00622 00623 ITrayWindowImpl_GetScreenRect(This, 00624 This->Monitor, 00625 &rcClip); 00626 00627 if (!IntersectRect(&rcClip, 00628 &rcClip, 00629 &rcWindow)) 00630 { 00631 rcClip = rcWindow; 00632 } 00633 00634 OffsetRect(&rcClip, 00635 -rcWindow.left, 00636 -rcWindow.top); 00637 00638 hClipRgn = CreateRectRgnIndirect(&rcClip); 00639 } 00640 else 00641 hClipRgn = NULL; 00642 00643 /* Set the clipping region or make sure the window isn't clipped 00644 by disabling it explicitly. */ 00645 SetWindowRgn(This->hWnd, 00646 hClipRgn, 00647 TRUE); 00648 } 00649 } 00650 00651 static VOID 00652 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl *This) 00653 { 00654 RECT rcTray,rcWorkArea; 00655 00656 /* If monitor has changed then fix the previous monitors work area */ 00657 if(This->PreviousMonitor!=This->Monitor) 00658 { 00659 ITrayWindowImpl_GetScreenRect(This, 00660 This->PreviousMonitor, 00661 &rcWorkArea); 00662 SystemParametersInfo(SPI_SETWORKAREA, 00663 1, 00664 &rcWorkArea, 00665 SPIF_SENDCHANGE); 00666 } 00667 00668 rcTray = This->rcTrayWnd[This->Position]; 00669 00670 ITrayWindowImpl_GetScreenRect(This, 00671 This->Monitor, 00672 &rcWorkArea); 00673 This->PreviousMonitor=This->Monitor; 00674 00675 /* If AutoHide is false then change the workarea to exclude the area that 00676 the taskbar covers. */ 00677 if(!This->AutoHide) 00678 { 00679 switch(This->Position) 00680 { 00681 case ABE_TOP: 00682 rcWorkArea.top=rcTray.bottom; 00683 break; 00684 case ABE_LEFT: 00685 rcWorkArea.left=rcTray.right; 00686 break; 00687 case ABE_RIGHT: 00688 rcWorkArea.right=rcTray.left; 00689 break; 00690 case ABE_BOTTOM: 00691 rcWorkArea.bottom=rcTray.top; 00692 break; 00693 } 00694 } 00695 00696 SystemParametersInfo(SPI_SETWORKAREA, 00697 1, 00698 &rcWorkArea, 00699 SPIF_SENDCHANGE); 00700 } 00701 00702 static VOID 00703 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl *This) 00704 { 00705 RECT rcTray; 00706 00707 rcTray = This->rcTrayWnd[This->Position]; 00708 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom); 00709 00710 /* Move the tray window */ 00711 SetWindowPos(This->hWnd, 00712 NULL, 00713 rcTray.left, 00714 rcTray.top, 00715 rcTray.right - rcTray.left, 00716 rcTray.bottom - rcTray.top, 00717 SWP_NOZORDER); 00718 00719 ITrayWindowImpl_ResizeWorkArea(This); 00720 00721 ITrayWindowImpl_ApplyClipping(This, 00722 TRUE); 00723 } 00724 00725 typedef struct _TW_STUCKRECTS2 00726 { 00727 DWORD cbSize; 00728 LONG Unknown; 00729 DWORD dwFlags; 00730 DWORD Position; 00731 SIZE Size; 00732 RECT Rect; 00733 } TW_STRUCKRECTS2, *PTW_STUCKRECTS2; 00734 00735 static VOID 00736 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl *This) 00737 { 00738 DWORD Pos; 00739 TW_STRUCKRECTS2 sr; 00740 RECT rcScreen; 00741 SIZE WndSize, EdgeSize, DlgFrameSize; 00742 DWORD cbSize = sizeof(sr); 00743 00744 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE); 00745 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE); 00746 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME); 00747 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME); 00748 00749 if (SHGetValue(hkExplorer, 00750 TEXT("StuckRects2"), 00751 TEXT("Settings"), 00752 NULL, 00753 &sr, 00754 &cbSize) == ERROR_SUCCESS && 00755 sr.cbSize == sizeof(sr)) 00756 { 00757 This->AutoHide = (sr.dwFlags & ABS_AUTOHIDE) != 0; 00758 This->AlwaysOnTop = (sr.dwFlags & ABS_ALWAYSONTOP) != 0; 00759 This->SmSmallIcons = (sr.dwFlags & 0x4) != 0; 00760 This->HideClock = (sr.dwFlags & 0x8) != 0; 00761 00762 /* FIXME: Are there more flags? */ 00763 00764 if (This->hWnd != NULL) 00765 SetWindowPos (This->hWnd, 00766 This->AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 00767 0, 00768 0, 00769 0, 00770 0, 00771 SWP_NOMOVE | SWP_NOSIZE); 00772 00773 if (sr.Position > ABE_BOTTOM) 00774 This->Position = ABE_BOTTOM; 00775 else 00776 This->Position = sr.Position; 00777 00778 /* Try to find out which monitor the tray window was located on last. 00779 Here we're only interested in the monitor screen that we think 00780 is the last one used. We're going to determine on which monitor 00781 we really are after calculating the docked position. */ 00782 rcScreen = sr.Rect; 00783 ITrayWindowImpl_GetScreenRectFromRect(This, 00784 &rcScreen, 00785 MONITOR_DEFAULTTONEAREST); 00786 } 00787 else 00788 { 00789 This->Position = ABE_BOTTOM; 00790 00791 /* Use the minimum size of the taskbar, we'll use the start 00792 button as a minimum for now. Make sure we calculate the 00793 entire window size, not just the client size. However, we 00794 use a thinner border than a standard thick border, so that 00795 the start button and bands are not stuck to the screen border. */ 00796 sr.Size.cx = This->StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx)); 00797 sr.Size.cy = This->StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); 00798 00799 /* Use the primary screen by default */ 00800 rcScreen.left = 0; 00801 rcScreen.top = 0; 00802 rcScreen.right = GetSystemMetrics(SM_CXSCREEN); 00803 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); 00804 ITrayWindowImpl_GetScreenRectFromRect(This, 00805 &rcScreen, 00806 MONITOR_DEFAULTTOPRIMARY); 00807 } 00808 00809 /* Determine a minimum tray window rectangle. The "client" height is 00810 zero here since we cannot determine an optimal minimum width when 00811 loaded as a vertical tray window. We just need to make sure the values 00812 loaded from the registry are at least. The windows explorer behaves 00813 the same way, it allows the user to save a zero width vertical tray 00814 window, but not a zero height horizontal tray window. */ 00815 WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx); 00816 WndSize.cy = This->StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); 00817 00818 if (WndSize.cx < sr.Size.cx) 00819 WndSize.cx = sr.Size.cx; 00820 if (WndSize.cy < sr.Size.cy) 00821 WndSize.cy = sr.Size.cy; 00822 00823 /* Save the calculated size */ 00824 This->TraySize = WndSize; 00825 00826 /* Calculate all docking rectangles. We need to do this here so they're 00827 initialized and dragging the tray window to another position gives 00828 usable results */ 00829 for (Pos = ABE_LEFT; 00830 Pos <= ABE_BOTTOM; 00831 Pos++) 00832 { 00833 ITrayWindowImpl_GetTrayRectFromScreenRect(This, 00834 Pos, 00835 &rcScreen, 00836 &This->TraySize, 00837 &This->rcTrayWnd[Pos]); 00838 // DbgPrint("rcTrayWnd[%d(%d)]: %d,%d,%d,%d\n", Pos, This->Position, This->rcTrayWnd[Pos].left, This->rcTrayWnd[Pos].top, This->rcTrayWnd[Pos].right, This->rcTrayWnd[Pos].bottom); 00839 } 00840 00841 /* Determine which monitor we are on. It shouldn't matter which docked 00842 position rectangle we use */ 00843 This->Monitor = ITrayWindowImpl_GetMonitorFromRect(This, 00844 &This->rcTrayWnd[ABE_LEFT]); 00845 } 00846 00847 static UINT 00848 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl *This, 00849 IN HMENU hMenu, 00850 IN POINT *ppt OPTIONAL, 00851 IN HWND hwndExclude OPTIONAL, 00852 IN BOOL TrackUp, 00853 IN BOOL IsContextMenu) 00854 { 00855 TPMPARAMS tmp, *ptmp = NULL; 00856 POINT pt; 00857 UINT cmdId; 00858 UINT fuFlags; 00859 00860 if (hwndExclude != NULL) 00861 { 00862 /* Get the client rectangle and map it to screen coordinates */ 00863 if (GetClientRect(hwndExclude, 00864 &tmp.rcExclude) && 00865 MapWindowPoints(hwndExclude, 00866 NULL, 00867 (LPPOINT)&tmp.rcExclude, 00868 2) != 0) 00869 { 00870 ptmp = &tmp; 00871 } 00872 } 00873 00874 if (ppt == NULL) 00875 { 00876 if (ptmp == NULL && 00877 GetClientRect(This->hWnd, 00878 &tmp.rcExclude) && 00879 MapWindowPoints(This->hWnd, 00880 NULL, 00881 (LPPOINT)&tmp.rcExclude, 00882 2) != 0) 00883 { 00884 ptmp = &tmp; 00885 } 00886 00887 if (ptmp != NULL) 00888 { 00889 /* NOTE: TrackPopupMenuEx will eventually align the track position 00890 for us, no need to take care of it here as long as the 00891 coordinates are somewhere within the exclusion rectangle */ 00892 pt.x = ptmp->rcExclude.left; 00893 pt.y = ptmp->rcExclude.top; 00894 } 00895 else 00896 pt.x = pt.y = 0; 00897 } 00898 else 00899 pt = *ppt; 00900 00901 tmp.cbSize = sizeof(tmp); 00902 00903 fuFlags = TPM_RETURNCMD | TPM_VERTICAL; 00904 fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN); 00905 if (IsContextMenu) 00906 fuFlags |= TPM_RIGHTBUTTON; 00907 else 00908 fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION); 00909 00910 cmdId = TrackPopupMenuEx(hMenu, 00911 fuFlags, 00912 pt.x, 00913 pt.y, 00914 This->hWnd, 00915 ptmp); 00916 00917 return cmdId; 00918 } 00919 00920 static UINT 00921 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl *This, 00922 IN const TRAYWINDOW_CTXMENU *pMenu, 00923 IN POINT *ppt OPTIONAL, 00924 IN HWND hwndExclude OPTIONAL, 00925 IN BOOL TrackUp, 00926 IN PVOID Context OPTIONAL) 00927 { 00928 HMENU hPopup; 00929 UINT cmdId = 0; 00930 PVOID pcmContext = NULL; 00931 00932 hPopup = pMenu->CreateCtxMenu(This->hWnd, 00933 &pcmContext, 00934 Context); 00935 if (hPopup != NULL) 00936 { 00937 cmdId = ITrayWindowImpl_TrackMenu(This, 00938 hPopup, 00939 ppt, 00940 hwndExclude, 00941 TrackUp, 00942 TRUE); 00943 00944 pMenu->CtxMenuCommand(This->hWnd, 00945 cmdId, 00946 pcmContext, 00947 Context); 00948 } 00949 00950 return cmdId; 00951 } 00952 00953 static VOID 00954 ITrayWindowImpl_Free(ITrayWindowImpl *This) 00955 { 00956 HeapFree(hProcessHeap, 00957 0, 00958 This); 00959 } 00960 00961 00962 static ULONG STDMETHODCALLTYPE 00963 ITrayWindowImpl_Release(IN OUT ITrayWindow *iface) 00964 { 00965 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 00966 ULONG Ret; 00967 00968 Ret = InterlockedDecrement(&This->Ref); 00969 if (Ret == 0) 00970 ITrayWindowImpl_Free(This); 00971 00972 return Ret; 00973 } 00974 00975 static VOID 00976 ITrayWindowImpl_Destroy(ITrayWindowImpl *This) 00977 { 00978 (void)InterlockedExchangePointer((PVOID*)&This->hWnd, 00979 NULL); 00980 00981 if (This->himlStartBtn != NULL) 00982 { 00983 ImageList_Destroy(This->himlStartBtn); 00984 This->himlStartBtn = NULL; 00985 } 00986 00987 if (This->hCaptionFont != NULL) 00988 { 00989 DeleteObject(This->hCaptionFont); 00990 This->hCaptionFont = NULL; 00991 } 00992 00993 if (This->hStartBtnFont != NULL) 00994 { 00995 DeleteObject(This->hStartBtnFont); 00996 This->hStartBtnFont = NULL; 00997 } 00998 00999 if (This->hFont != NULL) 01000 { 01001 DeleteObject(This->hFont); 01002 This->hFont = NULL; 01003 } 01004 01005 if (This->StartMenuPopup != NULL) 01006 { 01007 IMenuPopup_Release(This->StartMenuPopup); 01008 This->StartMenuPopup = NULL; 01009 } 01010 01011 if (This->hbmStartMenu != NULL) 01012 { 01013 DeleteObject(This->hbmStartMenu); 01014 This->hbmStartMenu = NULL; 01015 } 01016 01017 if (This->StartMenuBand != NULL) 01018 { 01019 IMenuBand_Release(This->StartMenuBand); 01020 This->StartMenuBand = NULL; 01021 } 01022 01023 if (This->TrayBandSite != NULL) 01024 { 01025 /* FIXME: Unload bands */ 01026 ITrayBandSite_Release(This->TrayBandSite); 01027 This->TrayBandSite = NULL; 01028 } 01029 01030 ITrayWindowImpl_Release(ITrayWindow_from_impl(This)); 01031 01032 if (InterlockedDecrement(&TrayWndCount) == 0) 01033 PostQuitMessage(0); 01034 } 01035 01036 static ULONG STDMETHODCALLTYPE 01037 ITrayWindowImpl_AddRef(IN OUT ITrayWindow *iface) 01038 { 01039 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01040 01041 return InterlockedIncrement(&This->Ref); 01042 } 01043 01044 01045 static BOOL 01046 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl *This) 01047 { 01048 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This)); 01049 01050 return TRUE; 01051 } 01052 01053 static VOID 01054 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl *This, 01055 IN HBITMAP hbmStart OPTIONAL) 01056 { 01057 SIZE Size = { 0, 0 }; 01058 01059 if (This->himlStartBtn == NULL || 01060 !SendMessage(This->hwndStart, 01061 BCM_GETIDEALSIZE, 01062 0, 01063 (LPARAM)&Size)) 01064 { 01065 Size.cx = GetSystemMetrics(SM_CXEDGE); 01066 Size.cy = GetSystemMetrics(SM_CYEDGE); 01067 01068 if (hbmStart == NULL) 01069 { 01070 hbmStart = (HBITMAP)SendMessage(This->hwndStart, 01071 BM_GETIMAGE, 01072 IMAGE_BITMAP, 01073 0); 01074 } 01075 01076 if (hbmStart != NULL) 01077 { 01078 BITMAP bmp; 01079 01080 if (GetObject(hbmStart, 01081 sizeof(bmp), 01082 &bmp) != 0) 01083 { 01084 Size.cx += bmp.bmWidth; 01085 Size.cy += max(bmp.bmHeight, 01086 GetSystemMetrics(SM_CYCAPTION)); 01087 } 01088 else 01089 { 01090 /* Huh?! Shouldn't happen... */ 01091 goto DefSize; 01092 } 01093 } 01094 else 01095 { 01096 DefSize: 01097 Size.cx += GetSystemMetrics(SM_CXMINIMIZED); 01098 Size.cy += GetSystemMetrics(SM_CYCAPTION); 01099 } 01100 } 01101 01102 /* Save the size of the start button */ 01103 This->StartBtnSize = Size; 01104 } 01105 01106 static VOID 01107 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl *This, 01108 IN PRECT prcClient OPTIONAL) 01109 { 01110 RECT rcClient; 01111 SIZE TraySize, StartSize; 01112 POINT ptTrayNotify = { 0, 0 }; 01113 BOOL Horizontal; 01114 HDWP dwp; 01115 01116 if (prcClient != NULL) 01117 { 01118 rcClient = *prcClient; 01119 } 01120 else 01121 { 01122 if (!GetClientRect(This->hWnd, 01123 &rcClient)) 01124 { 01125 return; 01126 } 01127 } 01128 01129 Horizontal = ITrayWindowImpl_IsPosHorizontal(This); 01130 01131 /* We're about to resize/move the start button, the rebar control and 01132 the tray notification control */ 01133 dwp = BeginDeferWindowPos(3); 01134 if (dwp == NULL) 01135 return; 01136 01137 /* Limit the Start button width to the client width, if neccessary */ 01138 StartSize = This->StartBtnSize; 01139 if (StartSize.cx > rcClient.right) 01140 StartSize.cx = rcClient.right; 01141 01142 if (This->hwndStart != NULL) 01143 { 01144 /* Resize and reposition the button */ 01145 dwp = DeferWindowPos(dwp, 01146 This->hwndStart, 01147 NULL, 01148 0, 01149 0, 01150 StartSize.cx, 01151 StartSize.cy, 01152 SWP_NOZORDER | SWP_NOACTIVATE); 01153 if (dwp == NULL) 01154 return; 01155 } 01156 01157 /* Determine the size that the tray notification window needs */ 01158 if (Horizontal) 01159 { 01160 TraySize.cx = 0; 01161 TraySize.cy = rcClient.bottom; 01162 } 01163 else 01164 { 01165 TraySize.cx = rcClient.right; 01166 TraySize.cy = 0; 01167 } 01168 01169 if (This->hwndTrayNotify != NULL && 01170 SendMessage(This->hwndTrayNotify, 01171 TNWM_GETMINIMUMSIZE, 01172 (WPARAM)Horizontal, 01173 (LPARAM)&TraySize)) 01174 { 01175 /* Move the tray notification window to the desired location */ 01176 if (Horizontal) 01177 ptTrayNotify.x = rcClient.right - TraySize.cx; 01178 else 01179 ptTrayNotify.y = rcClient.bottom - TraySize.cy; 01180 01181 dwp = DeferWindowPos(dwp, 01182 This->hwndTrayNotify, 01183 NULL, 01184 ptTrayNotify.x, 01185 ptTrayNotify.y, 01186 TraySize.cx, 01187 TraySize.cy, 01188 SWP_NOZORDER | SWP_NOACTIVATE); 01189 if (dwp == NULL) 01190 return; 01191 } 01192 01193 /* Resize/Move the rebar control */ 01194 if (This->hwndRebar != NULL) 01195 { 01196 POINT ptRebar = { 0, 0 }; 01197 SIZE szRebar; 01198 01199 SetWindowStyle(This->hwndRebar, 01200 CCS_VERT, 01201 Horizontal ? 0 : CCS_VERT); 01202 01203 if (Horizontal) 01204 { 01205 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME); 01206 szRebar.cx = ptTrayNotify.x - ptRebar.x; 01207 szRebar.cy = rcClient.bottom; 01208 } 01209 else 01210 { 01211 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME); 01212 szRebar.cx = rcClient.right; 01213 szRebar.cy = ptTrayNotify.y - ptRebar.y; 01214 } 01215 01216 dwp = DeferWindowPos(dwp, 01217 This->hwndRebar, 01218 NULL, 01219 ptRebar.x, 01220 ptRebar.y, 01221 szRebar.cx, 01222 szRebar.cy, 01223 SWP_NOZORDER | SWP_NOACTIVATE); 01224 } 01225 01226 if (dwp != NULL) 01227 EndDeferWindowPos(dwp); 01228 01229 if (This->hwndTaskSwitch != NULL) 01230 { 01231 /* Update the task switch window configuration */ 01232 SendMessage(This->hwndTaskSwitch, 01233 TSWM_UPDATETASKBARPOS, 01234 0, 01235 0); 01236 } 01237 } 01238 01239 static BOOL 01240 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl *This) 01241 { 01242 HICON hIconStart; 01243 SIZE IconSize; 01244 01245 if (This->himlStartBtn != NULL) 01246 return TRUE; 01247 01248 IconSize.cx = GetSystemMetrics(SM_CXSMICON); 01249 IconSize.cy = GetSystemMetrics(SM_CYSMICON); 01250 01251 /* Load the start button icon and create a image list for it */ 01252 hIconStart = LoadImage(hExplorerInstance, 01253 MAKEINTRESOURCE(IDI_START), 01254 IMAGE_ICON, 01255 IconSize.cx, 01256 IconSize.cy, 01257 LR_SHARED | LR_DEFAULTCOLOR); 01258 01259 if (hIconStart != NULL) 01260 { 01261 This->himlStartBtn = ImageList_Create(IconSize.cx, 01262 IconSize.cy, 01263 ILC_COLOR32 | ILC_MASK, 01264 1, 01265 1); 01266 if (This->himlStartBtn != NULL) 01267 { 01268 if (ImageList_AddIcon(This->himlStartBtn, 01269 hIconStart) >= 0) 01270 { 01271 return TRUE; 01272 } 01273 01274 /* Failed to add the icon! */ 01275 ImageList_Destroy(This->himlStartBtn); 01276 This->himlStartBtn = NULL; 01277 } 01278 } 01279 01280 return FALSE; 01281 } 01282 01283 static HBITMAP 01284 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl *This) 01285 { 01286 TCHAR szStartCaption[32]; 01287 HFONT hFontOld; 01288 HDC hDC = NULL; 01289 HDC hDCScreen = NULL; 01290 SIZE Size, SmallIcon; 01291 HBITMAP hbmpOld, hbmp = NULL; 01292 HBITMAP hBitmap = NULL; 01293 HICON hIconStart; 01294 BOOL Ret; 01295 UINT Flags; 01296 RECT rcButton; 01297 01298 /* NOTE: This is the backwards compatibility code that is used if the 01299 Common Controls Version 6.0 are not available! */ 01300 01301 if (!LoadString(hExplorerInstance, 01302 IDS_START, 01303 szStartCaption, 01304 sizeof(szStartCaption) / sizeof(szStartCaption[0]))) 01305 { 01306 return NULL; 01307 } 01308 01309 /* Load the start button icon */ 01310 SmallIcon.cx = GetSystemMetrics(SM_CXSMICON); 01311 SmallIcon.cy = GetSystemMetrics(SM_CYSMICON); 01312 hIconStart = LoadImage(hExplorerInstance, 01313 MAKEINTRESOURCE(IDI_START), 01314 IMAGE_ICON, 01315 SmallIcon.cx, 01316 SmallIcon.cy, 01317 LR_SHARED | LR_DEFAULTCOLOR); 01318 01319 hDCScreen = GetDC(NULL); 01320 if (hDCScreen == NULL) 01321 goto Cleanup; 01322 01323 hDC = CreateCompatibleDC(hDCScreen); 01324 if (hDC == NULL) 01325 goto Cleanup; 01326 01327 hFontOld = SelectObject(hDC, 01328 This->hStartBtnFont); 01329 01330 Ret = GetTextExtentPoint32(hDC, 01331 szStartCaption, 01332 _tcslen(szStartCaption), 01333 &Size); 01334 01335 SelectObject(hDC, 01336 hFontOld); 01337 if (!Ret) 01338 goto Cleanup; 01339 01340 /* Make sure the height is at least the size of a caption icon. */ 01341 if (hIconStart != NULL) 01342 Size.cx += SmallIcon.cx + 4; 01343 Size.cy = max(Size.cy, 01344 SmallIcon.cy); 01345 01346 /* Create the bitmap */ 01347 hbmp = CreateCompatibleBitmap(hDCScreen, 01348 Size.cx, 01349 Size.cy); 01350 if (hbmp == NULL) 01351 goto Cleanup; 01352 01353 /* Caluclate the button rect */ 01354 rcButton.left = 0; 01355 rcButton.top = 0; 01356 rcButton.right = Size.cx; 01357 rcButton.bottom = Size.cy; 01358 01359 /* Draw the button */ 01360 hbmpOld = SelectObject(hDC, 01361 hbmp); 01362 01363 Flags = DC_TEXT | DC_INBUTTON; 01364 if (hIconStart != NULL) 01365 Flags |= DC_ICON; 01366 01367 if (DrawCapTemp != NULL) 01368 { 01369 Ret = DrawCapTemp(NULL, 01370 hDC, 01371 &rcButton, 01372 This->hStartBtnFont, 01373 hIconStart, 01374 szStartCaption, 01375 Flags); 01376 } 01377 01378 SelectObject(hDC, 01379 hbmpOld); 01380 01381 if (!Ret) 01382 goto Cleanup; 01383 01384 /* We successfully created the bitmap! */ 01385 hBitmap = hbmp; 01386 hbmp = NULL; 01387 01388 Cleanup: 01389 if (hDCScreen != NULL) 01390 { 01391 ReleaseDC(NULL, 01392 hDCScreen); 01393 } 01394 01395 if (hbmp != NULL) 01396 DeleteObject(hbmp); 01397 01398 if (hDC != NULL) 01399 DeleteDC(hDC); 01400 01401 return hBitmap; 01402 } 01403 01404 static VOID 01405 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl *This) 01406 { 01407 TCHAR szStartCaption[32]; 01408 01409 InterlockedIncrement(&TrayWndCount); 01410 01411 if (!LoadString(hExplorerInstance, 01412 IDS_START, 01413 szStartCaption, 01414 sizeof(szStartCaption) / sizeof(szStartCaption[0]))) 01415 { 01416 szStartCaption[0] = TEXT('\0'); 01417 } 01418 01419 if (This->hStartBtnFont == NULL || This->hCaptionFont == NULL) 01420 { 01421 NONCLIENTMETRICS ncm; 01422 01423 /* Get the system fonts, we use the caption font, 01424 always bold, though. */ 01425 ncm.cbSize = sizeof(ncm); 01426 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 01427 sizeof(ncm), 01428 &ncm, 01429 FALSE)) 01430 { 01431 if (This->hCaptionFont == NULL) 01432 { 01433 ncm.lfCaptionFont.lfWeight = FW_NORMAL; 01434 This->hCaptionFont = CreateFontIndirect(&ncm.lfCaptionFont); 01435 } 01436 01437 if (This->hStartBtnFont == NULL) 01438 { 01439 ncm.lfCaptionFont.lfWeight = FW_BOLD; 01440 This->hStartBtnFont = CreateFontIndirect(&ncm.lfCaptionFont); 01441 } 01442 } 01443 } 01444 01445 /* Create the Start button */ 01446 This->hwndStart = CreateWindowEx(0, 01447 WC_BUTTON, 01448 szStartCaption, 01449 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | 01450 BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 01451 0, 01452 0, 01453 0, 01454 0, 01455 This->hWnd, 01456 (HMENU)IDC_STARTBTN, 01457 hExplorerInstance, 01458 NULL); 01459 if (This->hwndStart) 01460 { 01461 SendMessage(This->hwndStart, 01462 WM_SETFONT, 01463 (WPARAM)This->hStartBtnFont, 01464 FALSE); 01465 01466 if (ITrayWindowImpl_CreateStartBtnImageList(This)) 01467 { 01468 BUTTON_IMAGELIST bil; 01469 01470 /* Try to set the start button image. This requires the Common 01471 Controls 6.0 to be present (XP and later) */ 01472 bil.himl = This->himlStartBtn; 01473 bil.margin.left = bil.margin.right = 1; 01474 bil.margin.top = bil.margin.bottom = 1; 01475 bil.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; 01476 01477 if (!SendMessage(This->hwndStart, 01478 BCM_SETIMAGELIST, 01479 0, 01480 (LPARAM)&bil)) 01481 { 01482 /* Fall back to the deprecated method on older systems that don't 01483 support Common Controls 6.0 */ 01484 ImageList_Destroy(This->himlStartBtn); 01485 This->himlStartBtn = NULL; 01486 01487 goto SetStartBtnImage; 01488 } 01489 01490 /* We're using the image list, remove the BS_BITMAP style and 01491 don't center it horizontally */ 01492 SetWindowStyle(This->hwndStart, 01493 BS_BITMAP | BS_RIGHT, 01494 0); 01495 01496 ITrayWindowImpl_UpdateStartButton(This, 01497 NULL); 01498 } 01499 else 01500 { 01501 HBITMAP hbmStart, hbmOld; 01502 01503 SetStartBtnImage: 01504 hbmStart = ITrayWindowImpl_CreateStartButtonBitmap(This); 01505 if (hbmStart != NULL) 01506 { 01507 ITrayWindowImpl_UpdateStartButton(This, 01508 hbmStart); 01509 01510 hbmOld = (HBITMAP)SendMessage(This->hwndStart, 01511 BM_SETIMAGE, 01512 IMAGE_BITMAP, 01513 (LPARAM)hbmStart); 01514 01515 if (hbmOld != NULL) 01516 DeleteObject(hbmOld); 01517 } 01518 } 01519 } 01520 01521 /* Load the saved tray window settings */ 01522 ITrayWindowImpl_RegLoadSettings(This); 01523 01524 /* Create and initialize the start menu */ 01525 This->hbmStartMenu = LoadBitmap(hExplorerInstance, 01526 MAKEINTRESOURCE(IDB_STARTMENU)); 01527 This->StartMenuPopup = CreateStartMenu(ITrayWindow_from_impl(This), 01528 &This->StartMenuBand, 01529 This->hbmStartMenu, 01530 0); 01531 01532 /* Load the tray band site */ 01533 if (This->TrayBandSite != NULL) 01534 { 01535 ITrayBandSite_Release(This->TrayBandSite); 01536 } 01537 01538 This->TrayBandSite = CreateTrayBandSite(ITrayWindow_from_impl(This), 01539 &This->hwndRebar, 01540 &This->hwndTaskSwitch); 01541 01542 /* Create the tray notification window */ 01543 This->hwndTrayNotify = CreateTrayNotifyWnd(ITrayWindow_from_impl(This), 01544 This->HideClock); 01545 01546 if (ITrayWindowImpl_UpdateNonClientMetrics(This)) 01547 { 01548 ITrayWindowImpl_SetWindowsFont(This); 01549 } 01550 01551 /* Move the tray window to the right position and resize it if neccessary */ 01552 ITrayWindowImpl_CheckTrayWndPosition(This); 01553 01554 /* Align all controls on the tray window */ 01555 ITrayWindowImpl_AlignControls(This, 01556 NULL); 01557 } 01558 01559 static HRESULT STDMETHODCALLTYPE 01560 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow *iface, 01561 IN REFIID riid, 01562 OUT LPVOID *ppvObj) 01563 { 01564 ITrayWindowImpl *This; 01565 01566 if (ppvObj == NULL) 01567 return E_POINTER; 01568 01569 This = impl_from_ITrayWindow(iface); 01570 01571 if (IsEqualIID(riid, 01572 &IID_IUnknown)) 01573 { 01574 *ppvObj = IUnknown_from_impl(This); 01575 } 01576 else if (IsEqualIID(riid, 01577 &IID_IShellDesktopTray)) 01578 { 01579 *ppvObj = IShellDesktopTray_from_impl(This); 01580 } 01581 else 01582 { 01583 *ppvObj = NULL; 01584 return E_NOINTERFACE; 01585 } 01586 01587 ITrayWindowImpl_AddRef(iface); 01588 return S_OK; 01589 } 01590 01591 static ITrayWindowImpl * 01592 ITrayWindowImpl_Construct(VOID) 01593 { 01594 ITrayWindowImpl *This; 01595 01596 This = HeapAlloc(hProcessHeap, 01597 0, 01598 sizeof(*This)); 01599 if (This == NULL) 01600 return NULL; 01601 01602 ZeroMemory(This, 01603 sizeof(*This)); 01604 This->lpVtbl = &ITrayWindowImpl_Vtbl; 01605 This->lpVtblShellDesktopTray = &IShellDesktopTrayImpl_Vtbl; 01606 This->Ref = 1; 01607 This->Position = (DWORD)-1; 01608 01609 return This; 01610 } 01611 01612 static HRESULT STDMETHODCALLTYPE 01613 ITrayWindowImpl_Open(IN OUT ITrayWindow *iface) 01614 { 01615 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01616 HRESULT Ret = S_OK; 01617 HWND hWnd; 01618 DWORD dwExStyle; 01619 01620 /* Check if there's already a window created and try to show it. 01621 If it was somehow destroyed just create a new tray window. */ 01622 if (This->hWnd != NULL) 01623 { 01624 if (IsWindow(This->hWnd)) 01625 { 01626 if (!IsWindowVisible(This->hWnd)) 01627 { 01628 ITrayWindowImpl_CheckTrayWndPosition(This); 01629 01630 ShowWindow(This->hWnd, 01631 SW_SHOW); 01632 } 01633 } 01634 else 01635 goto TryCreateTrayWnd; 01636 } 01637 else 01638 { 01639 RECT rcWnd; 01640 01641 TryCreateTrayWnd: 01642 dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; 01643 if (This->AlwaysOnTop) 01644 dwExStyle |= WS_EX_TOPMOST; 01645 01646 if (This->Position != (DWORD)-1) 01647 rcWnd = This->rcTrayWnd[This->Position]; 01648 else 01649 { 01650 ZeroMemory(&rcWnd, 01651 sizeof(rcWnd)); 01652 } 01653 01654 hWnd = CreateWindowEx(dwExStyle, 01655 szTrayWndClass, 01656 NULL, 01657 WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 01658 WS_BORDER | WS_THICKFRAME, 01659 rcWnd.left, 01660 rcWnd.top, 01661 rcWnd.right - rcWnd.left, 01662 rcWnd.bottom - rcWnd.top, 01663 NULL, 01664 NULL, 01665 hExplorerInstance, 01666 This); 01667 if (hWnd == NULL) 01668 Ret = E_FAIL; 01669 } 01670 01671 return Ret; 01672 } 01673 01674 static HRESULT STDMETHODCALLTYPE 01675 ITrayWindowImpl_Close(IN OUT ITrayWindow *iface) 01676 { 01677 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01678 01679 if (This->hWnd != NULL) 01680 { 01681 SendMessage(This->hWnd, 01682 WM_APP_TRAYDESTROY, 01683 0, 01684 0); 01685 } 01686 01687 return S_OK; 01688 } 01689 01690 static HWND STDMETHODCALLTYPE 01691 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow *iface) 01692 { 01693 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01694 01695 return This->hWnd; 01696 } 01697 01698 static BOOL STDMETHODCALLTYPE 01699 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow *iface, 01700 IN HWND hWnd) 01701 { 01702 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01703 01704 return (hWnd == This->hWnd || 01705 (This->hWndDesktop != NULL && hWnd == This->hWndDesktop)); 01706 } 01707 01708 static BOOL STDMETHODCALLTYPE 01709 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow *iface) 01710 { 01711 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01712 return ITrayWindowImpl_IsPosHorizontal(This); 01713 } 01714 01715 static HFONT STDMETHODCALLTYPE 01716 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow *iface, 01717 OUT HFONT *phBoldCaption OPTIONAL) 01718 { 01719 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01720 01721 if (phBoldCaption != NULL) 01722 *phBoldCaption = This->hStartBtnFont; 01723 01724 return This->hCaptionFont; 01725 } 01726 01727 static HWND STDMETHODCALLTYPE 01728 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow *iface) 01729 { 01730 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01731 01732 if (This->hWndTrayProperties != NULL) 01733 { 01734 BringWindowToTop(This->hWndTrayProperties); 01735 return This->hWndTrayProperties; 01736 } 01737 01738 This->hWndTrayProperties = DisplayTrayProperties(ITrayWindow_from_impl(This)); 01739 return This->hWndTrayProperties; 01740 } 01741 01742 static VOID 01743 OpenCommonStartMenuDirectory(IN HWND hWndOwner, 01744 IN LPCTSTR lpOperation) 01745 { 01746 TCHAR szDir[MAX_PATH]; 01747 01748 if (SHGetSpecialFolderPath(hWndOwner, 01749 szDir, 01750 CSIDL_COMMON_STARTMENU, 01751 FALSE)) 01752 { 01753 ShellExecute(hWndOwner, 01754 lpOperation, 01755 NULL, 01756 NULL, 01757 szDir, 01758 SW_SHOWNORMAL); 01759 } 01760 } 01761 01762 static VOID 01763 OpenTaskManager(IN HWND hWndOwner) 01764 { 01765 ShellExecute(hWndOwner, 01766 TEXT("open"), 01767 TEXT("taskmgr.exe"), 01768 NULL, 01769 NULL, 01770 SW_SHOWNORMAL); 01771 } 01772 01773 static BOOL STDMETHODCALLTYPE 01774 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow *iface, 01775 IN UINT uiCmd) 01776 { 01777 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01778 BOOL bHandled = TRUE; 01779 01780 switch (uiCmd) 01781 { 01782 case ID_SHELL_CMD_PROPERTIES: 01783 ITrayWindow_DisplayProperties(iface); 01784 break; 01785 01786 case ID_SHELL_CMD_OPEN_ALL_USERS: 01787 OpenCommonStartMenuDirectory(This->hWnd, 01788 TEXT("open")); 01789 break; 01790 01791 case ID_SHELL_CMD_EXPLORE_ALL_USERS: 01792 OpenCommonStartMenuDirectory(This->hWnd, 01793 TEXT("explore")); 01794 break; 01795 01796 case ID_LOCKTASKBAR: 01797 if (SHRestricted(REST_CLASSICSHELL) == 0) 01798 { 01799 ITrayWindow_Lock(iface, 01800 !This->Locked); 01801 } 01802 break; 01803 01804 case ID_SHELL_CMD_OPEN_TASKMGR: 01805 OpenTaskManager(This->hWnd); 01806 break; 01807 01808 case ID_SHELL_CMD_UNDO_ACTION: 01809 break; 01810 01811 case ID_SHELL_CMD_SHOW_DESKTOP: 01812 break; 01813 01814 case ID_SHELL_CMD_TILE_WND_H: 01815 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL); 01816 break; 01817 01818 case ID_SHELL_CMD_TILE_WND_V: 01819 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL); 01820 break; 01821 01822 case ID_SHELL_CMD_CASCADE_WND: 01823 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL); 01824 break; 01825 01826 case ID_SHELL_CMD_CUST_NOTIF: 01827 break; 01828 01829 case ID_SHELL_CMD_ADJUST_DAT: 01830 LaunchCPanel(NULL, TEXT("timedate.cpl")); 01831 break; 01832 01833 default: 01834 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd); 01835 bHandled = FALSE; 01836 break; 01837 } 01838 01839 return bHandled; 01840 } 01841 01842 static BOOL STDMETHODCALLTYPE 01843 ITrayWindowImpl_Lock(IN OUT ITrayWindow *iface, 01844 IN BOOL bLock) 01845 { 01846 BOOL bPrevLock; 01847 ITrayWindowImpl *This = impl_from_ITrayWindow(iface); 01848 01849 bPrevLock = This->Locked; 01850 if (This->Locked != bLock) 01851 { 01852 This->Locked = bLock; 01853 01854 if (This->TrayBandSite != NULL) 01855 { 01856 if (!SUCCEEDED(ITrayBandSite_Lock(This->TrayBandSite, 01857 bLock))) 01858 { 01859 /* Reset?? */ 01860 This->Locked = bPrevLock; 01861 } 01862 } 01863 } 01864 01865 return bPrevLock; 01866 } 01867 01868 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl = 01869 { 01870 /* IUnknown */ 01871 ITrayWindowImpl_QueryInterface, 01872 ITrayWindowImpl_AddRef, 01873 ITrayWindowImpl_Release, 01874 /* ITrayWindow */ 01875 ITrayWindowImpl_Open, 01876 ITrayWindowImpl_Close, 01877 ITrayWindowImpl_GetHWND, 01878 ITrayWindowImpl_IsSpecialHWND, 01879 ITrayWindowImpl_IsHorizontal, 01880 ITrayWIndowImpl_GetCaptionFonts, 01881 ITrayWindowImpl_DisplayProperties, 01882 ITrayWindowImpl_ExecContextMenuCmd, 01883 ITrayWindowImpl_Lock 01884 }; 01885 01886 static LRESULT CALLBACK 01887 TrayWndProc(IN HWND hwnd, 01888 IN UINT uMsg, 01889 IN WPARAM wParam, 01890 IN LPARAM lParam) 01891 { 01892 ITrayWindowImpl *This = NULL; 01893 LRESULT Ret = FALSE; 01894 01895 if (uMsg != WM_NCCREATE) 01896 { 01897 This = (ITrayWindowImpl*)GetWindowLongPtr(hwnd, 01898 0); 01899 } 01900 01901 if (This != NULL || uMsg == WM_NCCREATE) 01902 { 01903 if (This != NULL && This->StartMenuBand != NULL) 01904 { 01905 MSG Msg; 01906 LRESULT lRet; 01907 01908 Msg.hwnd = hwnd; 01909 Msg.message = uMsg; 01910 Msg.wParam = wParam; 01911 Msg.lParam = lParam; 01912 01913 if (IMenuBand_TranslateMenuMessage(This->StartMenuBand, 01914 &Msg, 01915 &lRet) == S_OK) 01916 { 01917 return lRet; 01918 } 01919 01920 wParam = Msg.wParam; 01921 lParam = Msg.lParam; 01922 } 01923 01924 switch (uMsg) 01925 { 01926 case WM_NCHITTEST: 01927 { 01928 RECT rcClient; 01929 POINT pt; 01930 01931 if (This->Locked) 01932 { 01933 /* The user may not be able to resize the tray window. 01934 Pretend like the window is not sizeable when the user 01935 clicks on the border. */ 01936 return HTBORDER; 01937 } 01938 01939 if (GetClientRect(hwnd, 01940 &rcClient) && 01941 MapWindowPoints(hwnd, 01942 NULL, 01943 (LPPOINT)&rcClient, 01944 2) != 0) 01945 { 01946 pt.x = (SHORT)LOWORD(lParam); 01947 pt.y = (SHORT)HIWORD(lParam); 01948 01949 if (PtInRect(&rcClient, 01950 pt)) 01951 { 01952 /* The user is trying to drag the tray window */ 01953 return HTCAPTION; 01954 } 01955 01956 /* Depending on the position of the tray window, allow only 01957 changing the border next to the monitor working area */ 01958 switch (This->Position) 01959 { 01960 case ABE_TOP: 01961 if (pt.y > rcClient.bottom) 01962 return HTBOTTOM; 01963 break; 01964 01965 case ABE_BOTTOM: 01966 if (pt.y < rcClient.top) 01967 return HTTOP; 01968 break; 01969 01970 case ABE_LEFT: 01971 if (pt.x > rcClient.right) 01972 return HTRIGHT; 01973 break; 01974 01975 case ABE_RIGHT: 01976 if (pt.x < rcClient.left) 01977 return HTLEFT; 01978 break; 01979 01980 default: 01981 break; 01982 } 01983 } 01984 01985 return HTBORDER; 01986 } 01987 01988 case WM_MOVING: 01989 { 01990 POINT ptCursor; 01991 PRECT pRect = (PRECT)lParam; 01992 01993 /* We need to ensure that an application can not accidently 01994 move the tray window (using SetWindowPos). However, we still 01995 need to be able to move the window in case the user wants to 01996 drag the tray window to another position or in case the user 01997 wants to resize the tray window. */ 01998 if (!This->Locked && GetCursorPos(&ptCursor)) 01999 { 02000 This->IsDragging = TRUE; 02001 This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromPt(This, 02002 ptCursor, 02003 pRect, 02004 &This->DraggingMonitor); 02005 } 02006 else 02007 { 02008 *pRect = This->rcTrayWnd[This->Position]; 02009 } 02010 return TRUE; 02011 } 02012 02013 case WM_SIZING: 02014 { 02015 PRECT pRect = (PRECT)lParam; 02016 02017 if (!This->Locked) 02018 { 02019 ITrayWindowImpl_CalculateValidSize(This, 02020 This->Position, 02021 pRect); 02022 } 02023 else 02024 { 02025 *pRect = This->rcTrayWnd[This->Position]; 02026 } 02027 return TRUE; 02028 } 02029 02030 case WM_WINDOWPOSCHANGING: 02031 { 02032 ITrayWindowImpl_ChangingWinPos(This, 02033 (LPWINDOWPOS)lParam); 02034 break; 02035 } 02036 02037 case WM_SIZE: 02038 { 02039 RECT rcClient; 02040 02041 if (wParam == SIZE_RESTORED && lParam == 0) 02042 { 02043 ITrayWindowImpl_ResizeWorkArea(This); 02044 /* Clip the tray window on multi monitor systems so the edges can't 02045 overlap into another monitor */ 02046 ITrayWindowImpl_ApplyClipping(This, 02047 TRUE); 02048 02049 if (!GetClientRect(This->hWnd, 02050 &rcClient)) 02051 { 02052 break; 02053 } 02054 } 02055 else 02056 { 02057 rcClient.left = rcClient.top = 0; 02058 rcClient.right = LOWORD(lParam); 02059 rcClient.bottom = HIWORD(lParam); 02060 } 02061 02062 ITrayWindowImpl_AlignControls(This, 02063 &rcClient); 02064 break; 02065 } 02066 02067 case WM_ENTERSIZEMOVE: 02068 This->InSizeMove = TRUE; 02069 This->IsDragging = FALSE; 02070 if (!This->Locked) 02071 { 02072 /* Remove the clipping on multi monitor systems while dragging around */ 02073 ITrayWindowImpl_ApplyClipping(This, 02074 FALSE); 02075 } 02076 break; 02077 02078 case WM_EXITSIZEMOVE: 02079 This->InSizeMove = FALSE; 02080 if (!This->Locked) 02081 { 02082 /* Apply clipping */ 02083 PostMessage(This->hWnd, 02084 WM_SIZE, 02085 SIZE_RESTORED, 02086 0); 02087 } 02088 break; 02089 02090 case WM_SYSCHAR: 02091 switch (wParam) 02092 { 02093 case TEXT(' '): 02094 { 02095 /* The user pressed Alt+Space, this usually brings up the system menu of a window. 02096 The tray window needs to handle this specially, since it normally doesn't have 02097 a system menu. */ 02098 02099 static const UINT uidDisableItem[] = { 02100 SC_RESTORE, 02101 SC_MOVE, 02102 SC_SIZE, 02103 SC_MAXIMIZE, 02104 SC_MINIMIZE, 02105 }; 02106 HMENU hSysMenu; 02107 INT i; 02108 UINT uId; 02109 02110 /* temporarily enable the system menu */ 02111 SetWindowStyle(hwnd, 02112 WS_SYSMENU, 02113 WS_SYSMENU); 02114 02115 hSysMenu = GetSystemMenu(hwnd, 02116 FALSE); 02117 if (hSysMenu != NULL) 02118 { 02119 /* Disable all items that are not relevant */ 02120 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++) 02121 { 02122 EnableMenuItem(hSysMenu, 02123 uidDisableItem[i], 02124 MF_BYCOMMAND | MF_GRAYED); 02125 } 02126 02127 EnableMenuItem(hSysMenu, 02128 SC_CLOSE, 02129 MF_BYCOMMAND | 02130 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED)); 02131 02132 /* Display the system menu */ 02133 uId = ITrayWindowImpl_TrackMenu(This, 02134 hSysMenu, 02135 NULL, 02136 This->hwndStart, 02137 This->Position != ABE_TOP, 02138 FALSE); 02139 if (uId != 0) 02140 { 02141 SendMessage(This->hWnd, 02142 WM_SYSCOMMAND, 02143 (WPARAM)uId, 02144 0); 02145 } 02146 } 02147 02148 /* revert the system menu window style */ 02149 SetWindowStyle(hwnd, 02150 WS_SYSMENU, 02151 0); 02152 break; 02153 } 02154 02155 default: 02156 goto DefHandler; 02157 } 02158 break; 02159 02160 case WM_NCRBUTTONUP: 02161 /* We want the user to be able to get a context menu even on the nonclient 02162 area (including the sizing border)! */ 02163 uMsg = WM_CONTEXTMENU; 02164 wParam = (WPARAM)hwnd; 02165 /* fall through */ 02166 02167 case WM_CONTEXTMENU: 02168 { 02169 POINT pt, *ppt = NULL; 02170 HWND hWndExclude = NULL; 02171 02172 /* Check if the administrator has forbidden access to context menus */ 02173 if (SHRestricted(REST_NOTRAYCONTEXTMENU)) 02174 break; 02175 02176 pt.x = (SHORT)LOWORD(lParam); 02177 pt.y = (SHORT)HIWORD(lParam); 02178 02179 if (pt.x != -1 || pt.y != -1) 02180 ppt = &pt; 02181 else 02182 hWndExclude = This->hwndStart; 02183 02184 if ((HWND)wParam == This->hwndStart) 02185 { 02186 /* Make sure we can't track the context menu if the start 02187 menu is currently being shown */ 02188 if (!(SendMessage(This->hwndStart, 02189 BM_GETSTATE, 02190 0, 02191 0) & BST_PUSHED)) 02192 { 02193 ITrayWindowImpl_TrackCtxMenu(This, 02194 &StartMenuBtnCtxMenu, 02195 ppt, 02196 hWndExclude, 02197 This->Position == ABE_BOTTOM, 02198 This); 02199 } 02200 } 02201 else 02202 { 02203 /* See if the context menu should be handled by the task band site */ 02204 if (ppt != NULL && This->TrayBandSite != NULL) 02205 { 02206 HWND hWndAtPt; 02207 POINT ptClient = *ppt; 02208 02209 /* Convert the coordinates to client-coordinates */ 02210 MapWindowPoints(NULL, 02211 This->hWnd, 02212 &ptClient, 02213 1); 02214 02215 hWndAtPt = ChildWindowFromPoint(This->hWnd, 02216 ptClient); 02217 if (hWndAtPt != NULL && 02218 (hWndAtPt == This->hwndRebar || IsChild(This->hwndRebar, 02219 hWndAtPt))) 02220 { 02221 /* Check if the user clicked on the task switch window */ 02222 ptClient = *ppt; 02223 MapWindowPoints(NULL, 02224 This->hwndRebar, 02225 &ptClient, 02226 1); 02227 02228 hWndAtPt = ChildWindowFromPointEx(This->hwndRebar, 02229 ptClient, 02230 CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); 02231 if (hWndAtPt == This->hwndTaskSwitch) 02232 goto HandleTrayContextMenu; 02233 02234 /* Forward the message to the task band site */ 02235 ITrayBandSite_ProcessMessage(This->TrayBandSite, 02236 hwnd, 02237 uMsg, 02238 wParam, 02239 lParam, 02240 &Ret); 02241 } 02242 else 02243 goto HandleTrayContextMenu; 02244 } 02245 else 02246 { 02247 HandleTrayContextMenu: 02248 /* Tray the default tray window context menu */ 02249 ITrayWindowImpl_TrackCtxMenu(This, 02250 &TrayWindowCtxMenu, 02251 ppt, 02252 NULL, 02253 FALSE, 02254 This); 02255 } 02256 } 02257 break; 02258 } 02259 02260 case WM_NOTIFY: 02261 { 02262 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside 02263 the rebar control! But we shouldn't forward messages that the band 02264 site doesn't handle, such as other controls (start button, tray window */ 02265 if (This->TrayBandSite == NULL || 02266 !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite, 02267 hwnd, 02268 uMsg, 02269 wParam, 02270 lParam, 02271 &Ret))) 02272 { 02273 const NMHDR *nmh = (const NMHDR *)lParam; 02274 02275 if (nmh->hwndFrom == This->hwndTrayNotify) 02276 { 02277 switch (nmh->code) 02278 { 02279 case NTNWM_REALIGN: 02280 /* Cause all controls to be aligned */ 02281 PostMessage(This->hWnd, 02282 WM_SIZE, 02283 SIZE_RESTORED, 02284 0); 02285 break; 02286 } 02287 } 02288 } 02289 break; 02290 } 02291 02292 case WM_NCLBUTTONDBLCLK: 02293 /* We "handle" this message so users can't cause a weird maximize/restore 02294 window animation when double-clicking the tray window! */ 02295 break; 02296 02297 case WM_NCCREATE: 02298 { 02299 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; 02300 This = (ITrayWindowImpl*)CreateStruct->lpCreateParams; 02301 02302 if (InterlockedCompareExchangePointer((PVOID*)&This->hWnd, 02303 (PVOID)hwnd, 02304 NULL) != NULL) 02305 { 02306 /* Somebody else was faster... */ 02307 return FALSE; 02308 } 02309 02310 SetWindowLongPtr(hwnd, 02311 0, 02312 (LONG_PTR)This); 02313 02314 return ITrayWindowImpl_NCCreate(This); 02315 } 02316 02317 case WM_CREATE: 02318 ITrayWindowImpl_Create(This); 02319 break; 02320 02321 case WM_NCDESTROY: 02322 ITrayWindowImpl_Destroy(This); 02323 break; 02324 02325 case WM_APP_TRAYDESTROY: 02326 DestroyWindow(hwnd); 02327 break; 02328 02329 case WM_COMMAND: 02330 if ((HWND)lParam == This->hwndStart) 02331 { 02332 if (This->StartMenuPopup != NULL) 02333 { 02334 POINTL pt; 02335 RECTL rcExclude; 02336 DWORD dwFlags = 0; 02337 02338 if (GetWindowRect(This->hwndStart, 02339 (RECT*)&rcExclude)) 02340 { 02341 if (ITrayWindowImpl_IsPosHorizontal(This)) 02342 { 02343 pt.x = rcExclude.left; 02344 pt.y = rcExclude.top; 02345 dwFlags |= MPPF_BOTTOM; 02346 } 02347 else 02348 { 02349 if (This->Position == ABE_LEFT) 02350 pt.x = rcExclude.left; 02351 else 02352 pt.x = rcExclude.right; 02353 02354 pt.y = rcExclude.bottom; 02355 dwFlags |= MPPF_BOTTOM; 02356 } 02357 02358 IMenuPopup_Popup(This->StartMenuPopup, 02359 &pt, 02360 &rcExclude, 02361 dwFlags); 02362 } 02363 } 02364 break; 02365 } 02366 02367 if (This->TrayBandSite == NULL || 02368 !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite, 02369 hwnd, 02370 uMsg, 02371 wParam, 02372 lParam, 02373 &Ret))) 02374 { 02375 switch(LOWORD(wParam)) 02376 { 02377 /* FIXME: Handle these commands as well */ 02378 case IDM_TASKBARANDSTARTMENU: 02379 case IDM_SEARCH: 02380 case IDM_HELPANDSUPPORT: 02381 break; 02382 02383 case IDM_RUN: 02384 { 02385 HANDLE hShell32; 02386 RUNFILEDLG RunFileDlg; 02387 02388 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL")); 02389 RunFileDlg = (RUNFILEDLG)GetProcAddress(hShell32, (LPCSTR)61); 02390 02391 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY); 02392 break; 02393 } 02394 02395 /* FIXME: Handle these commands as well */ 02396 case IDM_SYNCHRONIZE: 02397 case IDM_LOGOFF: 02398 case IDM_DISCONNECT: 02399 case IDM_UNDOCKCOMPUTER: 02400 break; 02401 02402 case IDM_SHUTDOWN: 02403 { 02404 HANDLE hShell32; 02405 EXITWINDLG ExitWinDlg; 02406 02407 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL")); 02408 ExitWinDlg = (EXITWINDLG)GetProcAddress(hShell32, (LPCSTR)60); 02409 02410 ExitWinDlg(hwnd); 02411 break; 02412 } 02413 } 02414 } 02415 break; 02416 02417 default: 02418 goto DefHandler; 02419 } 02420 } 02421 else 02422 { 02423 DefHandler: 02424 Ret = DefWindowProc(hwnd, 02425 uMsg, 02426 wParam, 02427 lParam); 02428 } 02429 02430 return Ret; 02431 } 02432 02433 /* 02434 * Tray Window Context Menu 02435 */ 02436 02437 static HMENU 02438 CreateTrayWindowContextMenu(IN HWND hWndOwner, 02439 IN PVOID *ppcmContext, 02440 IN PVOID Context OPTIONAL) 02441 { 02442 ITrayWindowImpl *This = (ITrayWindowImpl *)Context; 02443 IContextMenu *pcm = NULL; 02444 HMENU hPopup; 02445 02446 hPopup = LoadPopupMenu(hExplorerInstance, 02447 MAKEINTRESOURCE(IDM_TRAYWND)); 02448 02449 if (hPopup != NULL) 02450 { 02451 if (SHRestricted(REST_CLASSICSHELL) != 0) 02452 { 02453 DeleteMenu(hPopup, 02454 ID_LOCKTASKBAR, 02455 MF_BYCOMMAND); 02456 } 02457 02458 CheckMenuItem(hPopup, 02459 ID_LOCKTASKBAR, 02460 MF_BYCOMMAND | (This->Locked ? MF_CHECKED : MF_UNCHECKED)); 02461 02462 if (This->TrayBandSite != NULL) 02463 { 02464 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This->TrayBandSite, 02465 hPopup, 02466 0, 02467 ID_SHELL_CMD_FIRST, 02468 ID_SHELL_CMD_LAST, 02469 CMF_NORMAL, 02470 &pcm))) 02471 { 02472 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n"); 02473 *(IContextMenu **)ppcmContext = pcm; 02474 } 02475 } 02476 } 02477 02478 return hPopup; 02479 } 02480 02481 static VOID 02482 OnTrayWindowContextMenuCommand(IN HWND hWndOwner, 02483 IN UINT uiCmdId, 02484 IN PVOID pcmContext OPTIONAL, 02485 IN PVOID Context OPTIONAL) 02486 { 02487 ITrayWindowImpl *This = (ITrayWindowImpl *)Context; 02488 IContextMenu *pcm = (IContextMenu *)pcmContext; 02489 02490 if (uiCmdId != 0) 02491 { 02492 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST) 02493 { 02494 CMINVOKECOMMANDINFO cmici = {0}; 02495 02496 if (pcm != NULL) 02497 { 02498 /* Setup and invoke the shell command */ 02499 cmici.cbSize = sizeof(cmici); 02500 cmici.hwnd = hWndOwner; 02501 cmici.lpVerb = (LPCSTR)MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST); 02502 cmici.nShow = SW_NORMAL; 02503 02504 IContextMenu_InvokeCommand(pcm, 02505 &cmici); 02506 } 02507 } 02508 else 02509 { 02510 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This), 02511 uiCmdId); 02512 } 02513 } 02514 02515 if (pcm != NULL) 02516 IContextMenu_Release(pcm); 02517 } 02518 02519 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu = { 02520 CreateTrayWindowContextMenu, 02521 OnTrayWindowContextMenuCommand 02522 }; 02523 02524 /*****************************************************************************/ 02525 02526 BOOL 02527 RegisterTrayWindowClass(VOID) 02528 { 02529 WNDCLASS wcTrayWnd; 02530 BOOL Ret; 02531 02532 if (!RegisterTrayNotifyWndClass()) 02533 return FALSE; 02534 02535 wcTrayWnd.style = CS_DBLCLKS; 02536 wcTrayWnd.lpfnWndProc = TrayWndProc; 02537 wcTrayWnd.cbClsExtra = 0; 02538 wcTrayWnd.cbWndExtra = sizeof(ITrayWindowImpl *); 02539 wcTrayWnd.hInstance = hExplorerInstance; 02540 wcTrayWnd.hIcon = NULL; 02541 wcTrayWnd.hCursor = LoadCursor(NULL, 02542 IDC_ARROW); 02543 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 02544 wcTrayWnd.lpszMenuName = NULL; 02545 wcTrayWnd.lpszClassName = szTrayWndClass; 02546 02547 Ret = RegisterClass(&wcTrayWnd) != 0; 02548 02549 if (!Ret) 02550 UnregisterTrayNotifyWndClass(); 02551 02552 return Ret; 02553 } 02554 02555 VOID 02556 UnregisterTrayWindowClass(VOID) 02557 { 02558 UnregisterTrayNotifyWndClass(); 02559 02560 UnregisterClass(szTrayWndClass, 02561 hExplorerInstance); 02562 } 02563 02564 ITrayWindow * 02565 CreateTrayWindow(VOID) 02566 { 02567 ITrayWindowImpl *This; 02568 ITrayWindow *TrayWindow; 02569 02570 This = ITrayWindowImpl_Construct(); 02571 if (This != NULL) 02572 { 02573 TrayWindow = ITrayWindow_from_impl(This); 02574 02575 ITrayWindowImpl_Open(TrayWindow); 02576 02577 return TrayWindow; 02578 } 02579 02580 return NULL; 02581 } 02582 02583 VOID 02584 TrayProcessMessages(IN OUT ITrayWindow *Tray) 02585 { 02586 ITrayWindowImpl *This; 02587 MSG Msg; 02588 02589 This = impl_from_ITrayWindow(Tray); 02590 02591 /* FIXME: We should keep a reference here... */ 02592 02593 while (PeekMessage(&Msg, 02594 NULL, 02595 0, 02596 0, 02597 PM_REMOVE)) 02598 { 02599 if (Msg.message == WM_QUIT) 02600 break; 02601 02602 if (This->StartMenuBand == NULL || 02603 IMenuBand_IsMenuMessage(This->StartMenuBand, 02604 &Msg) != S_OK) 02605 { 02606 TranslateMessage(&Msg); 02607 DispatchMessage(&Msg); 02608 } 02609 } 02610 } 02611 02612 VOID 02613 TrayMessageLoop(IN OUT ITrayWindow *Tray) 02614 { 02615 ITrayWindowImpl *This; 02616 MSG Msg; 02617 BOOL Ret; 02618 02619 This = impl_from_ITrayWindow(Tray); 02620 02621 /* FIXME: We should keep a reference here... */ 02622 02623 while (1) 02624 { 02625 Ret = (GetMessage(&Msg, 02626 NULL, 02627 0, 02628 0) != 0); 02629 02630 if (Ret != -1) 02631 { 02632 if (!Ret) 02633 break; 02634 02635 if (This->StartMenuBand == NULL || 02636 IMenuBand_IsMenuMessage(This->StartMenuBand, 02637 &Msg) != S_OK) 02638 { 02639 TranslateMessage(&Msg); 02640 DispatchMessage(&Msg); 02641 } 02642 } 02643 } 02644 } 02645 02646 /* 02647 * IShellDesktopTray 02648 * 02649 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()! 02650 * These are the calls I observed, it may be wrong/incomplete/buggy!!! 02651 * The reason we implement it is because we have to use SHCreateDesktop() so 02652 * that the shell provides the desktop window and all the features that come 02653 * with it (especially positioning of desktop icons) 02654 */ 02655 02656 static HRESULT STDMETHODCALLTYPE 02657 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray *iface, 02658 IN REFIID riid, 02659 OUT LPVOID *ppvObj) 02660 { 02661 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); 02662 ITrayWindow *tray = ITrayWindow_from_impl(This); 02663 02664 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid, ppvObj); 02665 return ITrayWindowImpl_QueryInterface(tray, 02666 riid, 02667 ppvObj); 02668 } 02669 02670 static ULONG STDMETHODCALLTYPE 02671 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray *iface) 02672 { 02673 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); 02674 ITrayWindow *tray = ITrayWindow_from_impl(This); 02675 02676 DbgPrint("IShellDesktopTray::Release()\n"); 02677 return ITrayWindowImpl_Release(tray); 02678 } 02679 02680 static ULONG STDMETHODCALLTYPE 02681 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray *iface) 02682 { 02683 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); 02684 ITrayWindow *tray = ITrayWindow_from_impl(This); 02685 02686 DbgPrint("IShellDesktopTray::AddRef()\n"); 02687 return ITrayWindowImpl_AddRef(tray); 02688 } 02689 02690 static ULONG STDMETHODCALLTYPE 02691 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray *iface) 02692 { 02693 /* FIXME: Return ABS_ flags? */ 02694 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n"); 02695 return 0; 02696 } 02697 02698 static HRESULT STDMETHODCALLTYPE 02699 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray *iface, 02700 OUT HWND *phWndTray) 02701 { 02702 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); 02703 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray); 02704 *phWndTray = This->hWnd; 02705 return S_OK; 02706 } 02707 02708 static HRESULT STDMETHODCALLTYPE 02709 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray *iface, 02710 IN HWND hWndDesktop) 02711 { 02712 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); 02713 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop); 02714 02715 This->hWndDesktop = hWndDesktop; 02716 return S_OK; 02717 } 02718 02719 static HRESULT STDMETHODCALLTYPE 02720 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray *iface, 02721 IN DWORD dwUnknown1, 02722 IN DWORD dwUnknown2) 02723 { 02724 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2); 02725 return S_OK; 02726 } 02727 02728 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl = 02729 { 02730 /*** IUnknown ***/ 02731 ITrayWindowImpl_IShellDesktopTray_QueryInterface, 02732 ITrayWindowImpl_IShellDesktopTray_AddRef, 02733 ITrayWindowImpl_IShellDesktopTray_Release, 02734 /*** IShellDesktopTray ***/ 02735 ITrayWindowImpl_IShellDesktopTray_GetState, 02736 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow, 02737 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow, 02738 ITrayWindowImpl_IShellDesktopTray_Unknown 02739 }; Generated on Sat May 26 2012 04:17:25 for ReactOS by
1.7.6.1
|