1 /*
2  * Copyright 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // traynotify.cpp
24  //
25  // Martin Fuchs, 22.08.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 #include "traynotify.h"
32 
33 #include "../notifyhook/notifyhook.h"
34 
35 NotifyHook::NotifyHook()
36  :	WM_GETMODULEPATH(InstallNotifyHook())
37 {
38 }
39 
40 NotifyHook::~NotifyHook()
41 {
42 	DeinstallNotifyHook();
43 }
44 
45 void NotifyHook::GetModulePath(HWND hwnd, HWND hwndCallback)
46 {
47 	PostMessage(hwnd, WM_GETMODULEPATH, (WPARAM)hwndCallback, 0);
48 }
49 
50 bool NotifyHook::ModulePathCopyData(LPARAM lparam, HWND* phwnd, String& path)
51 {
52 	char buffer[MAX_PATH];
53 
54 	int l = GetWindowModulePathCopyData(lparam, phwnd, buffer, COUNTOF(buffer));
55 
56 	if (l) {
57 		path.assign(buffer, l);
58 		return true;
59 	} else
60 		return false;
61 }
62 
63 
64 NotifyIconIndex::NotifyIconIndex(NOTIFYICONDATA* pnid)
65 {
66 	_hWnd = pnid->hWnd;
67 	_uID = pnid->uID;
68 
69 	 // special handling for windows task manager
70 	if ((int)_uID < 0)
71 		_uID = 0;
72 }
73 
74 NotifyIconIndex::NotifyIconIndex()
75 {
76 	_hWnd = 0;
77 	_uID = 0;
78 }
79 
80 
81 NotifyInfo::NotifyInfo()
82 {
83 	_idx = -1;
84 	_hIcon = 0;
85 	_dwState = 0;
86 	_uCallbackMessage = 0;
87 	_version = 0;
88 
89 	_mode = NIM_AUTO;
90 	_lastChange = GetTickCount();
91 }
92 
93 
94  // WCHAR versions von NOTIFYICONDATA
95 #define	NID_SIZE_W6	 sizeof(NOTIFYICONDATAW)										// _WIN32_IE = 0x600
96 #define	NID_SIZE_W5	(sizeof(NOTIFYICONDATAW)-sizeof(GUID))							// _WIN32_IE = 0x500
97 #define	NID_SIZE_W3	(sizeof(NOTIFYICONDATAW)-sizeof(GUID)-(128-64)*sizeof(WCHAR))	// _WIN32_IE < 0x500
98 
99  // CHAR versions von NOTIFYICONDATA
100 #define	NID_SIZE_A6	 sizeof(NOTIFYICONDATAA)
101 #define	NID_SIZE_A5	(sizeof(NOTIFYICONDATAA)-sizeof(GUID))
102 #define	NID_SIZE_A3	(sizeof(NOTIFYICONDATAA)-sizeof(GUID)-(128-64)*sizeof(CHAR))
103 
104 bool NotifyInfo::modify(NOTIFYICONDATA* pnid)
105 {
106 	bool changes = false;
107 
108 	if (_hWnd!=pnid->hWnd || _uID!=pnid->uID) {
109 		_hWnd = pnid->hWnd;
110 		_uID = pnid->uID;
111 
112 		changes = true;
113 	}
114 
115 	if (pnid->uFlags & NIF_MESSAGE) {
116 		if (_uCallbackMessage != pnid->uCallbackMessage) {
117 			_uCallbackMessage = pnid->uCallbackMessage;
118 			changes = true;
119 		}
120 	}
121 
122 	if (pnid->uFlags & NIF_ICON) {
123 		 // Some applications destroy the icon immediatelly after completing the
124 		 // NIM_ADD/MODIFY message, so we have to make a copy of it.
125 		if (_hIcon)
126 			DestroyIcon(_hIcon);
127 
128 		_hIcon = (HICON) CopyImage(pnid->hIcon, IMAGE_ICON, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0);
129 
130 		changes = true;	///@todo compare icon
131 	}
132 
133 	if (pnid->uFlags & NIF_STATE) {
134 		DWORD new_state = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask);
135 
136 		if (_dwState != new_state) {
137 			_dwState = new_state;
138 			changes = true;
139 		}
140 	}
141 
142 	 // store tool tip text
143 	if (pnid->uFlags & NIF_TIP) {
144 		String new_text;
145 
146 		if (pnid->cbSize==NID_SIZE_W6 || pnid->cbSize==NID_SIZE_W5 || pnid->cbSize==NID_SIZE_W3) {
147 			 // UNICODE version of NOTIFYICONDATA structure
148 			LPCWSTR txt = (LPCWSTR)pnid->szTip;
149 			int max_len = pnid->cbSize==NID_SIZE_W3? 64: 128;
150 
151 			 // get tooltip string length
152 			int l = 0;
153 			for(; l<max_len; ++l)
154 				if (!txt[l])
155 					break;
156 
157 			new_text.assign(txt, l);
158 
159 			if (new_text != _tipText) {
160 				_tipText = new_text;
161 				changes = true;
162 			}
163 		} else if (pnid->cbSize==NID_SIZE_A6 || pnid->cbSize==NID_SIZE_A5 || pnid->cbSize==NID_SIZE_A3) {
164 			LPCSTR txt = (LPCSTR)pnid->szTip;
165 			int max_len = pnid->cbSize==NID_SIZE_A3? 64: 128;
166 
167 			int l = 0;
168 			for(int l=0; l<max_len; ++l)
169 				if (!txt[l])
170 					break;
171 
172 			new_text.assign(txt, l);
173 
174 			if (new_text != _tipText) {
175 				_tipText = new_text;
176 				changes = true;
177 			}
178 		}
179 	}
180 
181 	TCHAR title[MAX_PATH];
182 
183 	DWORD pid;
184 	GetWindowThreadProcessId(_hWnd, &pid);
185 
186 	 // avoid to send WM_GETTEXT messages to the own process
187 	if (pid != GetCurrentProcessId())
188 		if (GetWindowText(_hWnd, title, COUNTOF(title))) {
189 			if (_windowTitle != title) {
190 				_windowTitle = title;
191 				changes = true;
192 			}
193 		}
194 
195 	if (changes) {
196 		create_name();
197 		_lastChange = GetTickCount();
198 	}
199 
200 	return changes;
201 }
202 
203 
204 NotifyArea::NotifyArea(HWND hwnd)
205  :	super(hwnd),
206 	_tooltip(hwnd)
207 {
208 	_next_idx = 0;
209 	_clock_width = 0;
210 	_last_icon_count = 0;
211 	_show_hidden = false;
212 	_hide_inactive = true;
213 	_show_button = true;
214 }
215 
216 NotifyArea::~NotifyArea()
217 {
218 	KillTimer(_hwnd, 0);
219 
220 	write_config();
221 }
222 
223 static bool get_hide_clock_from_registry()
224 {
225 	HKEY hkeyStuckRects = 0;
226 	DWORD buffer[10];
227 	DWORD len = sizeof(buffer);
228 
229 	bool hide_clock = false;
230 
231 	 // check if the clock should be hidden
232 	if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"), &hkeyStuckRects) &&
233 		!RegQueryValueEx(hkeyStuckRects, TEXT("Settings"), 0, NULL, (LPBYTE)buffer, &len) &&
234 		len==sizeof(buffer) && buffer[0]==sizeof(buffer))
235 		hide_clock = buffer[2] & 0x08? true: false;
236 
237 	if (hkeyStuckRects)
238 		RegCloseKey(hkeyStuckRects);
239 
240 	return hide_clock;
241 }
242 
243 void NotifyArea::read_config()
244 {
245 	bool clock_visible = true;
246 
247 	 // read notification icon settings from XML configuration
248 	XMLPos cfg_pos = g_Globals.get_cfg();
249 
250 	if (!g_Globals._SHRestricted || !SHRestricted(REST_HIDECLOCK))
251 	{
252 		if (cfg_pos.go_down("desktopbar")) {
253 			clock_visible = XMLBoolRef(XMLPos(cfg_pos,"options"), "show-clock", !get_hide_clock_from_registry());
254 			cfg_pos.back();
255 		}
256 	}
257 
258 	if (cfg_pos.go_down("notify-icons")) {
259 		XMLPos options(cfg_pos, "options");
260 
261 		_hide_inactive = XMLBool(options, "hide-inactive", true);	///@todo read default setting from registry
262 		_show_hidden = XMLBool(options, "show-hidden", false);	///@todo read default setting from registry
263 		_show_button = XMLBool(options, "show-button", true);
264 
265 		XMLChildrenFilter icons(cfg_pos, "icon");
266 
267 		for(XMLChildrenFilter::iterator it=icons.begin(); it!=icons.end(); ++it) {
268 			const XMLNode& node = **it;
269 
270 			NotifyIconConfig cfg;
271 
272 			cfg._name = node.get("name").c_str();
273 			cfg._tipText = node.get("text").c_str();
274 			cfg._windowTitle = node.get("window").c_str();
275 			cfg._modulePath = node.get("module").c_str();
276 			const string& mode = node.get("show");
277 
278 			if (mode == "show")
279 				cfg._mode = NIM_SHOW;
280 			else if (mode == "hide")
281 				cfg._mode = NIM_HIDE;
282 			else //if (mode == "auto")
283 				cfg._mode = NIM_HIDE;
284 
285 			_cfg.push_back(cfg);
286 		}
287 
288 		cfg_pos.back();
289 	}
290 
291 	show_clock(clock_visible);
292 }
293 
294 void NotifyArea::write_config()
295 {
296 	 // write notification icon settings to XML configuration file
297 	XMLPos cfg_pos = g_Globals.get_cfg();
298 
299 	cfg_pos.smart_create("desktopbar");
300 	XMLBoolRef boolRef(XMLPos(cfg_pos,"options"), "show-clock");
301     boolRef = _hwndClock!=0;
302 	cfg_pos.back();
303 
304 	cfg_pos.smart_create("notify-icons");
305 
306 	XMLPos options(cfg_pos, "options");
307 	XMLBoolRef(options, "hide-inactive") = _hide_inactive;
308 	XMLBoolRef(options, "show-hidden") = _show_hidden;
309 	XMLBoolRef(options, "show-button") = _show_button;
310 
311 	for(NotifyIconCfgList::iterator it=_cfg.begin(); it!=_cfg.end(); ++it) {
312 		NotifyIconConfig& cfg = *it;
313 
314 		 // search for the corresponding node using the original name
315 		cfg_pos.smart_create("icon", "name", cfg._name);
316 
317 		 // refresh unique name
318 		cfg.create_name();
319 
320 		cfg_pos["name"] = cfg._name.c_str();
321 		cfg_pos["text"] = cfg._tipText.c_str();
322 		cfg_pos["window"] = cfg._windowTitle.c_str();
323 		cfg_pos["module"] = cfg._modulePath.c_str();
324 		cfg_pos["show"] = string_from_mode(cfg._mode).c_str();
325 
326 		cfg_pos.back();
327 	}
328 
329 	cfg_pos.back();	// smart_create
330 }
331 
332 void NotifyArea::show_clock(bool flag)
333 {
334 	bool vis = _hwndClock!=0;
335 
336 	if (vis != flag) {
337 		if (flag) {
338 			 // create clock window
339 			_hwndClock = ClockWindow::Create(_hwnd);
340 
341 			if (_hwndClock) {
342 				ClientRect clock_size(_hwndClock);
343 				_clock_width = clock_size.right;
344 			}
345 		} else {
346 			DestroyWindow(_hwndClock);
347 			_hwndClock = 0;
348 			_clock_width = 0;
349 		}
350 
351 		SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
352 	}
353 }
354 
355 LRESULT NotifyArea::Init(LPCREATESTRUCT pcs)
356 {
357 	if (super::Init(pcs))
358 		return 1;
359 
360 	read_config();
361 
362 	SetTimer(_hwnd, 0, 1000, NULL);
363 
364 	return 0;
365 }
366 
367 HWND NotifyArea::Create(HWND hwndParent)
368 {
369 	static BtnWindowClass wcTrayNotify(CLASSNAME_TRAYNOTIFY, CS_DBLCLKS);
370 
371 	ClientRect clnt(hwndParent);
372 
373 	return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
374 							wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN,
375 							clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
376 }
377 
378 LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
379 {
380 	switch(nmsg) {
381 	  case WM_PAINT:
382 		Paint();
383 		break;
384 
385 	  case WM_TIMER: {
386 		Refresh();
387 
388 		ClockWindow* clock_window = GET_WINDOW(ClockWindow, _hwndClock);
389 
390 		if (clock_window)
391 			clock_window->TimerTick();
392 		break;}
393 
394 	  case PM_REFRESH:
395 		Refresh(true);
396 		break;
397 
398 	  case WM_SIZE: {
399 		int cx = LOWORD(lparam);
400 		SetWindowPos(_hwndClock, 0, cx-_clock_width, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
401 		break;}
402 
403 	  case PM_GET_WIDTH: {
404 		int w = _sorted_icons.size()*NOTIFYICON_DIST + NOTIFYAREA_SPACE + _clock_width;
405 		if (_show_button)
406 			w += NOTIFYICON_DIST;
407 		return w;}
408 
409 	  case PM_REFRESH_CONFIG:
410 		read_config();
411 		break;
412 
413 	  case WM_CONTEXTMENU: {
414 		Point pt(lparam);
415 		POINTS p;
416 		p.x = (SHORT) pt.x;
417 		p.y = (SHORT) pt.y;
418 		ScreenToClient(_hwnd, &pt);
419 
420 		if (IconHitTest(pt) == _sorted_icons.end()) { // display menu only when no icon clicked
421 			PopupMenu menu(IDM_NOTIFYAREA);
422 			SetMenuDefaultItem(menu, 0, MF_BYPOSITION);
423 			CheckMenuItem(menu, ID_SHOW_HIDDEN_ICONS, MF_BYCOMMAND|(_show_hidden?MF_CHECKED:MF_UNCHECKED));
424 			CheckMenuItem(menu, ID_SHOW_ICON_BUTTON, MF_BYCOMMAND|(_show_button?MF_CHECKED:MF_UNCHECKED));
425 			menu.TrackPopupMenu(_hwnd, p);
426 		}
427 		break;}
428 
429 	  case WM_COPYDATA: {	// receive NotifyHook answers
430 		String path;
431 		HWND hwnd;
432 
433 		if (_hook.ModulePathCopyData(lparam, &hwnd, path))
434 			_window_modules[hwnd] = path;
435 		break;}
436 
437 	  default:
438 		if (nmsg>=WM_MOUSEFIRST && nmsg<=WM_MOUSELAST) {
439 			 // close startup menu and other popup menus
440 			 // This functionality is missing in MS Windows.
441 			if (nmsg==WM_LBUTTONDOWN || nmsg==WM_MBUTTONDOWN || nmsg==WM_RBUTTONDOWN
442 #ifdef WM_XBUTTONDOWN
443 				|| nmsg==WM_XBUTTONDOWN
444 #endif
445 				)
446 
447 				CancelModes();
448 
449 			Point pt(lparam);
450 			NotifyIconSet::const_iterator found = IconHitTest(pt);
451 
452 			if (found != _sorted_icons.end()) {
453 				const NotifyInfo& entry = const_cast<NotifyInfo&>(*found);	// Why does GCC 3.3 need this additional const_cast ?!
454 
455 				 // set activation time stamp
456 				if (nmsg == WM_LBUTTONDOWN ||	// Some programs need PostMessage() instead of SendMessage().
457 					nmsg == WM_MBUTTONDOWN ||	// So call SendMessage() only for BUTTONUP and BLCLK messages
458 #ifdef WM_XBUTTONDOWN
459 					nmsg == WM_XBUTTONDOWN ||
460 #endif
461 					nmsg == WM_RBUTTONDOWN) {
462 					_icon_map[entry]._lastChange = GetTickCount();
463 				}
464 
465 				 // Notify the message if the owner is still alive
466 				if (IsWindow(entry._hWnd)) {
467 					if (nmsg == WM_MOUSEMOVE ||		// avoid to call blocking SendMessage() for merely moving the mouse over icons
468 						nmsg == WM_LBUTTONDOWN ||	// Some programs need PostMessage() instead of SendMessage().
469 						nmsg == WM_MBUTTONDOWN ||	// So call SendMessage() only for BUTTONUP and BLCLK messages
470 #ifdef WM_XBUTTONDOWN
471 						nmsg == WM_XBUTTONDOWN ||
472 #endif
473 						nmsg == WM_RBUTTONDOWN)
474 						PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
475 					else {
476 						 // allow SetForegroundWindow() in client process
477 						DWORD pid;
478 
479 						if (GetWindowThreadProcessId(entry._hWnd, &pid)) {
480 							 // bind dynamically to AllowSetForegroundWindow() to be compatible to WIN98
481 							static DynamicFct<BOOL(WINAPI*)(DWORD)> AllowSetForegroundWindow(TEXT("USER32"), "AllowSetForegroundWindow");
482 
483 							if (AllowSetForegroundWindow)
484 								(*AllowSetForegroundWindow)(pid);
485 						}
486 
487 						 // use PostMessage() for notifcation icons of Shell Service Objects in the own process
488 						if (pid == GetCurrentProcessId())
489 							PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
490 						else
491 							SendMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
492 					}
493 				}
494 				else if (_icon_map.erase(entry))	// delete icons without valid owner window
495 					UpdateIcons();
496 			} else
497 				 // handle clicks on notification area button "show hidden icons"
498 				if (_show_button)
499 					if (nmsg == WM_LBUTTONDOWN)
500 						if (pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE &&
501 							pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE)
502 							PostMessage(_hwnd, WM_COMMAND, MAKEWPARAM(ID_SHOW_HIDDEN_ICONS,0), 0);
503 		}
504 
505 		return super::WndProc(nmsg, wparam, lparam);
506 	}
507 
508 	return 0;
509 }
510 
511 int NotifyArea::Command(int id, int code)
512 {
513 	switch(id) {
514 	  case ID_SHOW_HIDDEN_ICONS:
515 		_show_hidden = !_show_hidden;
516 		UpdateIcons();
517 		break;
518 
519 	  case ID_SHOW_ICON_BUTTON:
520 		_show_button = !_show_button;
521 		UpdateIcons();
522 		break;
523 
524 	  case ID_CONFIG_NOTIFYAREA:
525 		Dialog::DoModal(IDD_NOTIFYAREA, WINDOW_CREATOR(TrayNotifyDlg), GetParent(_hwnd));
526 		break;
527 
528 	  case ID_CONFIG_TIME:
529 		launch_cpanel(_hwnd, TEXT("timedate.cpl"));
530 		break;
531 
532 	  default:
533 		SendParent(WM_COMMAND, MAKELONG(id,code), 0);
534 	}
535 
536 	return 0;
537 }
538 
539 int NotifyArea::Notify(int id, NMHDR* pnmh)
540 {
541 	if (pnmh->code == TTN_GETDISPINFO) {
542 		LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
543 
544 		Point pt(GetMessagePos());
545 		ScreenToClient(_hwnd, &pt);
546 
547 		if (_show_button &&
548 			pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE &&
549 			pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE)
550 		{
551 			static ResString sShowIcons(IDS_SHOW_HIDDEN_ICONS);
552 			static ResString sHideIcons(IDS_HIDE_ICONS);
553 
554 			pdi->lpszText = (_show_hidden? sHideIcons: sShowIcons).str();
555 		} else {
556 			NotifyIconSet::iterator found = IconHitTest(pt);
557 
558 			if (found != _sorted_icons.end()) {
559 				NotifyInfo& entry = const_cast<NotifyInfo&>(*found);	// Why does GCC 3.3 need this additional const_cast ?!
560 
561 				 // enable multiline tooltips (break at CR/LF and for very long one-line strings)
562 				SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400);
563 
564 				pdi->lpszText = entry._tipText.str();
565 			}
566 		}
567 	}
568 
569 	return 0;
570 }
571 
572 void NotifyArea::CancelModes()
573 {
574 	PostMessage(HWND_BROADCAST, WM_CANCELMODE, 0, 0);
575 
576 	for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it)
577 		PostMessage(it->_hWnd, WM_CANCELMODE, 0, 0);
578 }
579 
580 LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pnid)
581 {
582 	switch(notify_code) {
583 	  case NIM_ADD:
584 	  case NIM_MODIFY:
585 		if ((int)pnid->uID >= 0) {	///@todo This is a fix for Windows Task Manager.
586 			NotifyInfo& entry = _icon_map[pnid];
587 
588 			 // a new entry?
589 			if (entry._idx == -1)
590 				entry._idx = ++_next_idx;
591 		/* equivalent code using iterator::find();
592 			NotifyIconMap::iterator found = _icon_map.find(pnid);
593 			NotifyInfo* pentry;
594 			 // a new entry?
595 			if (found == _icon_map.end()) {
596 				pentry = &_icon_map[pnid];
597 				pentry->_idx = ++_next_idx;
598 			} else {
599 				pentry = &found->second;
600 			}
601 			NotifyInfo& entry = *pentry;
602 		*/
603 			bool changes = entry.modify(pnid);
604 
605 			if (DetermineHideState(entry) && entry._mode==NIM_HIDE) {
606 				entry._dwState |= NIS_HIDDEN;
607 				changes = true;
608 			}
609 
610 			if (changes)
611 				UpdateIcons();	///@todo call only if really changes occurred
612 
613 			return TRUE;
614 		}
615 		break;
616 
617 	  case NIM_DELETE: {
618 		NotifyIconMap::iterator found = _icon_map.find(pnid);
619 
620 		if (found != _icon_map.end()) {
621 			if (found->second._hIcon)
622 				DestroyIcon(found->second._hIcon);
623 			_icon_map.erase(found);
624 			UpdateIcons();
625 			return TRUE;
626 		}
627 		break;}
628 
629 	  case NIM_SETFOCUS:
630 		SetForegroundWindow(_hwnd);
631 		return TRUE;
632 
633 	  case NIM_SETVERSION:
634 		NotifyIconMap::iterator found = _icon_map.find(pnid);
635 
636 		if (found != _icon_map.end()) {
637 			found->second._version = pnid->UNION_MEMBER(uVersion);
638 			return TRUE;
639 		} else
640 			return FALSE;
641 	}
642 
643 	return FALSE;
644 }
645 
646 void NotifyArea::UpdateIcons()
647 {
648 	_sorted_icons.clear();
649 
650 	 // sort icon infos by display index
651 	for(NotifyIconMap::const_iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
652 		const NotifyInfo& entry = it->second;
653 
654 		if (_show_hidden || !(entry._dwState & NIS_HIDDEN))
655 			_sorted_icons.insert(entry);
656 	}
657 
658 	 // sync tooltip areas to current icon number
659 	if (_sorted_icons.size() != _last_icon_count) {
660 		RECT rect = {NOTIFYICON_X, NOTIFYICON_Y, NOTIFYICON_X+NOTIFYICON_SIZE, NOTIFYICON_Y+NOTIFYICON_SIZE};
661 
662 		size_t tt_idx = 0;
663 
664 		if (_show_button) {
665 			_tooltip.add(_hwnd, tt_idx++, rect);
666 
667 			rect.left += NOTIFYICON_DIST;
668 			rect.right += NOTIFYICON_DIST;
669 		}
670 
671 		size_t icon_cnt = _sorted_icons.size();
672 		while(tt_idx < icon_cnt) {
673 			_tooltip.add(_hwnd, tt_idx++, rect);
674 
675 			rect.left += NOTIFYICON_DIST;
676 			rect.right += NOTIFYICON_DIST;
677 		}
678 
679 		while(tt_idx < _last_icon_count)
680 			_tooltip.remove(_hwnd, tt_idx++);
681 
682 		_last_icon_count = _sorted_icons.size();
683 	}
684 
685 	SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
686 
687 	InvalidateRect(_hwnd, NULL, FALSE);	// refresh icon display
688 	UpdateWindow(_hwnd);
689 }
690 
691 #ifndef _NO_ALPHABLEND
692 #ifdef _MSC_VER
693 #pragma comment(lib, "msimg32")	// for AlphaBlend()
694 #endif
695 #endif
696 
697 void NotifyArea::Paint()
698 {
699 	BufferedPaintCanvas canvas(_hwnd);
700 
701 	 // first fill with the background color
702 	FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
703 
704 	 // draw icons
705 	int x = NOTIFYICON_X;
706 	int y = NOTIFYICON_Y;
707 
708 	if (_show_button) {
709 		static SmallIcon leftArrowIcon(IDI_NOTIFY_L);
710 		static SmallIcon rightArrowIcon(IDI_NOTIFY_R);
711 
712 		DrawIconEx(canvas, x, y, _show_hidden?rightArrowIcon:leftArrowIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
713 		x += NOTIFYICON_DIST;
714 	}
715 
716 #ifndef _NO_ALPHABLEND
717 	MemCanvas mem_dc;
718 	SelectedBitmap bmp(mem_dc, CreateCompatibleBitmap(canvas, NOTIFYICON_SIZE, NOTIFYICON_SIZE));
719 	RECT rect = {0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE};
720 	BLENDFUNCTION blend = {AC_SRC_OVER, 0, 128, 0};	// 50 % visible
721 #endif
722 
723 	for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
724 #ifndef _NO_ALPHABLEND
725 		if (it->_dwState & NIS_HIDDEN) {
726 			FillRect(mem_dc, &rect, GetSysColorBrush(COLOR_BTNFACE));
727 			DrawIconEx(mem_dc, 0, 0, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
728 			AlphaBlend(canvas, x, y, NOTIFYICON_SIZE, NOTIFYICON_SIZE, mem_dc, 0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE, blend);
729 		} else
730 #endif
731 			DrawIconEx(canvas, x, y, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
732 
733 		x += NOTIFYICON_DIST;
734 	}
735 }
736 
737 void NotifyArea::Refresh(bool update)
738 {
739 	 // Look for task icons without valid owner window.
740 	 // This is an extended feature missing in MS Windows.
741 	for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
742 		const NotifyInfo& entry = *it;
743 
744 		if (!IsWindow(entry._hWnd))
745 			if (_icon_map.erase(entry))	// delete icons without valid owner window
746 				++update;
747 	}
748 
749 	DWORD now = GetTickCount();
750 
751 	 // handle icon hiding
752 	for(NotifyIconMap::iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
753 		NotifyInfo& entry = it->second;
754 
755 		DetermineHideState(entry);
756 
757 		switch(entry._mode) {
758 		  case NIM_HIDE:
759 			if (!(entry._dwState & NIS_HIDDEN)) {
760 				entry._dwState |= NIS_HIDDEN;
761 				++update;
762 			}
763 			break;
764 
765 		  case NIM_SHOW:
766 			if (entry._dwState&NIS_HIDDEN) {
767 				entry._dwState &= ~NIS_HIDDEN;
768 				++update;
769 			}
770 			break;
771 
772 		  case NIM_AUTO:
773 			 // automatically hide icons after long periods of inactivity
774 			if (_hide_inactive)
775 				if (!(entry._dwState & NIS_HIDDEN))
776 					if (now-entry._lastChange > ICON_AUTOHIDE_SECONDS*1000) {
777 						entry._dwState |= NIS_HIDDEN;
778 						++update;
779 					}
780 			break;
781 		}
782 	}
783 
784 	if (update)
785 		UpdateIcons();
786 }
787 
788  /// search for a icon at a given client coordinate position
789 NotifyIconSet::iterator NotifyArea::IconHitTest(const POINT& pos)
790 {
791 	if (pos.y<NOTIFYICON_Y || pos.y>=NOTIFYICON_Y+NOTIFYICON_SIZE)
792 		return _sorted_icons.end();
793 
794 	NotifyIconSet::iterator it = _sorted_icons.begin();
795 
796 	int x = NOTIFYICON_X;
797 
798 	if (_show_button)
799 		x += NOTIFYICON_DIST;
800 
801 	for(; it!=_sorted_icons.end(); ++it) {
802 		//NotifyInfo& entry = const_cast<NotifyInfo&>(*it);	// Why does GCC 3.3 need this additional const_cast ?!
803 
804 		if (pos.x>=x && pos.x<x+NOTIFYICON_SIZE)
805 			break;
806 
807 		x += NOTIFYICON_DIST;
808 	}
809 
810 	return it;
811 }
812 
813 
814 void NotifyIconConfig::create_name()
815 {
816 	_name = FmtString(TEXT("'%s' - '%s' - '%s'"), _tipText.c_str(), _windowTitle.c_str(), _modulePath.c_str());
817 }
818 
819 
820 bool NotifyIconConfig::match(const NotifyIconConfig& props) const
821 {
822 	if (!_tipText.empty() && !props._tipText.empty())
823 		if (props._tipText == _tipText)
824 			return true;
825 
826 	if (!_windowTitle.empty() && !props._windowTitle.empty())
827 		if (_tcsstr(props._windowTitle, _windowTitle))
828 			return true;
829 
830 	if (!_modulePath.empty() && !props._modulePath.empty())
831 		if (!_tcsicmp(props._modulePath, _modulePath))
832 			return true;
833 
834 	return false;
835 }
836 
837 bool NotifyArea::DetermineHideState(NotifyInfo& entry)
838 {
839 	if (entry._modulePath.empty()) {
840 		const String& modulePath = _window_modules[entry._hWnd];
841 
842 		 // request module path for new windows (We will get an asynchronous answer by a WM_COPYDATA message.)
843 		if (!modulePath.empty())
844 			entry._modulePath = modulePath;
845 		else
846 			_hook.GetModulePath(entry._hWnd, _hwnd);
847 	}
848 
849 	for(NotifyIconCfgList::const_iterator it=_cfg.begin(); it!=_cfg.end(); ++it) {
850 		const NotifyIconConfig& cfg = *it;
851 
852 		if (cfg.match(entry)) {
853 			entry._mode = cfg._mode;
854 			return true;
855 		}
856 	}
857 
858 	return false;
859 }
860 
861 
862 
863 String string_from_mode(NOTIFYICONMODE mode)
864 {
865 	switch(mode) {
866 	  case NIM_SHOW:
867 		return ResString(IDS_NOTIFY_SHOW);
868 
869 	  case NIM_HIDE:
870 		return ResString(IDS_NOTIFY_HIDE);
871 
872 	  default:	//case NIM_AUTO
873 		return ResString(IDS_NOTIFY_AUTOHIDE);
874 	}
875 }
876 
877 
878 TrayNotifyDlg::TrayNotifyDlg(HWND hwnd)
879  :	super(hwnd),
880 	_tree_ctrl(GetDlgItem(hwnd, IDC_NOTIFY_ICONS)),
881 	_himl(ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR24, 3, 0)),
882 	_pNotifyArea(static_cast<NotifyArea*>(Window::get_window((HWND)SendMessage(g_Globals._hwndDesktopBar, PM_GET_NOTIFYAREA, 0, 0))))
883 {
884 	_selectedItem = 0;
885 
886 	if (_pNotifyArea) {
887 		 // save original icon states and configuration data
888 		for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it)
889 			_icon_states_org[it->first] = IconStatePair(it->second._mode, it->second._dwState);
890 
891 		_cfg_org = _pNotifyArea->_cfg;
892 		_show_hidden_org = _pNotifyArea->_show_hidden;
893 	}
894 
895 	SetWindowIcon(hwnd, IDI_REACTOS);
896 
897 	_haccel = LoadAccelerators(g_Globals._hInstance, MAKEINTRESOURCE(IDA_TRAYNOTIFY));
898 
899 	{
900 	WindowCanvas canvas(_hwnd);
901 	HBRUSH hbkgnd = GetStockBrush(WHITE_BRUSH);
902 
903 	ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT), hbkgnd, canvas);
904 	ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_TRANS), hbkgnd, canvas);
905 	ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_RED), hbkgnd, canvas);
906 	}
907 
908 	(void)TreeView_SetImageList(_tree_ctrl, _himl, TVSIL_NORMAL);
909 
910 	_resize_mgr.Add(IDC_NOTIFY_ICONS,	RESIZE);
911 	_resize_mgr.Add(IDC_LABEL1,			MOVE_Y);
912 	_resize_mgr.Add(IDC_NOTIFY_TOOLTIP,	RESIZE_X|MOVE_Y);
913 	_resize_mgr.Add(IDC_LABEL2,			MOVE_Y);
914 	_resize_mgr.Add(IDC_NOTIFY_TITLE,	RESIZE_X|MOVE_Y);
915 	_resize_mgr.Add(IDC_LABEL3,			MOVE_Y);
916 	_resize_mgr.Add(IDC_NOTIFY_MODULE,	RESIZE_X|MOVE_Y);
917 
918 	_resize_mgr.Add(IDC_LABEL4,			MOVE_Y);
919 	_resize_mgr.Add(IDC_NOTIFY_SHOW,	MOVE_Y);
920 	_resize_mgr.Add(IDC_NOTIFY_HIDE,	MOVE_Y);
921 	_resize_mgr.Add(IDC_NOTIFY_AUTOHIDE,MOVE_Y);
922 
923 	_resize_mgr.Add(IDC_PICTURE,		MOVE);
924 	_resize_mgr.Add(ID_SHOW_HIDDEN_ICONS,MOVE_Y);
925 
926 	_resize_mgr.Add(IDC_LABEL6,			MOVE_Y);
927 	_resize_mgr.Add(IDC_LAST_CHANGE,	MOVE_Y);
928 
929 	_resize_mgr.Add(IDOK,				MOVE);
930 	_resize_mgr.Add(IDCANCEL,			MOVE);
931 
932 	_resize_mgr.Resize(+150, +200);
933 
934 	Refresh();
935 
936 	SetTimer(_hwnd, 0, 3000, NULL);
937 	register_pretranslate(hwnd);
938 }
939 
940 TrayNotifyDlg::~TrayNotifyDlg()
941 {
942 	KillTimer(_hwnd, 0);
943 	unregister_pretranslate(_hwnd);
944 	ImageList_Destroy(_himl);
945 }
946 
947 void TrayNotifyDlg::Refresh()
948 {
949 	///@todo refresh incrementally
950 
951 	HiddenWindow hide(_tree_ctrl);
952 
953 	TreeView_DeleteAllItems(_tree_ctrl);
954 
955 	TV_INSERTSTRUCT tvi;
956 
957 	tvi.hParent = 0;
958 	tvi.hInsertAfter = TVI_LAST;
959 
960 	TV_ITEM& tv = tvi.item;
961 	tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
962 
963 	ResString str_cur(IDS_ITEMS_CUR);
964 	tv.pszText = str_cur.str();
965 	tv.iSelectedImage = tv.iImage = 0;	// IDI_DOT
966 	_hitemCurrent = TreeView_InsertItem(_tree_ctrl, &tvi);
967 
968 	ResString str_conf(IDS_ITEMS_CONFIGURED);
969 	tv.pszText = str_conf.str();
970 	tv.iSelectedImage = tv.iImage = 2;	// IDI_DOT_RED
971 	_hitemConfig = TreeView_InsertItem(_tree_ctrl, &tvi);
972 
973 	tvi.hParent = _hitemCurrent;
974 
975 	ResString str_visible(IDS_ITEMS_VISIBLE);
976 	tv.pszText = str_visible.str();
977 	tv.iSelectedImage = tv.iImage = 0;	// IDI_DOT
978 	_hitemCurrent_visible = TreeView_InsertItem(_tree_ctrl, &tvi);
979 
980 	ResString str_hidden(IDS_ITEMS_HIDDEN);
981 	tv.pszText = str_hidden.str();
982 	tv.iSelectedImage = tv.iImage = 1;	// IDI_DOT_TRANS
983 	_hitemCurrent_hidden = TreeView_InsertItem(_tree_ctrl, &tvi);
984 
985 	if (_pNotifyArea) {
986 		_info.clear();
987 
988 		tv.mask |= TVIF_PARAM;
989 
990 		WindowCanvas canvas(_hwnd);
991 
992 		 // insert current (visible and hidden) items
993 		for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it) {
994 			const NotifyInfo& entry = it->second;
995 
996 			InsertItem(entry._dwState&NIS_HIDDEN? _hitemCurrent_hidden: _hitemCurrent_visible, TVI_LAST, entry, canvas);
997 		}
998 
999 		 // insert configured items in tree view
1000 		const NotifyIconCfgList& cfg = _pNotifyArea->_cfg;
1001 		for(NotifyIconCfgList::const_iterator it=cfg.begin(); it!=cfg.end(); ++it) {
1002 			const NotifyIconConfig& cfg_entry = *it;
1003 
1004 			HICON hicon = 0;
1005 
1006 			if (!cfg_entry._modulePath.empty()) {
1007 				if ((int)ExtractIconEx(cfg_entry._modulePath, 0, NULL, &hicon, 1) <= 0)
1008 					hicon = 0;
1009 
1010 				if (!hicon) {
1011 					SHFILEINFO sfi;
1012 
1013 					if (SHGetFileInfo(cfg_entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
1014 						hicon = sfi.hIcon;
1015 				}
1016 			}
1017 
1018 			InsertItem(_hitemConfig, TVI_SORT, cfg_entry, canvas, hicon, cfg_entry._mode);
1019 
1020 			if (hicon)
1021 				DestroyIcon(hicon);
1022 		}
1023 
1024 		CheckDlgButton(_hwnd, ID_SHOW_HIDDEN_ICONS, _pNotifyArea->_show_hidden? BST_CHECKED: BST_UNCHECKED);
1025 	}
1026 
1027 	TreeView_Expand(_tree_ctrl, _hitemCurrent_visible, TVE_EXPAND);
1028 	TreeView_Expand(_tree_ctrl, _hitemCurrent_hidden, TVE_EXPAND);
1029 	TreeView_Expand(_tree_ctrl, _hitemCurrent, TVE_EXPAND);
1030 	TreeView_Expand(_tree_ctrl, _hitemConfig, TVE_EXPAND);
1031 
1032 	TreeView_EnsureVisible(_tree_ctrl, _hitemCurrent_visible);
1033 }
1034 
1035 void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyInfo& entry, HDC hdc)
1036 {
1037 	InsertItem(hparent, after, entry, hdc, entry._hIcon, entry._mode);
1038 }
1039 
1040 void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyIconDlgInfo& entry,
1041 								HDC hdc, HICON hicon, NOTIFYICONMODE mode)
1042 {
1043 	int idx = _info.size() + 1;
1044 	_info[idx] = entry;
1045 
1046 	String mode_str = string_from_mode(mode);
1047 
1048 	switch(mode) {
1049 	  case NIM_SHOW:	mode_str = ResString(IDS_NOTIFY_SHOW);		break;
1050 	  case NIM_HIDE:	mode_str = ResString(IDS_NOTIFY_HIDE);		break;
1051 	  case NIM_AUTO:	mode_str = ResString(IDS_NOTIFY_AUTOHIDE);
1052 	}
1053 
1054 	FmtString txt(TEXT("%s  -  %s  [%s]"), entry._tipText.c_str(), entry._windowTitle.c_str(), mode_str.c_str());
1055 
1056 	TV_INSERTSTRUCT tvi;
1057 
1058 	tvi.hParent = hparent;
1059 	tvi.hInsertAfter = after;
1060 
1061 	TV_ITEM& tv = tvi.item;
1062 	tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
1063 
1064 	tv.lParam = (LPARAM)idx;
1065 	tv.pszText = txt.str();
1066 	tv.iSelectedImage = tv.iImage = ImageList_AddAlphaIcon(_himl, hicon, GetStockBrush(WHITE_BRUSH), hdc);
1067 	(void)TreeView_InsertItem(_tree_ctrl, &tvi);
1068 }
1069 
1070 LRESULT TrayNotifyDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1071 {
1072 	switch(nmsg) {
1073 	  case PM_TRANSLATE_MSG: {
1074 		MSG* pmsg = (MSG*) lparam;
1075 
1076 		if (TranslateAccelerator(_hwnd, _haccel, pmsg))
1077 			return TRUE;
1078 
1079 		return FALSE;}
1080 
1081 	  case WM_TIMER:
1082 		Refresh();
1083 		break;
1084 
1085 	  default:
1086 		return super::WndProc(nmsg, wparam, lparam);
1087 	}
1088 
1089 	return 0;
1090 }
1091 
1092 int TrayNotifyDlg::Command(int id, int code)
1093 {
1094 	if (code == BN_CLICKED) {
1095 		switch(id) {
1096 		  case ID_REFRESH:
1097 			Refresh();
1098 			break;
1099 
1100 		  case IDC_NOTIFY_SHOW:
1101 			SetIconMode(NIM_SHOW);
1102 			break;
1103 
1104 		  case IDC_NOTIFY_HIDE:
1105 			SetIconMode(NIM_HIDE);
1106 			break;
1107 
1108 		  case IDC_NOTIFY_AUTOHIDE:
1109 			SetIconMode(NIM_AUTO);
1110 			break;
1111 
1112 		  case ID_SHOW_HIDDEN_ICONS:
1113 			if (_pNotifyArea)
1114 				SendMessage(*_pNotifyArea, WM_COMMAND, MAKEWPARAM(id,code), 0);
1115 			break;
1116 
1117 		  case IDOK:
1118 			EndDialog(_hwnd, id);
1119 			break;
1120 
1121 		  case IDCANCEL:
1122 			 // rollback changes
1123 			if (_pNotifyArea) {
1124 				 // restore original icon states and configuration data
1125 				_pNotifyArea->_cfg = _cfg_org;
1126 				_pNotifyArea->_show_hidden = _show_hidden_org;
1127 
1128 				for(IconStateMap::const_iterator it=_icon_states_org.begin(); it!=_icon_states_org.end(); ++it) {
1129 					NotifyInfo& info = _pNotifyArea->_icon_map[it->first];
1130 
1131 					info._mode = it->second.first;
1132 					info._dwState = it->second.second;
1133 				}
1134 
1135 				SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0);
1136 			}
1137 
1138 			EndDialog(_hwnd, id);
1139 			break;
1140 		}
1141 
1142 		return 0;
1143 	}
1144 
1145 	return 1;
1146 }
1147 
1148 int TrayNotifyDlg::Notify(int id, NMHDR* pnmh)
1149 {
1150 	switch(pnmh->code) {
1151 	  case TVN_SELCHANGED: {
1152 		NMTREEVIEW* pnmtv = (NMTREEVIEW*)pnmh;
1153 		int idx = pnmtv->itemNew.lParam;
1154 
1155 		if (idx) {
1156 			RefreshProperties(_info[idx]);
1157 			_selectedItem = pnmtv->itemNew.hItem;
1158 		} else {
1159 			/*
1160 			SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, NULL);
1161 			SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, NULL);
1162 			SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, NULL);
1163 			*/
1164 			CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, 0);
1165 		}
1166 		break;}
1167 	}
1168 
1169 	return 0;
1170 }
1171 
1172 void TrayNotifyDlg::RefreshProperties(const NotifyIconDlgInfo& entry)
1173 {
1174 	SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, entry._tipText);
1175 	SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, entry._windowTitle);
1176 	SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, entry._modulePath);
1177 
1178 	CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, IDC_NOTIFY_SHOW+entry._mode);
1179 
1180 	String change_str;
1181 	if (entry._lastChange)
1182 		change_str.printf(TEXT("before %d s"), (GetTickCount()-entry._lastChange+500)/1000);
1183 	SetDlgItemText(_hwnd, IDC_LAST_CHANGE, change_str);
1184 
1185 	HICON hicon = 0; //get_window_icon_big(entry._hWnd, false);
1186 
1187 	 // If we could not find an icon associated with the owner window, try to load one from the owning module.
1188 	if (!hicon && !entry._modulePath.empty()) {
1189 		hicon = ExtractIcon(g_Globals._hInstance, entry._modulePath, 0);
1190 
1191 		if (!hicon) {
1192 			SHFILEINFO sfi;
1193 
1194 			if (SHGetFileInfo(entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_LARGEICON))
1195 				hicon = sfi.hIcon;
1196 		}
1197 	}
1198 
1199 	if (hicon) {
1200 		SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, (LPARAM)hicon, 0);
1201 		DestroyIcon(hicon);
1202 	} else
1203 		SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, 0, 0);
1204 }
1205 
1206 void TrayNotifyDlg::SetIconMode(NOTIFYICONMODE mode)
1207 {
1208 	int idx = TreeView_GetItemData(_tree_ctrl, _selectedItem);
1209 
1210 	if (!idx)
1211 		return;
1212 
1213 	NotifyIconConfig& entry = _info[idx];
1214 
1215 	if (entry._mode != mode) {
1216 		entry._mode = mode;
1217 
1218 		 // trigger refresh in notify area and this dialog
1219 		if (_pNotifyArea)
1220 			SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0);
1221 	}
1222 
1223 	if (_pNotifyArea) {
1224 		bool found = false;
1225 
1226 		NotifyIconCfgList& cfg = _pNotifyArea->_cfg;
1227 		for(NotifyIconCfgList::iterator it=cfg.begin(); it!=cfg.end(); ++it) {
1228 			NotifyIconConfig& cfg_entry = *it;
1229 
1230 			if (cfg_entry.match(entry)) {
1231 				cfg_entry._mode = mode;
1232 				++found;
1233 				break;
1234 			}
1235 		}
1236 
1237 		if (!found) {
1238 			 // insert new configuration entry
1239 			NotifyIconConfig cfg_entry = entry;
1240 
1241 			cfg_entry._mode = mode;
1242 
1243 			_pNotifyArea->_cfg.push_back(cfg_entry);
1244 		}
1245 	}
1246 
1247 	Refresh();
1248 	///@todo select treeview item at new position in tree view -> refresh HTREEITEM in _selectedItem
1249 }
1250 
1251 
1252 ClockWindow::ClockWindow(HWND hwnd)
1253  :	super(hwnd),
1254 	_tooltip(hwnd)
1255 {
1256 	*_time = TEXT('\0');
1257 	FormatTime();
1258 
1259 	_tooltip.add(_hwnd, _hwnd);
1260 }
1261 
1262 HWND ClockWindow::Create(HWND hwndParent)
1263 {
1264 	static BtnWindowClass wcClock(CLASSNAME_CLOCKWINDOW, CS_DBLCLKS);
1265 
1266 	ClientRect clnt(hwndParent);
1267 
1268 	WindowCanvas canvas(hwndParent);
1269 	FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
1270 
1271 	RECT rect = {0, 0, 0, 0};
1272 	TCHAR buffer[16];
1273 	// Arbitrary high time so that the created clock window is big enough
1274 	SYSTEMTIME st = { 1601, 1, 0, 1, 23, 59, 59, 999 };
1275 
1276 	if (!GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buffer, sizeof(buffer)/sizeof(TCHAR)))
1277 		_tcscpy(buffer, TEXT("00:00"));
1278 
1279 	// Calculate the rectangle needed to draw the time (without actually drawing it)
1280 	DrawText(canvas, buffer, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
1281 	int clockwindowWidth = rect.right-rect.left + 4;
1282 
1283 	return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
1284 							wcClock, NULL, WS_CHILD|WS_VISIBLE,
1285 							clnt.right-(clockwindowWidth), 1, clockwindowWidth, clnt.bottom-2, hwndParent);
1286 }
1287 
1288 LRESULT ClockWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1289 {
1290 	switch(nmsg) {
1291 	  case WM_PAINT:
1292 		Paint();
1293 		break;
1294 
1295 	  case WM_LBUTTONDBLCLK:
1296 		launch_cpanel(_hwnd, TEXT("timedate.cpl"));
1297 		break;
1298 
1299 	  default:
1300 		return super::WndProc(nmsg, wparam, lparam);
1301 	}
1302 
1303 	return 0;
1304 }
1305 
1306 int ClockWindow::Notify(int id, NMHDR* pnmh)
1307 {
1308 	if (pnmh->code == TTN_GETDISPINFO) {
1309 		LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
1310 
1311 		SYSTEMTIME systime;
1312 		TCHAR buffer[64];
1313 
1314 		GetLocalTime(&systime);
1315 
1316 		if (GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systime, NULL, buffer, 64))
1317 			_tcscpy(pdi->szText, buffer);
1318 		else
1319 			pdi->szText[0] = '\0';
1320 	}
1321 
1322 	return 0;
1323 }
1324 
1325 void ClockWindow::TimerTick()
1326 {
1327 	if (FormatTime())
1328 		InvalidateRect(_hwnd, NULL, TRUE);	// refresh displayed time
1329 }
1330 
1331 bool ClockWindow::FormatTime()
1332 {
1333 	TCHAR buffer[16];
1334 
1335 	if (GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, NULL, buffer, sizeof(buffer)/sizeof(TCHAR)))
1336 		if (_tcscmp(buffer, _time)) {
1337 			_tcscpy(_time, buffer);
1338 			return true;	// The text to display has changed.
1339 		}
1340 
1341 	return false;	// no change
1342 }
1343 
1344 void ClockWindow::Paint()
1345 {
1346 	PaintCanvas canvas(_hwnd);
1347 
1348 	FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
1349 
1350 	BkMode bkmode(canvas, TRANSPARENT);
1351 	FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
1352 
1353 	DrawText(canvas, _time, -1, ClientRect(_hwnd), DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX);
1354 }
1355