ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

traywnd.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.