[EXPLORER] Pixel-perfect x-margins surrounding the clock
[reactos.git] / base / shell / explorer / trayclock.cpp
index 125fc73..9575783 100644 (file)
  * TrayClockWnd
  */
 
+const struct
+{
+    BOOL IsTime;
+    DWORD dwFormatFlags;
+    LPCWSTR lpFormat;
+} ClockWndFormats[] = {
+    { TRUE, 0, NULL },
+    { FALSE, 0, L"dddd" },
+    { FALSE, DATE_SHORTDATE, NULL }
+};
+const UINT ClockWndFormatsCount = _ARRAYSIZE(ClockWndFormats);
+
+#define CLOCKWND_FORMAT_COUNT ClockWndFormatsCount
+
+extern const WCHAR szTrayClockWndClass[];
+
+class CTrayClockWnd :
+    public CComCoClass<CTrayClockWnd>,
+    public CComObjectRootEx<CComMultiThreadModelNoCS>,
+    public CWindowImpl < CTrayClockWnd, CWindow, CControlWinTraits >,
+    public IOleWindow
+{
+    HFONT hFont;
+    COLORREF textColor;
+    RECT rcText;
+    SYSTEMTIME LocalTime;
+
+    union
+    {
+        DWORD dwFlags;
+        struct
+        {
+            DWORD IsTimerEnabled : 1;
+            DWORD IsInitTimerEnabled : 1;
+            DWORD LinesMeasured : 1;
+            DWORD IsHorizontal : 1;
+        };
+    };
+    DWORD LineSpacing;
+    SIZE CurrentSize;
+    WORD VisibleLines;
+    SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
+    WCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
+
+public:
+    CTrayClockWnd();
+    virtual ~CTrayClockWnd();
+
+private:
+    LRESULT OnThemeChanged();
+    LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+
+    BOOL MeasureLines();
+    WORD GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize);
+    VOID UpdateWnd();
+    VOID Update();
+    UINT CalculateDueTime();
+    BOOL ResetTime();
+    VOID CalibrateTimer();
+    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    VOID SetFont(IN HFONT hNewFont, IN BOOL bRedraw);
+    LRESULT DrawBackground(HDC hdc);
+    LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+    LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+
+public:
+
+    HRESULT WINAPI GetWindow(HWND* phwnd)
+    {
+        if (!phwnd)
+            return E_INVALIDARG;
+        *phwnd = m_hWnd;
+        return S_OK;
+    }
+
+    HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
+    {
+        return E_NOTIMPL;
+    }
+
+    DECLARE_NOT_AGGREGATABLE(CTrayClockWnd)
+
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+    BEGIN_COM_MAP(CTrayClockWnd)
+        COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
+    END_COM_MAP()
+
+    DECLARE_WND_CLASS_EX(szTrayClockWndClass, CS_DBLCLKS, COLOR_3DFACE)
+
+    BEGIN_MSG_MAP(CTrayClockWnd)
+        MESSAGE_HANDLER(WM_CREATE, OnCreate)
+        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+        MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+        MESSAGE_HANDLER(WM_SIZE, OnSize)
+        MESSAGE_HANDLER(WM_PAINT, OnPaint)
+        MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
+        MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
+        MESSAGE_HANDLER(WM_TIMER, OnTimer)
+        MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
+        MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
+        MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
+        MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
+        MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
+    END_MSG_MAP()
+
+    HRESULT Initialize(IN HWND hWndParent);
+};
+
 const WCHAR szTrayClockWndClass[] = L"TrayClockWClass";
 
 #define ID_TRAYCLOCK_TIMER  0
 #define ID_TRAYCLOCK_TIMER_INIT 1
 
-#define TRAY_CLOCK_WND_SPACING_X    0
+#define TRAY_CLOCK_WND_SPACING_X    5
 #define TRAY_CLOCK_WND_SPACING_Y    0
 
 CTrayClockWnd::CTrayClockWnd() :
-        hWndNotify(NULL),
         hFont(NULL),
         dwFlags(0),
         LineSpacing(0),
@@ -192,8 +307,8 @@ WORD CTrayClockWnd::GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize)
 
             /* Increase maximum rectangle */
             szMax.cy += LineSizes[i].cy;
-            if (LineSizes[i].cx > szMax.cx - (2 * TRAY_CLOCK_WND_SPACING_X))
-                szMax.cx = LineSizes[i].cx + (2 * TRAY_CLOCK_WND_SPACING_X);
+            if (LineSizes[i].cx > szMax.cx)
+                szMax.cx = LineSizes[i].cx;
         }
     }
 
