update for HEAD-2003091401
[reactos.git] / subsys / system / explorer / taskbar / traynotify.cpp
1 /*
2  * Copyright 2003 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20  //
21  // Explorer clone
22  //
23  // traynotify.cpp
24  //
25  // Martin Fuchs, 22.08.2003
26  //
27
28
29 #include "../utility/utility.h"
30
31 #include "../explorer.h"
32 #include "../globals.h"
33
34 #include "traynotify.h"
35
36
37 NotifyIconIndex::NotifyIconIndex(NOTIFYICONDATA* pnid)
38 {
39         _hWnd = pnid->hWnd;
40         _uID = pnid->uID;
41
42          // special handling for windows task manager
43         if ((int)_uID < 0)
44                 _uID = 0;
45 }
46
47 NotifyIconIndex::NotifyIconIndex()
48 {
49         _hWnd = 0;
50         _uID = 0;
51 }
52
53
54 NotifyInfo::NotifyInfo()
55 {
56         _idx = -1;
57         _hIcon = 0;
58         _dwState = 0;
59         _uCallbackMessage = 0;
60 }
61
62 NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid)
63 {
64         _hWnd = pnid->hWnd;
65         _uID = pnid->uID;
66
67         if (pnid->uFlags & NIF_MESSAGE)
68                 _uCallbackMessage = pnid->uCallbackMessage;
69
70         if (pnid->uFlags & NIF_ICON)
71                 _hIcon = pnid->hIcon;
72
73 #ifdef NIF_STATE        // currently (as of 21.08.2003) missing in MinGW headers
74         if (pnid->uFlags & NIF_STATE)
75                 _dwState = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask);
76 #endif
77
78         //TODO: store and display tool tip texts
79
80         return *this;
81 }
82
83
84 NotifyArea::NotifyArea(HWND hwnd)
85  :      super(hwnd)
86 {
87         _next_idx = 0;
88 }
89
90 LRESULT NotifyArea::Init(LPCREATESTRUCT pcs)
91 {
92         if (super::Init(pcs))
93                 return 1;
94
95          // create clock window
96         _hwndClock = ClockWindow::Create(_hwnd);
97
98         SetTimer(_hwnd, 0, 1000, NULL);
99
100         return 0;
101 }
102
103 NotifyArea::~NotifyArea()
104 {
105         KillTimer(_hwnd, 0);
106 }
107
108 HWND NotifyArea::Create(HWND hwndParent)
109 {
110         ClientRect clnt(hwndParent);
111
112         return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
113                                                         BtnWindowClass(CLASSNAME_TRAYNOTIFY,CS_DBLCLKS), TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
114                                                         clnt.right-(NOTIFYAREA_WIDTH+1), 1, NOTIFYAREA_WIDTH, clnt.bottom-2, hwndParent);
115 }
116
117 LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
118 {
119         switch(nmsg) {
120           case WM_PAINT:
121                 Paint();
122                 break;
123
124           case WM_TIMER: {
125                 TimerTick();
126
127                 ClockWindow* clock_window = GET_WINDOW(ClockWindow, _hwndClock);
128
129                 if (clock_window)
130                         clock_window->TimerTick();
131                 break;}
132
133           default:
134                 if (nmsg>=WM_MOUSEFIRST && nmsg<=WM_MOUSELAST) {
135                          // close startup menu and other popup menus
136                          // This functionality is missing in MS Windows.
137                         if (nmsg==WM_LBUTTONDOWN || nmsg==WM_MBUTTONDOWN || nmsg==WM_RBUTTONDOWN
138 #ifdef WM_XBUTTONDOWN
139                                 || nmsg==WM_XBUTTONDOWN
140 #endif
141                                 )
142                                 CancelModes();
143
144                         NotifyIconSet::iterator found = IconHitTest(Point(lparam));
145
146                         if (found != _sorted_icons.end()) {
147                                 NotifyInfo& entry = const_cast<NotifyInfo&>(*found);    // Why does GCC 3.3 need this additional const_cast ?!
148
149         //TODO: AttachThreadInput() für SetForegroundWindow() in Client-Prozess
150
151                                  // Notify the message if the owner if it's still alive
152                                 if (IsWindow(entry._hWnd))
153                                         PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
154                                 else if (_icon_map.erase(entry))        // delete icons without valid owner window
155                                         Refresh();
156                         }
157                 }
158
159                 return super::WndProc(nmsg, wparam, lparam);
160         }
161
162         return 0;
163 }
164
165 void NotifyArea::CancelModes()
166 {
167         PostMessage(HWND_BROADCAST, WM_CANCELMODE, 0, 0);
168
169         for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it)
170                 PostMessage(it->_hWnd, WM_CANCELMODE, 0, 0);
171 }
172
173 LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pnid)
174 {
175         switch(notify_code) {
176           case NIM_ADD:
177           case NIM_MODIFY:
178                 if ((int)pnid->uID >= 0) {      //TODO: fix for windows task manager
179                         NotifyInfo& entry = _icon_map[pnid] = pnid;
180
181                          // a new entry?
182                         if (entry._idx == -1)
183                                 entry._idx = ++_next_idx;
184
185                         Refresh();      //TODO: call only if really changes occurred
186                 }
187                 break;
188
189           case NIM_DELETE:
190                 if (_icon_map.erase(pnid))
191                         Refresh();
192                 break;
193
194 #if NOTIFYICON_VERSION>=3       // currently (as of 21.08.2003) missing in MinGW headers
195           case NIM_SETFOCUS:
196                 break;
197
198           case NIM_SETVERSION:
199                 break;
200 #endif
201         }
202
203         return 0;
204 }
205
206 void NotifyArea::Refresh()
207 {
208         _sorted_icons.clear();
209
210          // sort icon infos by display index
211         for(NotifyIconMap::const_iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
212                 const NotifyInfo& entry = it->second;
213
214 #ifdef NIF_STATE        // currently (as of 21.08.2003) missing in MinGW headers
215                 if (!(entry._dwState & NIS_HIDDEN))
216 #endif
217                         _sorted_icons.insert(entry);
218         }
219
220         InvalidateRect(_hwnd, NULL, FALSE);     // refresh icon display
221         UpdateWindow(_hwnd);
222 }
223
224 void NotifyArea::Paint()
225 {
226         BufferedPaintCanvas canvas(_hwnd);
227
228          // first fill with the background color
229         FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
230
231          // draw icons
232         int x = 2;
233         int y = 2;
234
235         for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
236                 DrawIconEx(canvas, x, y, it->_hIcon, 16, 16, 0, 0, DI_NORMAL);
237                 x += 20;
238         }
239 }
240
241 void NotifyArea::TimerTick()
242 {
243         bool do_refresh = false;
244
245          // Look for task icons without valid owner window.
246          // This is an advanced feature, which is missing in MS Windows.
247         for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
248                 const NotifyInfo& entry = *it;
249
250                 if (!IsWindow(entry._hWnd))
251                         if (_icon_map.erase(entry))     // delete icons without valid owner window
252                                 ++do_refresh;
253         }
254
255         if (do_refresh)
256                 Refresh();
257 }
258
259  /// search for a icon at a given client coordinate position
260 NotifyIconSet::iterator NotifyArea::IconHitTest(const POINT& pos)
261 {
262         if (pos.y<2 || pos.y>=2+16)
263                 return _sorted_icons.end();
264
265         NotifyIconSet::iterator it = _sorted_icons.begin();
266
267         int x = 2;
268
269         for(; it!=_sorted_icons.end(); ++it) {
270                 NotifyInfo& entry = const_cast<NotifyInfo&>(*it);       // Why does GCC 3.3 need this additional const_cast ?!
271
272                 if (pos.x>=x && pos.x<x+16)
273                         break;
274
275                 x += 20;
276         }
277
278         return it;
279 }
280
281
282 ClockWindow::ClockWindow(HWND hwnd)
283  :      super(hwnd),
284         _tooltip(hwnd)
285 {
286         *_time = _T('\0');
287         FormatTime();
288
289         _tooltip.add(_hwnd, _hwnd);
290 }
291
292 HWND ClockWindow::Create(HWND hwndParent)
293 {
294         ClientRect clnt(hwndParent);
295
296         return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
297                                                         BtnWindowClass(CLASSNAME_CLOCKWINDOW,CS_DBLCLKS), NULL, WS_CHILD|WS_VISIBLE,
298                                                         clnt.right-(CLOCKWINDOW_WIDTH+1), 1, CLOCKWINDOW_WIDTH, clnt.bottom-2, hwndParent);
299 }
300
301 LRESULT ClockWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
302 {
303         switch(nmsg) {
304           case WM_PAINT:
305                 Paint();
306                 break;
307
308           case WM_LBUTTONDBLCLK:
309                 //launch_file(_hwnd, _T("timedate.cpl"), SW_SHOWNORMAL);        // This would be enough, but we want the fastest solution.
310                 //launch_file(_hwnd, _T("rundll32.exe /d shell32.dll,Control_RunDLL timedate.cpl"), SW_SHOWNORMAL);
311                 RunDLL(_hwnd, _T("shell32"), "Control_RunDLL", _T("timedate.cpl"), SW_SHOWNORMAL);
312                 break;
313
314           default:
315                 return super::WndProc(nmsg, wparam, lparam);
316         }
317
318         return 0;
319 }
320
321 int ClockWindow::Notify(int id, NMHDR* pnmh)
322 {
323         if (pnmh->code == TTN_GETDISPINFO) {
324                 LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
325
326                 SYSTEMTIME systime;
327                 TCHAR buffer[64];
328
329                 GetLocalTime(&systime);
330                 GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systime, NULL, buffer, 64);
331
332                 _tcscpy(pdi->szText, buffer);
333         }
334
335         return 0;
336 }
337
338 void ClockWindow::TimerTick()
339 {
340         if (FormatTime())
341                 InvalidateRect(_hwnd, NULL, TRUE);      // refresh displayed time
342 }
343
344 bool ClockWindow::FormatTime()
345 {
346         SYSTEMTIME systime;
347         TCHAR buffer[16];
348
349         GetLocalTime(&systime);
350
351         //_stprintf(buffer, TEXT("%02d:%02d:%02d"), systime.wHour, systime.wMinute, systime.wSecond);
352         _stprintf(buffer, TEXT("%02d:%02d"), systime.wHour, systime.wMinute);
353
354         if (_tcscmp(buffer, _time)) {
355                 _tcscpy(_time, buffer);
356                 return true;    // The text to display has changed.
357         }
358
359         return false;   // no change
360 }
361
362 void ClockWindow::Paint()
363 {
364         PaintCanvas canvas(_hwnd);
365
366         BkMode bkmode(canvas, TRANSPARENT);
367         FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
368
369         DrawText(canvas, _time, -1, ClientRect(_hwnd), DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX);
370 }