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
NotifyHook()35 NotifyHook::NotifyHook()
36 : WM_GETMODULEPATH(InstallNotifyHook())
37 {
38 }
39
~NotifyHook()40 NotifyHook::~NotifyHook()
41 {
42 DeinstallNotifyHook();
43 }
44
GetModulePath(HWND hwnd,HWND hwndCallback)45 void NotifyHook::GetModulePath(HWND hwnd, HWND hwndCallback)
46 {
47 PostMessage(hwnd, WM_GETMODULEPATH, (WPARAM)hwndCallback, 0);
48 }
49
ModulePathCopyData(LPARAM lparam,HWND * phwnd,String & path)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
NotifyIconIndex(NOTIFYICONDATA * pnid)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
NotifyIconIndex()74 NotifyIconIndex::NotifyIconIndex()
75 {
76 _hWnd = 0;
77 _uID = 0;
78 }
79
80
NotifyInfo()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
modify(NOTIFYICONDATA * pnid)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
NotifyArea(HWND hwnd)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
~NotifyArea()216 NotifyArea::~NotifyArea()
217 {
218 KillTimer(_hwnd, 0);
219
220 write_config();
221 }
222
get_hide_clock_from_registry()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
read_config()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
write_config()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
show_clock(bool flag)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
Init(LPCREATESTRUCT pcs)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
Create(HWND hwndParent)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
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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
Command(int id,int code)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
Notify(int id,NMHDR * pnmh)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
CancelModes()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
ProcessTrayNotification(int notify_code,NOTIFYICONDATA * pnid)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
UpdateIcons()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
Paint()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
Refresh(bool update)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
IconHitTest(const POINT & pos)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
create_name()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
match(const NotifyIconConfig & props) const820 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
DetermineHideState(NotifyInfo & entry)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
string_from_mode(NOTIFYICONMODE mode)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
TrayNotifyDlg(HWND hwnd)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
~TrayNotifyDlg()940 TrayNotifyDlg::~TrayNotifyDlg()
941 {
942 KillTimer(_hwnd, 0);
943 unregister_pretranslate(_hwnd);
944 ImageList_Destroy(_himl);
945 }
946
Refresh()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
InsertItem(HTREEITEM hparent,HTREEITEM after,const NotifyInfo & entry,HDC hdc)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
InsertItem(HTREEITEM hparent,HTREEITEM after,const NotifyIconDlgInfo & entry,HDC hdc,HICON hicon,NOTIFYICONMODE mode)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
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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
Command(int id,int code)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
Notify(int id,NMHDR * pnmh)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
RefreshProperties(const NotifyIconDlgInfo & entry)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
SetIconMode(NOTIFYICONMODE mode)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
ClockWindow(HWND hwnd)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
Create(HWND hwndParent)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
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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
Notify(int id,NMHDR * pnmh)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
TimerTick()1325 void ClockWindow::TimerTick()
1326 {
1327 if (FormatTime())
1328 InvalidateRect(_hwnd, NULL, TRUE); // refresh displayed time
1329 }
1330
FormatTime()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
Paint()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