* created a Makefile for compiling as standalone project using MinGW
[reactos.git] / reactos / 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 }