@@ -265,20 +380,12 @@ VOID CTrayClockWnd::UpdateWnd()
     {
         InvalidateRect(NULL, TRUE);
 
-        if (hWndNotify != NULL &&
-            (szPrevCurrent.cx != CurrentSize.cx ||
-            szPrevCurrent.cy != CurrentSize.cy))
+        if (szPrevCurrent.cx != CurrentSize.cx ||
+            szPrevCurrent.cy != CurrentSize.cy)
         {
-            NMHDR nmh;
-
-            nmh.hwndFrom = m_hWnd;
-            nmh.idFrom = GetWindowLongPtr(GWLP_ID);
-            nmh.code = NTNWM_REALIGN;
-
-            ::SendMessage(hWndNotify,
-                WM_NOTIFY,
-                (WPARAM) nmh.idFrom,
-                (LPARAM) &nmh);
+            /* Ask the parent to resize */
+            NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
+            GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
         }
     }
 }
@@ -427,9 +534,7 @@ LRESULT CTrayClockWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
 
         hPrevFont = (HFONT) SelectObject(hDC, hFont);
 
-        rcClient.left = (rcClient.right / 2) - (CurrentSize.cx / 2);
         rcClient.top = (rcClient.bottom / 2) - (CurrentSize.cy / 2);
-        rcClient.right = rcClient.left + CurrentSize.cx;
         rcClient.bottom = rcClient.top + CurrentSize.cy;
 
         for (i = 0, line = 0;
@@ -439,8 +544,7 @@ LRESULT CTrayClockWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
             if (LineSizes[i].cx != 0)
             {
                 TextOut(hDC,
-                    rcClient.left + (CurrentSize.cx / 2) - (LineSizes[i].cx / 2) +
-                    TRAY_CLOCK_WND_SPACING_X,
+                    (rcClient.right - LineSizes[i].cx) / 2,
                     rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
                     szLines[i],
                     wcslen(szLines[i]));
@@ -518,11 +622,6 @@ LRESULT CTrayClockWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam,
     return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam) != 0;
 }
 
-LRESULT CTrayClockWnd::OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
-{
-    return (LRESULT) ResetTime();
-}
-
 LRESULT CTrayClockWnd::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
     return HTTRANSPARENT;
@@ -554,23 +653,76 @@ LRESULT CTrayClockWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHa
     return TRUE;
 }
 
-HWND CTrayClockWnd::_Init(IN HWND hWndParent, IN BOOL bVisible)
+LRESULT CTrayClockWnd::OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
-    IsHorizontal = TRUE;
+    BOOL bRealign = FALSE;
 
-    hWndNotify = hWndParent;
+    TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
+    if (newSettings->bShowSeconds != g_TaskbarSettings.bShowSeconds)
+    {
+        g_TaskbarSettings.bShowSeconds = newSettings->bShowSeconds;
+        bRealign = TRUE;
+    }
+
+    if (newSettings->sr.HideClock != g_TaskbarSettings.sr.HideClock)
+    {
+        g_TaskbarSettings.sr.HideClock = newSettings->sr.HideClock;
+        ShowWindow(g_TaskbarSettings.sr.HideClock ? SW_HIDE : SW_SHOW);
+        bRealign = TRUE;
+    }
+
+    if (bRealign)
+    {
+        /* Ask the parent to resize */
+        NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
+        GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
+        Update();
+    }
+    return 0;
+}
+
+LRESULT CTrayClockWnd::OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+{
+    if (IsWindowVisible())
+    {
+        /* We get all WM_NCLBUTTONDBLCLK for the taskbar so we need to check if it is on the clock*/
+        RECT rcClock;
+        if (GetWindowRect(&rcClock))
+        {
+            POINT ptClick;
+            ptClick.x = MAKEPOINTS(lParam).x;
+            ptClick.y = MAKEPOINTS(lParam).y;
+            if (PtInRect(&rcClock, ptClick))
+            {
+                //FIXME: use SHRunControlPanel
+                ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
+            }
+        }
+    }
+    return TRUE;
+}
+
+HRESULT CTrayClockWnd::Initialize(IN HWND hWndParent)
+{
+    IsHorizontal = TRUE;
 
     /* Create the window. The tray window is going to move it to the correct
         position and resize it as needed. */
     DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
-    if (bVisible)
+    if (!g_TaskbarSettings.sr.HideClock)
         dwStyle |= WS_VISIBLE;
 
     Create(hWndParent, 0, NULL, dwStyle);
+    if (!m_hWnd)
+        return E_FAIL;
 
-    if (m_hWnd != NULL)
-        SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
+    SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
 
-    return m_hWnd;
+    return S_OK;
 
 };
+
+HRESULT CTrayClockWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
+{
+    return ShellObjectCreatorInit<CTrayClockWnd>(hwndParent, riid, ppv);
+}