This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / subsys / system / explorer / taskbar / traynotify.cpp
diff --git a/subsys/system/explorer/taskbar/traynotify.cpp b/subsys/system/explorer/taskbar/traynotify.cpp
new file mode 100644 (file)
index 0000000..a4d3b41
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2003 Martin Fuchs
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+ //
+ // Explorer clone
+ //
+ // traynotify.cpp
+ //
+ // Martin Fuchs, 22.08.2003
+ //
+
+
+#include "../utility/utility.h"
+
+#include "../explorer.h"
+#include "../globals.h"
+
+#include "traynotify.h"
+
+
+NotifyIconIndex::NotifyIconIndex(NOTIFYICONDATA* pnid)
+{
+       _hWnd = pnid->hWnd;
+       _uID = pnid->uID;
+
+        // special handling for windows task manager
+       if ((int)_uID < 0)
+               _uID = 0;
+}
+
+NotifyIconIndex::NotifyIconIndex()
+{
+       _hWnd = 0;
+       _uID = 0;
+}
+
+
+NotifyInfo::NotifyInfo()
+{
+       _idx = -1;
+       _hIcon = 0;
+       _dwState = 0;
+       _uCallbackMessage = 0;
+}
+
+NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid)
+{
+       _hWnd = pnid->hWnd;
+       _uID = pnid->uID;
+
+       if (pnid->uFlags & NIF_MESSAGE)
+               _uCallbackMessage = pnid->uCallbackMessage;
+
+       if (pnid->uFlags & NIF_ICON)
+               _hIcon = pnid->hIcon;
+
+#ifdef NIF_STATE       // currently (as of 21.08.2003) missing in MinGW headers
+       if (pnid->uFlags & NIF_STATE)
+               _dwState = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask);
+#endif
+
+       //TODO: store and display tool tip texts
+
+       return *this;
+}
+
+
+NotifyArea::NotifyArea(HWND hwnd)
+ :     super(hwnd)
+{
+       _next_idx = 0;
+}
+
+LRESULT NotifyArea::Init(LPCREATESTRUCT pcs)
+{
+       if (super::Init(pcs))
+               return 1;
+
+        // create clock window
+       _hwndClock = ClockWindow::Create(_hwnd);
+
+       SetTimer(_hwnd, 0, 1000, NULL);
+
+       return 0;
+}
+
+NotifyArea::~NotifyArea()
+{
+       KillTimer(_hwnd, 0);
+}
+
+HWND NotifyArea::Create(HWND hwndParent)
+{
+       ClientRect clnt(hwndParent);
+
+       return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
+                                                       BtnWindowClass(CLASSNAME_TRAYNOTIFY,CS_DBLCLKS), TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
+                                                       clnt.right-(NOTIFYAREA_WIDTH+1), 1, NOTIFYAREA_WIDTH, clnt.bottom-2, hwndParent);
+}
+
+LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
+{
+       switch(nmsg) {
+         case WM_PAINT:
+               Paint();
+               break;
+
+         case WM_TIMER: {
+               TimerTick();
+
+               ClockWindow* clock_window = GET_WINDOW(ClockWindow, _hwndClock);
+
+               if (clock_window)
+                       clock_window->TimerTick();
+               break;}
+
+         default:
+               if (nmsg>=WM_MOUSEFIRST && nmsg<=WM_MOUSELAST) {
+                        // close startup menu and other popup menus
+                        // This functionality is missing in MS Windows.
+                       if (nmsg==WM_LBUTTONDOWN || nmsg==WM_MBUTTONDOWN || nmsg==WM_RBUTTONDOWN
+#ifdef WM_XBUTTONDOWN
+                               || nmsg==WM_XBUTTONDOWN
+#endif
+                               )
+                               CancelModes();
+
+                       NotifyIconSet::iterator found = IconHitTest(Point(lparam));
+
+                       if (found != _sorted_icons.end()) {
+                               NotifyInfo& entry = const_cast<NotifyInfo&>(*found);    // Why does GCC 3.3 need this additional const_cast ?!
+
+       //TODO: AttachThreadInput() für SetForegroundWindow() in Client-Prozess
+
+                                // Notify the message if the owner if it's still alive
+                               if (IsWindow(entry._hWnd))
+                                       PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
+                               else if (_icon_map.erase(entry))        // delete icons without valid owner window
+                                       Refresh();
+                       }
+               }
+
+               return super::WndProc(nmsg, wparam, lparam);
+       }
+
+       return 0;
+}
+
+void NotifyArea::CancelModes()
+{
+       PostMessage(HWND_BROADCAST, WM_CANCELMODE, 0, 0);
+
+       for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it)
+               PostMessage(it->_hWnd, WM_CANCELMODE, 0, 0);
+}
+
+LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pnid)
+{
+       switch(notify_code) {
+         case NIM_ADD:
+         case NIM_MODIFY:
+               if ((int)pnid->uID >= 0) {      //TODO: fix for windows task manager
+                       NotifyInfo& entry = _icon_map[pnid] = pnid;
+
+                        // a new entry?
+                       if (entry._idx == -1)
+                               entry._idx = ++_next_idx;
+
+                       Refresh();      //TODO: call only if really changes occurred
+               }
+               break;
+
+         case NIM_DELETE:
+               if (_icon_map.erase(pnid))
+                       Refresh();
+               break;
+
+#if NOTIFYICON_VERSION>=3      // currently (as of 21.08.2003) missing in MinGW headers
+         case NIM_SETFOCUS:
+               break;
+
+         case NIM_SETVERSION:
+               break;
+#endif
+       }
+
+       return 0;
+}
+
+void NotifyArea::Refresh()
+{
+       _sorted_icons.clear();
+
+        // sort icon infos by display index
+       for(NotifyIconMap::const_iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
+               const NotifyInfo& entry = it->second;
+
+#ifdef NIF_STATE       // currently (as of 21.08.2003) missing in MinGW headers
+               if (!(entry._dwState & NIS_HIDDEN))
+#endif
+                       _sorted_icons.insert(entry);
+       }
+
+       InvalidateRect(_hwnd, NULL, FALSE);     // refresh icon display
+       UpdateWindow(_hwnd);
+}
+
+void NotifyArea::Paint()
+{
+       BufferedPaintCanvas canvas(_hwnd);
+
+        // first fill with the background color
+       FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
+
+        // draw icons
+       int x = 2;
+       int y = 2;
+
+       for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
+               DrawIconEx(canvas, x, y, it->_hIcon, 16, 16, 0, 0, DI_NORMAL);
+               x += 20;
+       }
+}
+
+void NotifyArea::TimerTick()
+{
+       bool do_refresh = false;
+
+        // Look for task icons without valid owner window.
+        // This is an advanced feature, which is missing in MS Windows.
+       for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
+               const NotifyInfo& entry = *it;
+
+               if (!IsWindow(entry._hWnd))
+                       if (_icon_map.erase(entry))     // delete icons without valid owner window
+                               ++do_refresh;
+       }
+
+       if (do_refresh)
+               Refresh();
+}
+
+ /// search for a icon at a given client coordinate position
+NotifyIconSet::iterator NotifyArea::IconHitTest(const POINT& pos)
+{
+       if (pos.y<2 || pos.y>=2+16)
+               return _sorted_icons.end();
+
+       NotifyIconSet::iterator it = _sorted_icons.begin();
+
+       int x = 2;
+
+       for(; it!=_sorted_icons.end(); ++it) {
+               NotifyInfo& entry = const_cast<NotifyInfo&>(*it);       // Why does GCC 3.3 need this additional const_cast ?!
+
+               if (pos.x>=x && pos.x<x+16)
+                       break;
+
+               x += 20;
+       }
+
+       return it;
+}
+
+
+ClockWindow::ClockWindow(HWND hwnd)
+ :     super(hwnd),
+       _tooltip(hwnd)
+{
+       *_time = _T('\0');
+       FormatTime();
+
+       _tooltip.add(_hwnd, _hwnd);
+}
+
+HWND ClockWindow::Create(HWND hwndParent)
+{
+       ClientRect clnt(hwndParent);
+
+       return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
+                                                       BtnWindowClass(CLASSNAME_CLOCKWINDOW,CS_DBLCLKS), NULL, WS_CHILD|WS_VISIBLE,
+                                                       clnt.right-(CLOCKWINDOW_WIDTH+1), 1, CLOCKWINDOW_WIDTH, clnt.bottom-2, hwndParent);
+}
+
+LRESULT ClockWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
+{
+       switch(nmsg) {
+         case WM_PAINT:
+               Paint();
+               break;
+
+         case WM_LBUTTONDBLCLK:
+               //launch_file(_hwnd, _T("timedate.cpl"), SW_SHOWNORMAL);        // This would be enough, but we want the fastest solution.
+               //launch_file(_hwnd, _T("rundll32.exe /d shell32.dll,Control_RunDLL timedate.cpl"), SW_SHOWNORMAL);
+               RunDLL(_hwnd, _T("shell32"), "Control_RunDLL", _T("timedate.cpl"), SW_SHOWNORMAL);
+               break;
+
+         default:
+               return super::WndProc(nmsg, wparam, lparam);
+       }
+
+       return 0;
+}
+
+int ClockWindow::Notify(int id, NMHDR* pnmh)
+{
+       if (pnmh->code == TTN_GETDISPINFO) {
+               LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
+
+               SYSTEMTIME systime;
+               TCHAR buffer[64];
+
+               GetLocalTime(&systime);
+               GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systime, NULL, buffer, 64);
+
+               _tcscpy(pdi->szText, buffer);
+       }
+
+       return 0;
+}
+
+void ClockWindow::TimerTick()
+{
+       if (FormatTime())
+               InvalidateRect(_hwnd, NULL, TRUE);      // refresh displayed time
+}
+
+bool ClockWindow::FormatTime()
+{
+       SYSTEMTIME systime;
+       TCHAR buffer[16];
+
+       GetLocalTime(&systime);
+
+       //_stprintf(buffer, TEXT("%02d:%02d:%02d"), systime.wHour, systime.wMinute, systime.wSecond);
+       _stprintf(buffer, TEXT("%02d:%02d"), systime.wHour, systime.wMinute);
+
+       if (_tcscmp(buffer, _time)) {
+               _tcscpy(_time, buffer);
+               return true;    // The text to display has changed.
+       }
+
+       return false;   // no change
+}
+
+void ClockWindow::Paint()
+{
+       PaintCanvas canvas(_hwnd);
+
+       BkMode bkmode(canvas, TRANSPARENT);
+       FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
+
+       DrawText(canvas, _time, -1, ClientRect(_hwnd), DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX);
+}