traynotify.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2003, 2004, 2005 Martin Fuchs
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 
00020  //
00021  // Explorer clone
00022  //
00023  // traynotify.cpp
00024  //
00025  // Martin Fuchs, 22.08.2003
00026  //
00027 
00028 
00029 #include <precomp.h>
00030 
00031 #include "../resource.h"
00032 
00033 #include "traynotify.h"
00034 
00035 
00036 #include "../notifyhook/notifyhook.h"
00037 
00038 NotifyHook::NotifyHook()
00039  :  WM_GETMODULEPATH(InstallNotifyHook())
00040 {
00041 }
00042 
00043 NotifyHook::~NotifyHook()
00044 {
00045     DeinstallNotifyHook();
00046 }
00047 
00048 void NotifyHook::GetModulePath(HWND hwnd, HWND hwndCallback)
00049 {
00050     PostMessage(hwnd, WM_GETMODULEPATH, (WPARAM)hwndCallback, 0);
00051 }
00052 
00053 bool NotifyHook::ModulePathCopyData(LPARAM lparam, HWND* phwnd, String& path)
00054 {
00055     char buffer[MAX_PATH];
00056 
00057     int l = GetWindowModulePathCopyData(lparam, phwnd, buffer, COUNTOF(buffer));
00058 
00059     if (l) {
00060         path.assign(buffer, l);
00061         return true;
00062     } else
00063         return false;
00064 }
00065 
00066 
00067 NotifyIconIndex::NotifyIconIndex(NOTIFYICONDATA* pnid)
00068 {
00069     _hWnd = pnid->hWnd;
00070     _uID = pnid->uID;
00071 
00072      // special handling for windows task manager
00073     if ((int)_uID < 0)
00074         _uID = 0;
00075 }
00076 
00077 NotifyIconIndex::NotifyIconIndex()
00078 {
00079     _hWnd = 0;
00080     _uID = 0;
00081 }
00082 
00083 
00084 NotifyInfo::NotifyInfo()
00085 {
00086     _idx = -1;
00087     _hIcon = 0;
00088     _dwState = 0;
00089     _uCallbackMessage = 0;
00090     _version = 0;
00091 
00092     _mode = NIM_AUTO;
00093     _lastChange = GetTickCount();
00094 }
00095 
00096 
00097  // WCHAR versions von NOTIFYICONDATA
00098 #define NID_SIZE_W6  sizeof(NOTIFYICONDATAW)                                        // _WIN32_IE = 0x600
00099 #define NID_SIZE_W5 (sizeof(NOTIFYICONDATAW)-sizeof(GUID))                          // _WIN32_IE = 0x500
00100 #define NID_SIZE_W3 (sizeof(NOTIFYICONDATAW)-sizeof(GUID)-(128-64)*sizeof(WCHAR))   // _WIN32_IE < 0x500
00101 
00102  // CHAR versions von NOTIFYICONDATA
00103 #define NID_SIZE_A6  sizeof(NOTIFYICONDATAA)
00104 #define NID_SIZE_A5 (sizeof(NOTIFYICONDATAA)-sizeof(GUID))
00105 #define NID_SIZE_A3 (sizeof(NOTIFYICONDATAA)-sizeof(GUID)-(128-64)*sizeof(CHAR))
00106 
00107 bool NotifyInfo::modify(NOTIFYICONDATA* pnid)
00108 {
00109     bool changes = false;
00110 
00111     if (_hWnd!=pnid->hWnd || _uID!=pnid->uID) {
00112         _hWnd = pnid->hWnd;
00113         _uID = pnid->uID;
00114 
00115         changes = true;
00116     }
00117 
00118     if (pnid->uFlags & NIF_MESSAGE) {
00119         if (_uCallbackMessage != pnid->uCallbackMessage) {
00120             _uCallbackMessage = pnid->uCallbackMessage;
00121             changes = true;
00122         }
00123     }
00124 
00125     if (pnid->uFlags & NIF_ICON) {
00126          // Some applications destroy the icon immediatelly after completing the
00127          // NIM_ADD/MODIFY message, so we have to make a copy of it.
00128         if (_hIcon)
00129             DestroyIcon(_hIcon);
00130 
00131         _hIcon = (HICON) CopyImage(pnid->hIcon, IMAGE_ICON, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0);
00132 
00133         changes = true; ///@todo compare icon
00134     }
00135 
00136 #ifdef NIF_STATE    // as of 21.08.2003 missing in MinGW headers
00137     if (pnid->uFlags & NIF_STATE) {
00138         DWORD new_state = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask);
00139 
00140         if (_dwState != new_state) {
00141             _dwState = new_state;
00142             changes = true;
00143         }
00144     }
00145 #endif
00146 
00147      // store tool tip text
00148     if (pnid->uFlags & NIF_TIP) {
00149         String new_text;
00150 
00151         if (pnid->cbSize==NID_SIZE_W6 || pnid->cbSize==NID_SIZE_W5 || pnid->cbSize==NID_SIZE_W3) {
00152              // UNICODE version of NOTIFYICONDATA structure
00153             LPCWSTR txt = (LPCWSTR)pnid->szTip;
00154             int max_len = pnid->cbSize==NID_SIZE_W3? 64: 128;
00155 
00156              // get tooltip string length
00157             int l = 0;
00158             for(; l<max_len; ++l)
00159                 if (!txt[l])
00160                     break;
00161 
00162             new_text.assign(txt, l);
00163 
00164             if (new_text != _tipText) {
00165                 _tipText = new_text;
00166                 changes = true;
00167             }
00168         } else if (pnid->cbSize==NID_SIZE_A6 || pnid->cbSize==NID_SIZE_A5 || pnid->cbSize==NID_SIZE_A3) {
00169             LPCSTR txt = (LPCSTR)pnid->szTip;
00170             int max_len = pnid->cbSize==NID_SIZE_A3? 64: 128;
00171 
00172             int l = 0;
00173             for(int l=0; l<max_len; ++l)
00174                 if (!txt[l])
00175                     break;
00176 
00177             new_text.assign(txt, l);
00178 
00179             if (new_text != _tipText) {
00180                 _tipText = new_text;
00181                 changes = true;
00182             }
00183         }
00184     }
00185 
00186     TCHAR title[MAX_PATH];
00187 
00188     DWORD pid;
00189     GetWindowThreadProcessId(_hWnd, &pid);
00190 
00191      // avoid to send WM_GETTEXT messages to the own process
00192     if (pid != GetCurrentProcessId())
00193         if (GetWindowText(_hWnd, title, COUNTOF(title))) {
00194             if (_windowTitle != title) {
00195                 _windowTitle = title;
00196                 changes = true;
00197             }
00198         }
00199 
00200     if (changes) {
00201         create_name();
00202         _lastChange = GetTickCount();
00203     }
00204 
00205     return changes;
00206 }
00207 
00208 
00209 NotifyArea::NotifyArea(HWND hwnd)
00210  :  super(hwnd),
00211     _tooltip(hwnd)
00212 {
00213     _next_idx = 0;
00214     _clock_width = 0;
00215     _last_icon_count = 0;
00216     _show_hidden = false;
00217     _hide_inactive = true;
00218     _show_button = true;
00219 }
00220 
00221 NotifyArea::~NotifyArea()
00222 {
00223     KillTimer(_hwnd, 0);
00224 
00225     write_config();
00226 }
00227 
00228 static bool get_hide_clock_from_registry()
00229 {
00230     HKEY hkeyStuckRects = 0;
00231     DWORD buffer[10];
00232     DWORD len = sizeof(buffer);
00233 
00234     bool hide_clock = false;
00235 
00236      // check if the clock should be hidden
00237     if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"), &hkeyStuckRects) &&
00238         !RegQueryValueEx(hkeyStuckRects, TEXT("Settings"), 0, NULL, (LPBYTE)buffer, &len) &&
00239         len==sizeof(buffer) && buffer[0]==sizeof(buffer))
00240         hide_clock = buffer[2] & 0x08? true: false;
00241 
00242     if (hkeyStuckRects)
00243         RegCloseKey(hkeyStuckRects);
00244 
00245     return hide_clock;
00246 }
00247 
00248 void NotifyArea::read_config()
00249 {
00250     bool clock_visible = true;
00251 
00252      // read notification icon settings from XML configuration
00253     XMLPos cfg_pos = g_Globals.get_cfg();
00254 
00255 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
00256     if (!g_Globals._SHRestricted || !SHRestricted(REST_HIDECLOCK))
00257 #endif
00258     {
00259         if (cfg_pos.go_down("desktopbar")) {
00260             clock_visible = XMLBoolRef(XMLPos(cfg_pos,"options"), "show-clock", !get_hide_clock_from_registry());
00261             cfg_pos.back();
00262         }
00263     }
00264 
00265     if (cfg_pos.go_down("notify-icons")) {
00266         XMLPos options(cfg_pos, "options");
00267 
00268         _hide_inactive = XMLBool(options, "hide-inactive", true);   ///@todo read default setting from registry
00269         _show_hidden = XMLBool(options, "show-hidden", false);  ///@todo read default setting from registry
00270         _show_button = XMLBool(options, "show-button", true);
00271 
00272         XMLChildrenFilter icons(cfg_pos, "icon");
00273 
00274         for(XMLChildrenFilter::iterator it=icons.begin(); it!=icons.end(); ++it) {
00275             const XMLNode& node = **it;
00276 
00277             NotifyIconConfig cfg;
00278 
00279             cfg._name = node.get("name").c_str();
00280             cfg._tipText = node.get("text").c_str();
00281             cfg._windowTitle = node.get("window").c_str();
00282             cfg._modulePath = node.get("module").c_str();
00283             const string& mode = node.get("show");
00284 
00285             if (mode == "show")
00286                 cfg._mode = NIM_SHOW;
00287             else if (mode == "hide")
00288                 cfg._mode = NIM_HIDE;
00289             else //if (mode == "auto")
00290                 cfg._mode = NIM_HIDE;
00291 
00292             _cfg.push_back(cfg);
00293         }
00294 
00295         cfg_pos.back();
00296     }
00297 
00298     show_clock(clock_visible);
00299 }
00300 
00301 void NotifyArea::write_config()
00302 {
00303      // write notification icon settings to XML configuration file
00304     XMLPos cfg_pos = g_Globals.get_cfg();
00305 
00306     cfg_pos.smart_create("desktopbar");
00307     XMLBoolRef boolRef(XMLPos(cfg_pos,"options"), "show-clock");
00308     boolRef = _hwndClock!=0;
00309     cfg_pos.back();
00310 
00311     cfg_pos.smart_create("notify-icons");
00312 
00313     XMLPos options(cfg_pos, "options");
00314     XMLBoolRef(options, "hide-inactive") = _hide_inactive;
00315     XMLBoolRef(options, "show-hidden") = _show_hidden;
00316     XMLBoolRef(options, "show-button") = _show_button;
00317 
00318     for(NotifyIconCfgList::iterator it=_cfg.begin(); it!=_cfg.end(); ++it) {
00319         NotifyIconConfig& cfg = *it;
00320 
00321          // search for the corresponding node using the original name
00322         cfg_pos.smart_create("icon", "name", cfg._name);
00323 
00324          // refresh unique name
00325         cfg.create_name();
00326 
00327         cfg_pos["name"] = cfg._name.c_str();
00328         cfg_pos["text"] = cfg._tipText.c_str();
00329         cfg_pos["window"] = cfg._windowTitle.c_str();
00330         cfg_pos["module"] = cfg._modulePath.c_str();
00331         cfg_pos["show"] = string_from_mode(cfg._mode).c_str();
00332 
00333         cfg_pos.back();
00334     }
00335 
00336     cfg_pos.back(); // smart_create
00337 }
00338 
00339 void NotifyArea::show_clock(bool flag)
00340 {
00341     bool vis = _hwndClock!=0;
00342 
00343     if (vis != flag) {
00344         if (flag) {
00345              // create clock window
00346             _hwndClock = ClockWindow::Create(_hwnd);
00347 
00348             if (_hwndClock) {
00349                 ClientRect clock_size(_hwndClock);
00350                 _clock_width = clock_size.right;
00351             }
00352         } else {
00353             DestroyWindow(_hwndClock);
00354             _hwndClock = 0;
00355             _clock_width = 0;
00356         }
00357 
00358         SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
00359     }
00360 }
00361 
00362 LRESULT NotifyArea::Init(LPCREATESTRUCT pcs)
00363 {
00364     if (super::Init(pcs))
00365         return 1;
00366 
00367     read_config();
00368 
00369     SetTimer(_hwnd, 0, 1000, NULL);
00370 
00371     return 0;
00372 }
00373 
00374 HWND NotifyArea::Create(HWND hwndParent)
00375 {
00376     static BtnWindowClass wcTrayNotify(CLASSNAME_TRAYNOTIFY, CS_DBLCLKS);
00377 
00378     ClientRect clnt(hwndParent);
00379 
00380     return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
00381                             wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN,
00382                             clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
00383 }
00384 
00385 LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
00386 {
00387     switch(nmsg) {
00388       case WM_PAINT:
00389         Paint();
00390         break;
00391 
00392       case WM_TIMER: {
00393         Refresh();
00394 
00395         ClockWindow* clock_window = GET_WINDOW(ClockWindow, _hwndClock);
00396 
00397         if (clock_window)
00398             clock_window->TimerTick();
00399         break;}
00400 
00401       case PM_REFRESH:
00402         Refresh(true);
00403         break;
00404 
00405       case WM_SIZE: {
00406         int cx = LOWORD(lparam);
00407         SetWindowPos(_hwndClock, 0, cx-_clock_width, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
00408         break;}
00409 
00410       case PM_GET_WIDTH: {
00411         int w = _sorted_icons.size()*NOTIFYICON_DIST + NOTIFYAREA_SPACE + _clock_width;
00412         if (_show_button)
00413             w += NOTIFYICON_DIST;
00414         return w;}
00415 
00416       case PM_REFRESH_CONFIG:
00417         read_config();
00418         break;
00419 
00420       case WM_CONTEXTMENU: {
00421         Point pt(lparam);
00422         POINTS p;
00423         p.x = (SHORT) pt.x;
00424         p.y = (SHORT) pt.y;
00425         ScreenToClient(_hwnd, &pt);
00426 
00427         if (IconHitTest(pt) == _sorted_icons.end()) { // display menu only when no icon clicked
00428             PopupMenu menu(IDM_NOTIFYAREA);
00429             SetMenuDefaultItem(menu, 0, MF_BYPOSITION);
00430             CheckMenuItem(menu, ID_SHOW_HIDDEN_ICONS, MF_BYCOMMAND|(_show_hidden?MF_CHECKED:MF_UNCHECKED));
00431             CheckMenuItem(menu, ID_SHOW_ICON_BUTTON, MF_BYCOMMAND|(_show_button?MF_CHECKED:MF_UNCHECKED));
00432             menu.TrackPopupMenu(_hwnd, p);
00433         }
00434         break;}
00435 
00436       case WM_COPYDATA: {   // receive NotifyHook answers
00437         String path;
00438         HWND hwnd;
00439 
00440         if (_hook.ModulePathCopyData(lparam, &hwnd, path))
00441             _window_modules[hwnd] = path;
00442         break;}
00443 
00444       default:
00445         if (nmsg>=WM_MOUSEFIRST && nmsg<=WM_MOUSELAST) {
00446              // close startup menu and other popup menus
00447              // This functionality is missing in MS Windows.
00448             if (nmsg==WM_LBUTTONDOWN || nmsg==WM_MBUTTONDOWN || nmsg==WM_RBUTTONDOWN
00449 #ifdef WM_XBUTTONDOWN
00450                 || nmsg==WM_XBUTTONDOWN
00451 #endif
00452                 )
00453 
00454                 CancelModes();
00455 
00456             Point pt(lparam);
00457             NotifyIconSet::const_iterator found = IconHitTest(pt);
00458 
00459             if (found != _sorted_icons.end()) {
00460                 const NotifyInfo& entry = const_cast<NotifyInfo&>(*found);  // Why does GCC 3.3 need this additional const_cast ?!
00461 
00462                  // set activation time stamp
00463                 if (nmsg == WM_LBUTTONDOWN ||   // Some programs need PostMessage() instead of SendMessage().
00464                     nmsg == WM_MBUTTONDOWN ||   // So call SendMessage() only for BUTTONUP and BLCLK messages
00465 #ifdef WM_XBUTTONDOWN
00466                     nmsg == WM_XBUTTONDOWN ||
00467 #endif
00468                     nmsg == WM_RBUTTONDOWN) {
00469                     _icon_map[entry]._lastChange = GetTickCount();
00470                 }
00471 
00472                  // Notify the message if the owner is still alive
00473                 if (IsWindow(entry._hWnd)) {
00474                     if (nmsg == WM_MOUSEMOVE ||     // avoid to call blocking SendMessage() for merely moving the mouse over icons
00475                         nmsg == WM_LBUTTONDOWN ||   // Some programs need PostMessage() instead of SendMessage().
00476                         nmsg == WM_MBUTTONDOWN ||   // So call SendMessage() only for BUTTONUP and BLCLK messages
00477 #ifdef WM_XBUTTONDOWN
00478                         nmsg == WM_XBUTTONDOWN ||
00479 #endif
00480                         nmsg == WM_RBUTTONDOWN)
00481                         PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
00482                     else {
00483                          // allow SetForegroundWindow() in client process
00484                         DWORD pid;
00485 
00486                         if (GetWindowThreadProcessId(entry._hWnd, &pid)) {
00487                              // bind dynamically to AllowSetForegroundWindow() to be compatible to WIN98
00488                             static DynamicFct<BOOL(WINAPI*)(DWORD)> AllowSetForegroundWindow(TEXT("USER32"), "AllowSetForegroundWindow");
00489 
00490                             if (AllowSetForegroundWindow)
00491                                 (*AllowSetForegroundWindow)(pid);
00492                         }
00493 
00494                          // use PostMessage() for notifcation icons of Shell Service Objects in the own process
00495                         if (pid == GetCurrentProcessId())
00496                             PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
00497                         else
00498                             SendMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg);
00499                     }
00500                 }
00501                 else if (_icon_map.erase(entry))    // delete icons without valid owner window
00502                     UpdateIcons();
00503             } else
00504                  // handle clicks on notification area button "show hidden icons"
00505                 if (_show_button)
00506                     if (nmsg == WM_LBUTTONDOWN)
00507                         if (pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE &&
00508                             pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE)
00509                             PostMessage(_hwnd, WM_COMMAND, MAKEWPARAM(ID_SHOW_HIDDEN_ICONS,0), 0);
00510         }
00511 
00512         return super::WndProc(nmsg, wparam, lparam);
00513     }
00514 
00515     return 0;
00516 }
00517 
00518 int NotifyArea::Command(int id, int code)
00519 {
00520     switch(id) {
00521       case ID_SHOW_HIDDEN_ICONS:
00522         _show_hidden = !_show_hidden;
00523         UpdateIcons();
00524         break;
00525 
00526       case ID_SHOW_ICON_BUTTON:
00527         _show_button = !_show_button;
00528         UpdateIcons();
00529         break;
00530 
00531       case ID_CONFIG_NOTIFYAREA:
00532         Dialog::DoModal(IDD_NOTIFYAREA, WINDOW_CREATOR(TrayNotifyDlg), GetParent(_hwnd));
00533         break;
00534 
00535       case ID_CONFIG_TIME:
00536         launch_cpanel(_hwnd, TEXT("timedate.cpl"));
00537         break;
00538 
00539       default:
00540         SendParent(WM_COMMAND, MAKELONG(id,code), 0);
00541     }
00542 
00543     return 0;
00544 }
00545 
00546 int NotifyArea::Notify(int id, NMHDR* pnmh)
00547 {
00548     if (pnmh->code == TTN_GETDISPINFO) {
00549         LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
00550 
00551         Point pt(GetMessagePos());
00552         ScreenToClient(_hwnd, &pt);
00553 
00554         if (_show_button &&
00555             pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE &&
00556             pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE)
00557         {
00558             static ResString sShowIcons(IDS_SHOW_HIDDEN_ICONS);
00559             static ResString sHideIcons(IDS_HIDE_ICONS);
00560 
00561             pdi->lpszText = (_show_hidden? sHideIcons: sShowIcons).str();
00562         } else {
00563             NotifyIconSet::iterator found = IconHitTest(pt);
00564 
00565             if (found != _sorted_icons.end()) {
00566                 NotifyInfo& entry = const_cast<NotifyInfo&>(*found);    // Why does GCC 3.3 need this additional const_cast ?!
00567 
00568                  // enable multiline tooltips (break at CR/LF and for very long one-line strings)
00569                 SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400);
00570 
00571                 pdi->lpszText = entry._tipText.str();
00572             }
00573         }
00574     }
00575 
00576     return 0;
00577 }
00578 
00579 void NotifyArea::CancelModes()
00580 {
00581     PostMessage(HWND_BROADCAST, WM_CANCELMODE, 0, 0);
00582 
00583     for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it)
00584         PostMessage(it->_hWnd, WM_CANCELMODE, 0, 0);
00585 }
00586 
00587 LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pnid)
00588 {
00589     switch(notify_code) {
00590       case NIM_ADD:
00591       case NIM_MODIFY:
00592         if ((int)pnid->uID >= 0) {  ///@todo This is a fix for Windows Task Manager.
00593             NotifyInfo& entry = _icon_map[pnid];
00594 
00595              // a new entry?
00596             if (entry._idx == -1)
00597                 entry._idx = ++_next_idx;
00598         /* equivalent code using iterator::find();
00599             NotifyIconMap::iterator found = _icon_map.find(pnid);
00600             NotifyInfo* pentry;
00601              // a new entry?
00602             if (found == _icon_map.end()) {
00603                 pentry = &_icon_map[pnid];
00604                 pentry->_idx = ++_next_idx;
00605             } else {
00606                 pentry = &found->second;
00607             }
00608             NotifyInfo& entry = *pentry;
00609         */
00610             bool changes = entry.modify(pnid);
00611 
00612 #if NOTIFYICON_VERSION>=3   // as of 21.08.2003 missing in MinGW headers
00613             if (DetermineHideState(entry) && entry._mode==NIM_HIDE) {
00614                 entry._dwState |= NIS_HIDDEN;
00615                 changes = true;
00616             }
00617 #endif
00618 
00619             if (changes)
00620                 UpdateIcons();  ///@todo call only if really changes occurred
00621 
00622             return TRUE;
00623         }
00624         break;
00625 
00626       case NIM_DELETE: {
00627         NotifyIconMap::iterator found = _icon_map.find(pnid);
00628 
00629         if (found != _icon_map.end()) {
00630             if (found->second._hIcon)
00631                 DestroyIcon(found->second._hIcon);
00632             _icon_map.erase(found);
00633             UpdateIcons();
00634             return TRUE;
00635         }
00636         break;}
00637 
00638 #if NOTIFYICON_VERSION>=3   // as of 21.08.2003 missing in MinGW headers
00639       case NIM_SETFOCUS:
00640         SetForegroundWindow(_hwnd);
00641         return TRUE;
00642 
00643       case NIM_SETVERSION:
00644         NotifyIconMap::iterator found = _icon_map.find(pnid);
00645 
00646         if (found != _icon_map.end()) {
00647             found->second._version = pnid->UNION_MEMBER(uVersion);
00648             return TRUE;
00649         } else
00650             return FALSE;
00651 #endif
00652     }
00653 
00654     return FALSE;
00655 }
00656 
00657 void NotifyArea::UpdateIcons()
00658 {
00659     _sorted_icons.clear();
00660 
00661      // sort icon infos by display index
00662     for(NotifyIconMap::const_iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
00663         const NotifyInfo& entry = it->second;
00664 
00665 #ifdef NIF_STATE    // as of 21.08.2003 missing in MinGW headers
00666         if (_show_hidden || !(entry._dwState & NIS_HIDDEN))
00667 #endif
00668             _sorted_icons.insert(entry);
00669     }
00670 
00671      // sync tooltip areas to current icon number
00672     if (_sorted_icons.size() != _last_icon_count) {
00673         RECT rect = {NOTIFYICON_X, NOTIFYICON_Y, NOTIFYICON_X+NOTIFYICON_SIZE, NOTIFYICON_Y+NOTIFYICON_SIZE};
00674 
00675         size_t tt_idx = 0;
00676 
00677         if (_show_button) {
00678             _tooltip.add(_hwnd, tt_idx++, rect);
00679 
00680             rect.left += NOTIFYICON_DIST;
00681             rect.right += NOTIFYICON_DIST;
00682         }
00683 
00684         size_t icon_cnt = _sorted_icons.size();
00685         while(tt_idx < icon_cnt) {
00686             _tooltip.add(_hwnd, tt_idx++, rect);
00687 
00688             rect.left += NOTIFYICON_DIST;
00689             rect.right += NOTIFYICON_DIST;
00690         }
00691 
00692         while(tt_idx < _last_icon_count)
00693             _tooltip.remove(_hwnd, tt_idx++);
00694 
00695         _last_icon_count = _sorted_icons.size();
00696     }
00697 
00698     SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
00699 
00700     InvalidateRect(_hwnd, NULL, FALSE); // refresh icon display
00701     UpdateWindow(_hwnd);
00702 }
00703 
00704 #ifndef _NO_ALPHABLEND
00705 #ifdef _MSC_VER
00706 #pragma comment(lib, "msimg32") // for AlphaBlend()
00707 #endif
00708 #endif
00709 
00710 void NotifyArea::Paint()
00711 {
00712     BufferedPaintCanvas canvas(_hwnd);
00713 
00714      // first fill with the background color
00715     FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
00716 
00717      // draw icons
00718     int x = NOTIFYICON_X;
00719     int y = NOTIFYICON_Y;
00720 
00721     if (_show_button) {
00722         static SmallIcon leftArrowIcon(IDI_NOTIFY_L);
00723         static SmallIcon rightArrowIcon(IDI_NOTIFY_R);
00724 
00725         DrawIconEx(canvas, x, y, _show_hidden?rightArrowIcon:leftArrowIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
00726         x += NOTIFYICON_DIST;
00727     }
00728 
00729 #ifndef _NO_ALPHABLEND
00730     MemCanvas mem_dc;
00731     SelectedBitmap bmp(mem_dc, CreateCompatibleBitmap(canvas, NOTIFYICON_SIZE, NOTIFYICON_SIZE));
00732     RECT rect = {0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE};
00733     BLENDFUNCTION blend = {AC_SRC_OVER, 0, 128, 0}; // 50 % visible
00734 #endif
00735 
00736     for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
00737 #ifndef _NO_ALPHABLEND
00738         if (it->_dwState & NIS_HIDDEN) {
00739             FillRect(mem_dc, &rect, GetSysColorBrush(COLOR_BTNFACE));
00740             DrawIconEx(mem_dc, 0, 0, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
00741             AlphaBlend(canvas, x, y, NOTIFYICON_SIZE, NOTIFYICON_SIZE, mem_dc, 0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE, blend);
00742         } else
00743 #endif
00744             DrawIconEx(canvas, x, y, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
00745 
00746         x += NOTIFYICON_DIST;
00747     }
00748 }
00749 
00750 void NotifyArea::Refresh(bool update)
00751 {
00752      // Look for task icons without valid owner window.
00753      // This is an extended feature missing in MS Windows.
00754     for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {
00755         const NotifyInfo& entry = *it;
00756 
00757         if (!IsWindow(entry._hWnd))
00758             if (_icon_map.erase(entry)) // delete icons without valid owner window
00759                 ++update;
00760     }
00761 
00762     DWORD now = GetTickCount();
00763 
00764      // handle icon hiding
00765     for(NotifyIconMap::iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) {
00766         NotifyInfo& entry = it->second;
00767 
00768         DetermineHideState(entry);
00769 
00770         switch(entry._mode) {
00771           case NIM_HIDE:
00772             if (!(entry._dwState & NIS_HIDDEN)) {
00773                 entry._dwState |= NIS_HIDDEN;
00774                 ++update;
00775             }
00776             break;
00777 
00778           case NIM_SHOW:
00779             if (entry._dwState&NIS_HIDDEN) {
00780                 entry._dwState &= ~NIS_HIDDEN;
00781                 ++update;
00782             }
00783             break;
00784 
00785           case NIM_AUTO:
00786              // automatically hide icons after long periods of inactivity
00787             if (_hide_inactive)
00788                 if (!(entry._dwState & NIS_HIDDEN))
00789                     if (now-entry._lastChange > ICON_AUTOHIDE_SECONDS*1000) {
00790                         entry._dwState |= NIS_HIDDEN;
00791                         ++update;
00792                     }
00793             break;
00794         }
00795     }
00796 
00797     if (update)
00798         UpdateIcons();
00799 }
00800 
00801  /// search for a icon at a given client coordinate position
00802 NotifyIconSet::iterator NotifyArea::IconHitTest(const POINT& pos)
00803 {
00804     if (pos.y<NOTIFYICON_Y || pos.y>=NOTIFYICON_Y+NOTIFYICON_SIZE)
00805         return _sorted_icons.end();
00806 
00807     NotifyIconSet::iterator it = _sorted_icons.begin();
00808 
00809     int x = NOTIFYICON_X;
00810 
00811     if (_show_button)
00812         x += NOTIFYICON_DIST;
00813 
00814     for(; it!=_sorted_icons.end(); ++it) {
00815         //NotifyInfo& entry = const_cast<NotifyInfo&>(*it); // Why does GCC 3.3 need this additional const_cast ?!
00816 
00817         if (pos.x>=x && pos.x<x+NOTIFYICON_SIZE)
00818             break;
00819 
00820         x += NOTIFYICON_DIST;
00821     }
00822 
00823     return it;
00824 }
00825 
00826 
00827 void NotifyIconConfig::create_name()
00828 {
00829     _name = FmtString(TEXT("'%s' - '%s' - '%s'"), _tipText.c_str(), _windowTitle.c_str(), _modulePath.c_str());
00830 }
00831 
00832 
00833 #if NOTIFYICON_VERSION>=3   // as of 21.08.2003 missing in MinGW headers
00834 
00835 bool NotifyIconConfig::match(const NotifyIconConfig& props) const
00836 {
00837     if (!_tipText.empty() && !props._tipText.empty())
00838         if (props._tipText == _tipText)
00839             return true;
00840 
00841     if (!_windowTitle.empty() && !props._windowTitle.empty())
00842         if (_tcsstr(props._windowTitle, _windowTitle))
00843             return true;
00844 
00845     if (!_modulePath.empty() && !props._modulePath.empty())
00846         if (!_tcsicmp(props._modulePath, _modulePath))
00847             return true;
00848 
00849     return false;
00850 }
00851 
00852 bool NotifyArea::DetermineHideState(NotifyInfo& entry)
00853 {
00854     if (entry._modulePath.empty()) {
00855         const String& modulePath = _window_modules[entry._hWnd];
00856 
00857          // request module path for new windows (We will get an asynchronous answer by a WM_COPYDATA message.)
00858         if (!modulePath.empty())
00859             entry._modulePath = modulePath;
00860         else
00861             _hook.GetModulePath(entry._hWnd, _hwnd);
00862     }
00863 
00864     for(NotifyIconCfgList::const_iterator it=_cfg.begin(); it!=_cfg.end(); ++it) {
00865         const NotifyIconConfig& cfg = *it;
00866 
00867         if (cfg.match(entry)) {
00868             entry._mode = cfg._mode;
00869             return true;
00870         }
00871     }
00872 
00873     return false;
00874 }
00875 
00876 #endif
00877 
00878 
00879 String string_from_mode(NOTIFYICONMODE mode)
00880 {
00881     switch(mode) {
00882       case NIM_SHOW:
00883         return ResString(IDS_NOTIFY_SHOW);
00884 
00885       case NIM_HIDE:
00886         return ResString(IDS_NOTIFY_HIDE);
00887 
00888       default:  //case NIM_AUTO
00889         return ResString(IDS_NOTIFY_AUTOHIDE);
00890     }
00891 }
00892 
00893 
00894 TrayNotifyDlg::TrayNotifyDlg(HWND hwnd)
00895  :  super(hwnd),
00896     _tree_ctrl(GetDlgItem(hwnd, IDC_NOTIFY_ICONS)),
00897     _himl(ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR24, 3, 0)),
00898     _pNotifyArea(static_cast<NotifyArea*>(Window::get_window((HWND)SendMessage(g_Globals._hwndDesktopBar, PM_GET_NOTIFYAREA, 0, 0))))
00899 {
00900     _selectedItem = 0;
00901 
00902     if (_pNotifyArea) {
00903          // save original icon states and configuration data
00904         for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it)
00905             _icon_states_org[it->first] = IconStatePair(it->second._mode, it->second._dwState);
00906 
00907         _cfg_org = _pNotifyArea->_cfg;
00908         _show_hidden_org = _pNotifyArea->_show_hidden;
00909     }
00910 
00911     SetWindowIcon(hwnd, IDI_REACTOS);
00912 
00913     _haccel = LoadAccelerators(g_Globals._hInstance, MAKEINTRESOURCE(IDA_TRAYNOTIFY));
00914 
00915     {
00916     WindowCanvas canvas(_hwnd);
00917     HBRUSH hbkgnd = GetStockBrush(WHITE_BRUSH);
00918 
00919     ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT), hbkgnd, canvas);
00920     ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_TRANS), hbkgnd, canvas);
00921     ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_RED), hbkgnd, canvas);
00922     }
00923 
00924     (void)TreeView_SetImageList(_tree_ctrl, _himl, TVSIL_NORMAL);
00925 
00926     _resize_mgr.Add(IDC_NOTIFY_ICONS,   RESIZE);
00927     _resize_mgr.Add(IDC_LABEL1,         MOVE_Y);
00928     _resize_mgr.Add(IDC_NOTIFY_TOOLTIP, RESIZE_X|MOVE_Y);
00929     _resize_mgr.Add(IDC_LABEL2,         MOVE_Y);
00930     _resize_mgr.Add(IDC_NOTIFY_TITLE,   RESIZE_X|MOVE_Y);
00931     _resize_mgr.Add(IDC_LABEL3,         MOVE_Y);
00932     _resize_mgr.Add(IDC_NOTIFY_MODULE,  RESIZE_X|MOVE_Y);
00933 
00934     _resize_mgr.Add(IDC_LABEL4,         MOVE_Y);
00935     _resize_mgr.Add(IDC_NOTIFY_SHOW,    MOVE_Y);
00936     _resize_mgr.Add(IDC_NOTIFY_HIDE,    MOVE_Y);
00937     _resize_mgr.Add(IDC_NOTIFY_AUTOHIDE,MOVE_Y);
00938 
00939     _resize_mgr.Add(IDC_PICTURE,        MOVE);
00940     _resize_mgr.Add(ID_SHOW_HIDDEN_ICONS,MOVE_Y);
00941 
00942     _resize_mgr.Add(IDC_LABEL6,         MOVE_Y);
00943     _resize_mgr.Add(IDC_LAST_CHANGE,    MOVE_Y);
00944 
00945     _resize_mgr.Add(IDOK,               MOVE);
00946     _resize_mgr.Add(IDCANCEL,           MOVE);
00947 
00948     _resize_mgr.Resize(+150, +200);
00949 
00950     Refresh();
00951 
00952     SetTimer(_hwnd, 0, 3000, NULL);
00953     register_pretranslate(hwnd);
00954 }
00955 
00956 TrayNotifyDlg::~TrayNotifyDlg()
00957 {
00958     KillTimer(_hwnd, 0);
00959     unregister_pretranslate(_hwnd);
00960     ImageList_Destroy(_himl);
00961 }
00962 
00963 void TrayNotifyDlg::Refresh()
00964 {
00965     ///@todo refresh incrementally
00966 
00967     HiddenWindow hide(_tree_ctrl);
00968 
00969     TreeView_DeleteAllItems(_tree_ctrl);
00970 
00971     TV_INSERTSTRUCT tvi;
00972 
00973     tvi.hParent = 0;
00974     tvi.hInsertAfter = TVI_LAST;
00975 
00976     TV_ITEM& tv = tvi.item;
00977     tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
00978 
00979     ResString str_cur(IDS_ITEMS_CUR);
00980     tv.pszText = str_cur.str();
00981     tv.iSelectedImage = tv.iImage = 0;  // IDI_DOT
00982     _hitemCurrent = TreeView_InsertItem(_tree_ctrl, &tvi);
00983 
00984     ResString str_conf(IDS_ITEMS_CONFIGURED);
00985     tv.pszText = str_conf.str();
00986     tv.iSelectedImage = tv.iImage = 2;  // IDI_DOT_RED
00987     _hitemConfig = TreeView_InsertItem(_tree_ctrl, &tvi);
00988 
00989     tvi.hParent = _hitemCurrent;
00990 
00991     ResString str_visible(IDS_ITEMS_VISIBLE);
00992     tv.pszText = str_visible.str();
00993     tv.iSelectedImage = tv.iImage = 0;  // IDI_DOT
00994     _hitemCurrent_visible = TreeView_InsertItem(_tree_ctrl, &tvi);
00995 
00996     ResString str_hidden(IDS_ITEMS_HIDDEN);
00997     tv.pszText = str_hidden.str();
00998     tv.iSelectedImage = tv.iImage = 1;  // IDI_DOT_TRANS
00999     _hitemCurrent_hidden = TreeView_InsertItem(_tree_ctrl, &tvi);
01000 
01001     if (_pNotifyArea) {
01002         _info.clear();
01003 
01004         tv.mask |= TVIF_PARAM;
01005 
01006         WindowCanvas canvas(_hwnd);
01007 
01008          // insert current (visible and hidden) items
01009         for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it) {
01010             const NotifyInfo& entry = it->second;
01011 
01012             InsertItem(entry._dwState&NIS_HIDDEN? _hitemCurrent_hidden: _hitemCurrent_visible, TVI_LAST, entry, canvas);
01013         }
01014 
01015          // insert configured items in tree view
01016         const NotifyIconCfgList& cfg = _pNotifyArea->_cfg;
01017         for(NotifyIconCfgList::const_iterator it=cfg.begin(); it!=cfg.end(); ++it) {
01018             const NotifyIconConfig& cfg_entry = *it;
01019 
01020             HICON hicon = 0;
01021 
01022             if (!cfg_entry._modulePath.empty()) {
01023                 if ((int)ExtractIconEx(cfg_entry._modulePath, 0, NULL, &hicon, 1) <= 0)
01024                     hicon = 0;
01025 
01026                 if (!hicon) {
01027                     SHFILEINFO sfi;
01028 
01029                     if (SHGetFileInfo(cfg_entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
01030                         hicon = sfi.hIcon;
01031                 }
01032             }
01033 
01034             InsertItem(_hitemConfig, TVI_SORT, cfg_entry, canvas, hicon, cfg_entry._mode);
01035 
01036             if (hicon)
01037                 DestroyIcon(hicon);
01038         }
01039 
01040         CheckDlgButton(_hwnd, ID_SHOW_HIDDEN_ICONS, _pNotifyArea->_show_hidden? BST_CHECKED: BST_UNCHECKED);
01041     }
01042 
01043     TreeView_Expand(_tree_ctrl, _hitemCurrent_visible, TVE_EXPAND);
01044     TreeView_Expand(_tree_ctrl, _hitemCurrent_hidden, TVE_EXPAND);
01045     TreeView_Expand(_tree_ctrl, _hitemCurrent, TVE_EXPAND);
01046     TreeView_Expand(_tree_ctrl, _hitemConfig, TVE_EXPAND);
01047 
01048     TreeView_EnsureVisible(_tree_ctrl, _hitemCurrent_visible);
01049 }
01050 
01051 void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyInfo& entry, HDC hdc)
01052 {
01053     InsertItem(hparent, after, entry, hdc, entry._hIcon, entry._mode);
01054 }
01055 
01056 void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyIconDlgInfo& entry,
01057                                 HDC hdc, HICON hicon, NOTIFYICONMODE mode)
01058 {
01059     int idx = _info.size() + 1;
01060     _info[idx] = entry;
01061 
01062     String mode_str = string_from_mode(mode);
01063 
01064     switch(mode) {
01065       case NIM_SHOW:    mode_str = ResString(IDS_NOTIFY_SHOW);      break;
01066       case NIM_HIDE:    mode_str = ResString(IDS_NOTIFY_HIDE);      break;
01067       case NIM_AUTO:    mode_str = ResString(IDS_NOTIFY_AUTOHIDE);
01068     }
01069 
01070     FmtString txt(TEXT("%s  -  %s  [%s]"), entry._tipText.c_str(), entry._windowTitle.c_str(), mode_str.c_str());
01071 
01072     TV_INSERTSTRUCT tvi;
01073 
01074     tvi.hParent = hparent;
01075     tvi.hInsertAfter = after;
01076 
01077     TV_ITEM& tv = tvi.item;
01078     tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
01079 
01080     tv.lParam = (LPARAM)idx;
01081     tv.pszText = txt.str();
01082     tv.iSelectedImage = tv.iImage = ImageList_AddAlphaIcon(_himl, hicon, GetStockBrush(WHITE_BRUSH), hdc);
01083     (void)TreeView_InsertItem(_tree_ctrl, &tvi);
01084 }
01085 
01086 LRESULT TrayNotifyDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
01087 {
01088     switch(nmsg) {
01089       case PM_TRANSLATE_MSG: {
01090         MSG* pmsg = (MSG*) lparam;
01091 
01092         if (TranslateAccelerator(_hwnd, _haccel, pmsg))
01093             return TRUE;
01094 
01095         return FALSE;}
01096 
01097       case WM_TIMER:
01098         Refresh();
01099         break;
01100 
01101       default:
01102         return super::WndProc(nmsg, wparam, lparam);
01103     }
01104 
01105     return 0;
01106 }
01107 
01108 int TrayNotifyDlg::Command(int id, int code)
01109 {
01110     if (code == BN_CLICKED) {
01111         switch(id) {
01112           case ID_REFRESH:
01113             Refresh();
01114             break;
01115 
01116           case IDC_NOTIFY_SHOW:
01117             SetIconMode(NIM_SHOW);
01118             break;
01119 
01120           case IDC_NOTIFY_HIDE:
01121             SetIconMode(NIM_HIDE);
01122             break;
01123 
01124           case IDC_NOTIFY_AUTOHIDE:
01125             SetIconMode(NIM_AUTO);
01126             break;
01127 
01128           case ID_SHOW_HIDDEN_ICONS:
01129             if (_pNotifyArea)
01130                 SendMessage(*_pNotifyArea, WM_COMMAND, MAKEWPARAM(id,code), 0);
01131             break;
01132 
01133           case IDOK:
01134             EndDialog(_hwnd, id);
01135             break;
01136 
01137           case IDCANCEL:
01138              // rollback changes
01139             if (_pNotifyArea) {
01140                  // restore original icon states and configuration data
01141                 _pNotifyArea->_cfg = _cfg_org;
01142                 _pNotifyArea->_show_hidden = _show_hidden_org;
01143 
01144                 for(IconStateMap::const_iterator it=_icon_states_org.begin(); it!=_icon_states_org.end(); ++it) {
01145                     NotifyInfo& info = _pNotifyArea->_icon_map[it->first];
01146 
01147                     info._mode = it->second.first;
01148                     info._dwState = it->second.second;
01149                 }
01150 
01151                 SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0);
01152             }
01153 
01154             EndDialog(_hwnd, id);
01155             break;
01156         }
01157 
01158         return 0;
01159     }
01160 
01161     return 1;
01162 }
01163 
01164 int TrayNotifyDlg::Notify(int id, NMHDR* pnmh)
01165 {
01166     switch(pnmh->code) {
01167       case TVN_SELCHANGED: {
01168         NMTREEVIEW* pnmtv = (NMTREEVIEW*)pnmh;
01169         int idx = pnmtv->itemNew.lParam;
01170 
01171         if (idx) {
01172             RefreshProperties(_info[idx]);
01173             _selectedItem = pnmtv->itemNew.hItem;
01174         } else {
01175             /*
01176             SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, NULL);
01177             SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, NULL);
01178             SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, NULL);
01179             */
01180             CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, 0);
01181         }
01182         break;}
01183     }
01184 
01185     return 0;
01186 }
01187 
01188 void TrayNotifyDlg::RefreshProperties(const NotifyIconDlgInfo& entry)
01189 {
01190     SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, entry._tipText);
01191     SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, entry._windowTitle);
01192     SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, entry._modulePath);
01193 
01194     CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, IDC_NOTIFY_SHOW+entry._mode);
01195 
01196     String change_str;
01197     if (entry._lastChange)
01198         change_str.printf(TEXT("before %d s"), (GetTickCount()-entry._lastChange+500)/1000);
01199     SetDlgItemText(_hwnd, IDC_LAST_CHANGE, change_str);
01200 
01201     HICON hicon = 0; //get_window_icon_big(entry._hWnd, false);
01202 
01203      // If we could not find an icon associated with the owner window, try to load one from the owning module.
01204     if (!hicon && !entry._modulePath.empty()) {
01205         hicon = ExtractIcon(g_Globals._hInstance, entry._modulePath, 0);
01206 
01207         if (!hicon) {
01208             SHFILEINFO sfi;
01209 
01210             if (SHGetFileInfo(entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_LARGEICON))
01211                 hicon = sfi.hIcon;
01212         }
01213     }
01214 
01215     if (hicon) {
01216         SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, (LPARAM)hicon, 0);
01217         DestroyIcon(hicon);
01218     } else
01219         SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, 0, 0);
01220 }
01221 
01222 void TrayNotifyDlg::SetIconMode(NOTIFYICONMODE mode)
01223 {
01224     int idx = TreeView_GetItemData(_tree_ctrl, _selectedItem);
01225 
01226     if (!idx)
01227         return;
01228 
01229     NotifyIconConfig& entry = _info[idx];
01230 
01231     if (entry._mode != mode) {
01232         entry._mode = mode;
01233 
01234          // trigger refresh in notify area and this dialog
01235         if (_pNotifyArea)
01236             SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0);
01237     }
01238 
01239     if (_pNotifyArea) {
01240         bool found = false;
01241 
01242         NotifyIconCfgList& cfg = _pNotifyArea->_cfg;
01243         for(NotifyIconCfgList::iterator it=cfg.begin(); it!=cfg.end(); ++it) {
01244             NotifyIconConfig& cfg_entry = *it;
01245 
01246             if (cfg_entry.match(entry)) {
01247                 cfg_entry._mode = mode;
01248                 ++found;
01249                 break;
01250             }
01251         }
01252 
01253         if (!found) {
01254              // insert new configuration entry
01255             NotifyIconConfig cfg_entry = entry;
01256 
01257             cfg_entry._mode = mode;
01258 
01259             _pNotifyArea->_cfg.push_back(cfg_entry);
01260         }
01261     }
01262 
01263     Refresh();
01264     ///@todo select treeview item at new position in tree view -> refresh HTREEITEM in _selectedItem
01265 }
01266 
01267 
01268 ClockWindow::ClockWindow(HWND hwnd)
01269  :  super(hwnd),
01270     _tooltip(hwnd)
01271 {
01272     *_time = TEXT('\0');
01273     FormatTime();
01274 
01275     _tooltip.add(_hwnd, _hwnd);
01276 }
01277 
01278 HWND ClockWindow::Create(HWND hwndParent)
01279 {
01280     static BtnWindowClass wcClock(CLASSNAME_CLOCKWINDOW, CS_DBLCLKS);
01281 
01282     ClientRect clnt(hwndParent);
01283 
01284     WindowCanvas canvas(hwndParent);
01285     FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
01286 
01287     RECT rect = {0, 0, 0, 0};
01288     TCHAR buffer[8];
01289 
01290     if (!GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, NULL, buffer, sizeof(buffer)/sizeof(TCHAR)))
01291         _tcscpy(buffer, TEXT("00:00"));
01292 
01293     DrawText(canvas, buffer, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
01294     int clockwindowWidth = rect.right-rect.left + 4;
01295 
01296     return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
01297                             wcClock, NULL, WS_CHILD|WS_VISIBLE,
01298                             clnt.right-(clockwindowWidth), 1, clockwindowWidth, clnt.bottom-2, hwndParent);
01299 }
01300 
01301 LRESULT ClockWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
01302 {
01303     switch(nmsg) {
01304       case WM_PAINT:
01305         Paint();
01306         break;
01307 
01308       case WM_LBUTTONDBLCLK:
01309         launch_cpanel(_hwnd, TEXT("timedate.cpl"));
01310         break;
01311 
01312       default:
01313         return super::WndProc(nmsg, wparam, lparam);
01314     }
01315 
01316     return 0;
01317 }
01318 
01319 int ClockWindow::Notify(int id, NMHDR* pnmh)
01320 {
01321     if (pnmh->code == TTN_GETDISPINFO) {
01322         LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
01323 
01324         SYSTEMTIME systime;
01325         TCHAR buffer[64];
01326 
01327         GetLocalTime(&systime);
01328 
01329         if (GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systime, NULL, buffer, 64))
01330             _tcscpy(pdi->szText, buffer);
01331         else
01332             pdi->szText[0] = '\0';
01333     }
01334 
01335     return 0;
01336 }
01337 
01338 void ClockWindow::TimerTick()
01339 {
01340     if (FormatTime())
01341         InvalidateRect(_hwnd, NULL, TRUE);  // refresh displayed time
01342 }
01343 
01344 bool ClockWindow::FormatTime()
01345 {
01346     TCHAR buffer[16];
01347 
01348     if (GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, NULL, buffer, sizeof(buffer)/sizeof(TCHAR)))
01349         if (_tcscmp(buffer, _time)) {
01350             _tcscpy(_time, buffer);
01351             return true;    // The text to display has changed.
01352         }
01353 
01354     return false;   // no change
01355 }
01356 
01357 void ClockWindow::Paint()
01358 {
01359     PaintCanvas canvas(_hwnd);
01360 
01361     FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
01362 
01363     BkMode bkmode(canvas, TRANSPARENT);
01364     FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
01365 
01366     DrawText(canvas, _time, -1, ClientRect(_hwnd), DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX);
01367 }

ROS Explorer Source Code Documentation
generated on 02.01.2013 by doxygen

ROS Explorer Homepage