Something went wrong. Try again.
Reactos
Something went wrong. Try again.
34 kB · 1354 lines
C++
at master
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355/* * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// // Explorer clone // // traynotify.cpp // // Martin Fuchs, 22.08.2003 //
#include <precomp.h>
#include "traynotify.h"
#include "../notifyhook/notifyhook.h"
NotifyHook::NotifyHook() : WM_GETMODULEPATH(InstallNotifyHook()){}
NotifyHook::~NotifyHook(){ DeinstallNotifyHook();}
void NotifyHook::GetModulePath(HWND hwnd, HWND hwndCallback){ PostMessage(hwnd, WM_GETMODULEPATH, (WPARAM)hwndCallback, 0);}
bool NotifyHook::ModulePathCopyData(LPARAM lparam, HWND* phwnd, String& path){ char buffer[MAX_PATH];
int l = GetWindowModulePathCopyData(lparam, phwnd, buffer, COUNTOF(buffer));
if (l) { path.assign(buffer, l); return true; } else return false;}
NotifyIconIndex::NotifyIconIndex(NOTIFYICONDATA* pnid){ _hWnd = pnid->hWnd; _uID = pnid->uID;
// special handling for windows task manager if ((int)_uID < 0) _uID = 0;}
NotifyIconIndex::NotifyIconIndex(){ _hWnd = 0; _uID = 0;}
NotifyInfo::NotifyInfo(){ _idx = -1; _hIcon = 0; _dwState = 0; _uCallbackMessage = 0; _version = 0;
_mode = NIM_AUTO; _lastChange = GetTickCount();}
// WCHAR versions von NOTIFYICONDATA#define NID_SIZE_W6 sizeof(NOTIFYICONDATAW) // _WIN32_IE = 0x600#define NID_SIZE_W5 (sizeof(NOTIFYICONDATAW)-sizeof(GUID)) // _WIN32_IE = 0x500#define NID_SIZE_W3 (sizeof(NOTIFYICONDATAW)-sizeof(GUID)-(128-64)*sizeof(WCHAR)) // _WIN32_IE < 0x500
// CHAR versions von NOTIFYICONDATA#define NID_SIZE_A6 sizeof(NOTIFYICONDATAA)#define NID_SIZE_A5 (sizeof(NOTIFYICONDATAA)-sizeof(GUID))#define NID_SIZE_A3 (sizeof(NOTIFYICONDATAA)-sizeof(GUID)-(128-64)*sizeof(CHAR))
bool NotifyInfo::modify(NOTIFYICONDATA* pnid){ bool changes = false;
if (_hWnd!=pnid->hWnd || _uID!=pnid->uID) { _hWnd = pnid->hWnd; _uID = pnid->uID;
changes = true; }
if (pnid->uFlags & NIF_MESSAGE) { if (_uCallbackMessage != pnid->uCallbackMessage) { _uCallbackMessage = pnid->uCallbackMessage; changes = true; } }
if (pnid->uFlags & NIF_ICON) { // Some applications destroy the icon immediatelly after completing the // NIM_ADD/MODIFY message, so we have to make a copy of it. if (_hIcon) DestroyIcon(_hIcon);
_hIcon = (HICON) CopyImage(pnid->hIcon, IMAGE_ICON, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0);
changes = true; ///@todo compare icon }
if (pnid->uFlags & NIF_STATE) { DWORD new_state = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask);
if (_dwState != new_state) { _dwState = new_state; changes = true; } }
// store tool tip text if (pnid->uFlags & NIF_TIP) { String new_text;
if (pnid->cbSize==NID_SIZE_W6 || pnid->cbSize==NID_SIZE_W5 || pnid->cbSize==NID_SIZE_W3) { // UNICODE version of NOTIFYICONDATA structure LPCWSTR txt = (LPCWSTR)pnid->szTip; int max_len = pnid->cbSize==NID_SIZE_W3? 64: 128;
// get tooltip string length int l = 0; for(; l<max_len; ++l) if (!txt[l]) break;
new_text.assign(txt, l);
if (new_text != _tipText) { _tipText = new_text; changes = true; } } else if (pnid->cbSize==NID_SIZE_A6 || pnid->cbSize==NID_SIZE_A5 || pnid->cbSize==NID_SIZE_A3) { LPCSTR txt = (LPCSTR)pnid->szTip; int max_len = pnid->cbSize==NID_SIZE_A3? 64: 128;
int l = 0; for(int l=0; l<max_len; ++l) if (!txt[l]) break;
new_text.assign(txt, l);
if (new_text != _tipText) { _tipText = new_text; changes = true; } } }
TCHAR title[MAX_PATH];
DWORD pid; GetWindowThreadProcessId(_hWnd, &pid);
// avoid to send WM_GETTEXT messages to the own process if (pid != GetCurrentProcessId()) if (GetWindowText(_hWnd, title, COUNTOF(title))) { if (_windowTitle != title) { _windowTitle = title; changes = true; } }
if (changes) { create_name(); _lastChange = GetTickCount(); }
return changes;}
NotifyArea::NotifyArea(HWND hwnd) : super(hwnd), _tooltip(hwnd){ _next_idx = 0; _clock_width = 0; _last_icon_count = 0; _show_hidden = false; _hide_inactive = true; _show_button = true;}
NotifyArea::~NotifyArea(){ KillTimer(_hwnd, 0);
write_config();}
static bool get_hide_clock_from_registry(){ HKEY hkeyStuckRects = 0; DWORD buffer[10]; DWORD len = sizeof(buffer);
bool hide_clock = false;
// check if the clock should be hidden if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"), &hkeyStuckRects) && !RegQueryValueEx(hkeyStuckRects, TEXT("Settings"), 0, NULL, (LPBYTE)buffer, &len) && len==sizeof(buffer) && buffer[0]==sizeof(buffer)) hide_clock = buffer[2] & 0x08? true: false;
if (hkeyStuckRects) RegCloseKey(hkeyStuckRects);
return hide_clock;}
void NotifyArea::read_config(){ bool clock_visible = true;
// read notification icon settings from XML configuration XMLPos cfg_pos = g_Globals.get_cfg();
if (!g_Globals._SHRestricted || !SHRestricted(REST_HIDECLOCK)) { if (cfg_pos.go_down("desktopbar")) { clock_visible = XMLBoolRef(XMLPos(cfg_pos,"options"), "show-clock", !get_hide_clock_from_registry()); cfg_pos.back(); } }
if (cfg_pos.go_down("notify-icons")) { XMLPos options(cfg_pos, "options");
_hide_inactive = XMLBool(options, "hide-inactive", true); ///@todo read default setting from registry _show_hidden = XMLBool(options, "show-hidden", false); ///@todo read default setting from registry _show_button = XMLBool(options, "show-button", true);
XMLChildrenFilter icons(cfg_pos, "icon");
for(XMLChildrenFilter::iterator it=icons.begin(); it!=icons.end(); ++it) { const XMLNode& node = **it;
NotifyIconConfig cfg;
cfg._name = node.get("name").c_str(); cfg._tipText = node.get("text").c_str(); cfg._windowTitle = node.get("window").c_str(); cfg._modulePath = node.get("module").c_str(); const string& mode = node.get("show");
if (mode == "show") cfg._mode = NIM_SHOW; else if (mode == "hide") cfg._mode = NIM_HIDE; else //if (mode == "auto") cfg._mode = NIM_HIDE;
_cfg.push_back(cfg); }
cfg_pos.back(); }
show_clock(clock_visible);}
void NotifyArea::write_config(){ // write notification icon settings to XML configuration file XMLPos cfg_pos = g_Globals.get_cfg();
cfg_pos.smart_create("desktopbar"); XMLBoolRef boolRef(XMLPos(cfg_pos,"options"), "show-clock"); boolRef = _hwndClock!=0; cfg_pos.back();
cfg_pos.smart_create("notify-icons");
XMLPos options(cfg_pos, "options"); XMLBoolRef(options, "hide-inactive") = _hide_inactive; XMLBoolRef(options, "show-hidden") = _show_hidden; XMLBoolRef(options, "show-button") = _show_button;
for(NotifyIconCfgList::iterator it=_cfg.begin(); it!=_cfg.end(); ++it) { NotifyIconConfig& cfg = *it;
// search for the corresponding node using the original name cfg_pos.smart_create("icon", "name", cfg._name);
// refresh unique name cfg.create_name();
cfg_pos["name"] = cfg._name.c_str(); cfg_pos["text"] = cfg._tipText.c_str(); cfg_pos["window"] = cfg._windowTitle.c_str(); cfg_pos["module"] = cfg._modulePath.c_str(); cfg_pos["show"] = string_from_mode(cfg._mode).c_str();
cfg_pos.back(); }
cfg_pos.back(); // smart_create}
void NotifyArea::show_clock(bool flag){ bool vis = _hwndClock!=0;
if (vis != flag) { if (flag) { // create clock window _hwndClock = ClockWindow::Create(_hwnd);
if (_hwndClock) { ClientRect clock_size(_hwndClock); _clock_width = clock_size.right; } } else { DestroyWindow(_hwndClock); _hwndClock = 0; _clock_width = 0; }
SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0); }}
LRESULT NotifyArea::Init(LPCREATESTRUCT pcs){ if (super::Init(pcs)) return 1;
read_config();
SetTimer(_hwnd, 0, 1000, NULL);
return 0;}
HWND NotifyArea::Create(HWND hwndParent){ static BtnWindowClass wcTrayNotify(CLASSNAME_TRAYNOTIFY, CS_DBLCLKS);
ClientRect clnt(hwndParent);
return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE, wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN, clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);}
LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam){ switch(nmsg) { case WM_PAINT: Paint(); break;
case WM_TIMER: { Refresh();
ClockWindow* clock_window = GET_WINDOW(ClockWindow, _hwndClock);
if (clock_window) clock_window->TimerTick(); break;}
case PM_REFRESH: Refresh(true); break;
case WM_SIZE: { int cx = LOWORD(lparam); SetWindowPos(_hwndClock, 0, cx-_clock_width, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); break;}
case PM_GET_WIDTH: { int w = _sorted_icons.size()*NOTIFYICON_DIST + NOTIFYAREA_SPACE + _clock_width; if (_show_button) w += NOTIFYICON_DIST; return w;}
case PM_REFRESH_CONFIG: read_config(); break;
case WM_CONTEXTMENU: { Point pt(lparam); POINTS p; p.x = (SHORT) pt.x; p.y = (SHORT) pt.y; ScreenToClient(_hwnd, &pt);
if (IconHitTest(pt) == _sorted_icons.end()) { // display menu only when no icon clicked PopupMenu menu(IDM_NOTIFYAREA); SetMenuDefaultItem(menu, 0, MF_BYPOSITION); CheckMenuItem(menu, ID_SHOW_HIDDEN_ICONS, MF_BYCOMMAND|(_show_hidden?MF_CHECKED:MF_UNCHECKED)); CheckMenuItem(menu, ID_SHOW_ICON_BUTTON, MF_BYCOMMAND|(_show_button?MF_CHECKED:MF_UNCHECKED)); menu.TrackPopupMenu(_hwnd, p); } break;}
case WM_COPYDATA: { // receive NotifyHook answers String path; HWND hwnd;
if (_hook.ModulePathCopyData(lparam, &hwnd, path)) _window_modules[hwnd] = path; break;}
default: if (nmsg>=WM_MOUSEFIRST && nmsg<=WM_MOUSELAST) { // close startup menu and other popup menus // This functionality is missing in MS Windows. if (nmsg==WM_LBUTTONDOWN || nmsg==WM_MBUTTONDOWN || nmsg==WM_RBUTTONDOWN#ifdef WM_XBUTTONDOWN || nmsg==WM_XBUTTONDOWN#endif )
CancelModes();
Point pt(lparam); NotifyIconSet::const_iterator found = IconHitTest(pt);
if (found != _sorted_icons.end()) { const NotifyInfo& entry = const_cast<NotifyInfo&>(*found); // Why does GCC 3.3 need this additional const_cast ?!
// set activation time stamp if (nmsg == WM_LBUTTONDOWN || // Some programs need PostMessage() instead of SendMessage(). nmsg == WM_MBUTTONDOWN || // So call SendMessage() only for BUTTONUP and BLCLK messages#ifdef WM_XBUTTONDOWN nmsg == WM_XBUTTONDOWN ||#endif nmsg == WM_RBUTTONDOWN) { _icon_map[entry]._lastChange = GetTickCount(); }
// Notify the message if the owner is still alive if (IsWindow(entry._hWnd)) { if (nmsg == WM_MOUSEMOVE || // avoid to call blocking SendMessage() for merely moving the mouse over icons nmsg == WM_LBUTTONDOWN || // Some programs need PostMessage() instead of SendMessage(). nmsg == WM_MBUTTONDOWN || // So call SendMessage() only for BUTTONUP and BLCLK messages#ifdef WM_XBUTTONDOWN nmsg == WM_XBUTTONDOWN ||#endif nmsg == WM_RBUTTONDOWN) PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); else { // allow SetForegroundWindow() in client process DWORD pid;
if (GetWindowThreadProcessId(entry._hWnd, &pid)) { // bind dynamically to AllowSetForegroundWindow() to be compatible to WIN98 static DynamicFct<BOOL(WINAPI*)(DWORD)> AllowSetForegroundWindow(TEXT("USER32"), "AllowSetForegroundWindow");
if (AllowSetForegroundWindow) (*AllowSetForegroundWindow)(pid); }
// use PostMessage() for notifcation icons of Shell Service Objects in the own process if (pid == GetCurrentProcessId()) PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); else SendMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); } } else if (_icon_map.erase(entry)) // delete icons without valid owner window UpdateIcons(); } else // handle clicks on notification area button "show hidden icons" if (_show_button) if (nmsg == WM_LBUTTONDOWN) if (pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE && pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE) PostMessage(_hwnd, WM_COMMAND, MAKEWPARAM(ID_SHOW_HIDDEN_ICONS,0), 0); }
return super::WndProc(nmsg, wparam, lparam); }
return 0;}
int NotifyArea::Command(int id, int code){ switch(id) { case ID_SHOW_HIDDEN_ICONS: _show_hidden = !_show_hidden; UpdateIcons(); break;
case ID_SHOW_ICON_BUTTON: _show_button = !_show_button; UpdateIcons(); break;
case ID_CONFIG_NOTIFYAREA: Dialog::DoModal(IDD_NOTIFYAREA, WINDOW_CREATOR(TrayNotifyDlg), GetParent(_hwnd)); break;
case ID_CONFIG_TIME: launch_cpanel(_hwnd, TEXT("timedate.cpl")); break;
default: SendParent(WM_COMMAND, MAKELONG(id,code), 0); }
return 0;}
int NotifyArea::Notify(int id, NMHDR* pnmh){ if (pnmh->code == TTN_GETDISPINFO) { LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
Point pt(GetMessagePos()); ScreenToClient(_hwnd, &pt);
if (_show_button && pt.x>=NOTIFYICON_X && pt.x<NOTIFYICON_X+NOTIFYICON_SIZE && pt.y>=NOTIFYICON_Y && pt.y<NOTIFYICON_Y+NOTIFYICON_SIZE) { static ResString sShowIcons(IDS_SHOW_HIDDEN_ICONS); static ResString sHideIcons(IDS_HIDE_ICONS);
pdi->lpszText = (_show_hidden? sHideIcons: sShowIcons).str(); } else { NotifyIconSet::iterator found = IconHitTest(pt);
if (found != _sorted_icons.end()) { NotifyInfo& entry = const_cast<NotifyInfo&>(*found); // Why does GCC 3.3 need this additional const_cast ?!
// enable multiline tooltips (break at CR/LF and for very long one-line strings) SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400);
pdi->lpszText = entry._tipText.str(); } } }
return 0;}
void NotifyArea::CancelModes(){ PostMessage(HWND_BROADCAST, WM_CANCELMODE, 0, 0);
for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) PostMessage(it->_hWnd, WM_CANCELMODE, 0, 0);}
LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pnid){ switch(notify_code) { case NIM_ADD: case NIM_MODIFY: if ((int)pnid->uID >= 0) { ///@todo This is a fix for Windows Task Manager. NotifyInfo& entry = _icon_map[pnid];
// a new entry? if (entry._idx == -1) entry._idx = ++_next_idx; /* equivalent code using iterator::find(); NotifyIconMap::iterator found = _icon_map.find(pnid); NotifyInfo* pentry; // a new entry? if (found == _icon_map.end()) { pentry = &_icon_map[pnid]; pentry->_idx = ++_next_idx; } else { pentry = &found->second; } NotifyInfo& entry = *pentry; */ bool changes = entry.modify(pnid);
if (DetermineHideState(entry) && entry._mode==NIM_HIDE) { entry._dwState |= NIS_HIDDEN; changes = true; }
if (changes) UpdateIcons(); ///@todo call only if really changes occurred
return TRUE; } break;
case NIM_DELETE: { NotifyIconMap::iterator found = _icon_map.find(pnid);
if (found != _icon_map.end()) { if (found->second._hIcon) DestroyIcon(found->second._hIcon); _icon_map.erase(found); UpdateIcons(); return TRUE; } break;}
case NIM_SETFOCUS: SetForegroundWindow(_hwnd); return TRUE;
case NIM_SETVERSION: NotifyIconMap::iterator found = _icon_map.find(pnid);
if (found != _icon_map.end()) { found->second._version = pnid->UNION_MEMBER(uVersion); return TRUE; } else return FALSE; }
return FALSE;}
void NotifyArea::UpdateIcons(){ _sorted_icons.clear();
// sort icon infos by display index for(NotifyIconMap::const_iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) { const NotifyInfo& entry = it->second;
if (_show_hidden || !(entry._dwState & NIS_HIDDEN)) _sorted_icons.insert(entry); }
// sync tooltip areas to current icon number if (_sorted_icons.size() != _last_icon_count) { RECT rect = {NOTIFYICON_X, NOTIFYICON_Y, NOTIFYICON_X+NOTIFYICON_SIZE, NOTIFYICON_Y+NOTIFYICON_SIZE};
size_t tt_idx = 0;
if (_show_button) { _tooltip.add(_hwnd, tt_idx++, rect);
rect.left += NOTIFYICON_DIST; rect.right += NOTIFYICON_DIST; }
size_t icon_cnt = _sorted_icons.size(); while(tt_idx < icon_cnt) { _tooltip.add(_hwnd, tt_idx++, rect);
rect.left += NOTIFYICON_DIST; rect.right += NOTIFYICON_DIST; }
while(tt_idx < _last_icon_count) _tooltip.remove(_hwnd, tt_idx++);
_last_icon_count = _sorted_icons.size(); }
SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
InvalidateRect(_hwnd, NULL, FALSE); // refresh icon display UpdateWindow(_hwnd);}
#ifndef _NO_ALPHABLEND#ifdef _MSC_VER#pragma comment(lib, "msimg32") // for AlphaBlend()#endif#endif
void NotifyArea::Paint(){ BufferedPaintCanvas canvas(_hwnd);
// first fill with the background color FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
// draw icons int x = NOTIFYICON_X; int y = NOTIFYICON_Y;
if (_show_button) { static SmallIcon leftArrowIcon(IDI_NOTIFY_L); static SmallIcon rightArrowIcon(IDI_NOTIFY_R);
DrawIconEx(canvas, x, y, _show_hidden?rightArrowIcon:leftArrowIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL); x += NOTIFYICON_DIST; }
#ifndef _NO_ALPHABLEND MemCanvas mem_dc; SelectedBitmap bmp(mem_dc, CreateCompatibleBitmap(canvas, NOTIFYICON_SIZE, NOTIFYICON_SIZE)); RECT rect = {0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE}; BLENDFUNCTION blend = {AC_SRC_OVER, 0, 128, 0}; // 50 % visible#endif
for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) {#ifndef _NO_ALPHABLEND if (it->_dwState & NIS_HIDDEN) { FillRect(mem_dc, &rect, GetSysColorBrush(COLOR_BTNFACE)); DrawIconEx(mem_dc, 0, 0, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL); AlphaBlend(canvas, x, y, NOTIFYICON_SIZE, NOTIFYICON_SIZE, mem_dc, 0, 0, NOTIFYICON_SIZE, NOTIFYICON_SIZE, blend); } else#endif DrawIconEx(canvas, x, y, it->_hIcon, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0, 0, DI_NORMAL);
x += NOTIFYICON_DIST; }}
void NotifyArea::Refresh(bool update){ // Look for task icons without valid owner window. // This is an extended feature missing in MS Windows. for(NotifyIconSet::const_iterator it=_sorted_icons.begin(); it!=_sorted_icons.end(); ++it) { const NotifyInfo& entry = *it;
if (!IsWindow(entry._hWnd)) if (_icon_map.erase(entry)) // delete icons without valid owner window ++update; }
DWORD now = GetTickCount();
// handle icon hiding for(NotifyIconMap::iterator it=_icon_map.begin(); it!=_icon_map.end(); ++it) { NotifyInfo& entry = it->second;
DetermineHideState(entry);
switch(entry._mode) { case NIM_HIDE: if (!(entry._dwState & NIS_HIDDEN)) { entry._dwState |= NIS_HIDDEN; ++update; } break;
case NIM_SHOW: if (entry._dwState&NIS_HIDDEN) { entry._dwState &= ~NIS_HIDDEN; ++update; } break;
case NIM_AUTO: // automatically hide icons after long periods of inactivity if (_hide_inactive) if (!(entry._dwState & NIS_HIDDEN)) if (now-entry._lastChange > ICON_AUTOHIDE_SECONDS*1000) { entry._dwState |= NIS_HIDDEN; ++update; } break; } }
if (update) UpdateIcons();}
/// search for a icon at a given client coordinate positionNotifyIconSet::iterator NotifyArea::IconHitTest(const POINT& pos){ if (pos.y<NOTIFYICON_Y || pos.y>=NOTIFYICON_Y+NOTIFYICON_SIZE) return _sorted_icons.end();
NotifyIconSet::iterator it = _sorted_icons.begin();
int x = NOTIFYICON_X;
if (_show_button) x += NOTIFYICON_DIST;
for(; it!=_sorted_icons.end(); ++it) { //NotifyInfo& entry = const_cast<NotifyInfo&>(*it); // Why does GCC 3.3 need this additional const_cast ?!
if (pos.x>=x && pos.x<x+NOTIFYICON_SIZE) break;
x += NOTIFYICON_DIST; }
return it;}
void NotifyIconConfig::create_name(){ _name = FmtString(TEXT("'%s' - '%s' - '%s'"), _tipText.c_str(), _windowTitle.c_str(), _modulePath.c_str());}
bool NotifyIconConfig::match(const NotifyIconConfig& props) const{ if (!_tipText.empty() && !props._tipText.empty()) if (props._tipText == _tipText) return true;
if (!_windowTitle.empty() && !props._windowTitle.empty()) if (_tcsstr(props._windowTitle, _windowTitle)) return true;
if (!_modulePath.empty() && !props._modulePath.empty()) if (!_tcsicmp(props._modulePath, _modulePath)) return true;
return false;}
bool NotifyArea::DetermineHideState(NotifyInfo& entry){ if (entry._modulePath.empty()) { const String& modulePath = _window_modules[entry._hWnd];
// request module path for new windows (We will get an asynchronous answer by a WM_COPYDATA message.) if (!modulePath.empty()) entry._modulePath = modulePath; else _hook.GetModulePath(entry._hWnd, _hwnd); }
for(NotifyIconCfgList::const_iterator it=_cfg.begin(); it!=_cfg.end(); ++it) { const NotifyIconConfig& cfg = *it;
if (cfg.match(entry)) { entry._mode = cfg._mode; return true; } }
return false;}
String string_from_mode(NOTIFYICONMODE mode){ switch(mode) { case NIM_SHOW: return ResString(IDS_NOTIFY_SHOW);
case NIM_HIDE: return ResString(IDS_NOTIFY_HIDE);
default: //case NIM_AUTO return ResString(IDS_NOTIFY_AUTOHIDE); }}
TrayNotifyDlg::TrayNotifyDlg(HWND hwnd) : super(hwnd), _tree_ctrl(GetDlgItem(hwnd, IDC_NOTIFY_ICONS)), _himl(ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR24, 3, 0)), _pNotifyArea(static_cast<NotifyArea*>(Window::get_window((HWND)SendMessage(g_Globals._hwndDesktopBar, PM_GET_NOTIFYAREA, 0, 0)))){ _selectedItem = 0;
if (_pNotifyArea) { // save original icon states and configuration data for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it) _icon_states_org[it->first] = IconStatePair(it->second._mode, it->second._dwState);
_cfg_org = _pNotifyArea->_cfg; _show_hidden_org = _pNotifyArea->_show_hidden; }
SetWindowIcon(hwnd, IDI_REACTOS);
_haccel = LoadAccelerators(g_Globals._hInstance, MAKEINTRESOURCE(IDA_TRAYNOTIFY));
{ WindowCanvas canvas(_hwnd); HBRUSH hbkgnd = GetStockBrush(WHITE_BRUSH);
ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT), hbkgnd, canvas); ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_TRANS), hbkgnd, canvas); ImageList_AddAlphaIcon(_himl, SmallIcon(IDI_DOT_RED), hbkgnd, canvas); }
(void)TreeView_SetImageList(_tree_ctrl, _himl, TVSIL_NORMAL);
_resize_mgr.Add(IDC_NOTIFY_ICONS, RESIZE); _resize_mgr.Add(IDC_LABEL1, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_TOOLTIP, RESIZE_X|MOVE_Y); _resize_mgr.Add(IDC_LABEL2, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_TITLE, RESIZE_X|MOVE_Y); _resize_mgr.Add(IDC_LABEL3, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_MODULE, RESIZE_X|MOVE_Y);
_resize_mgr.Add(IDC_LABEL4, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_SHOW, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_HIDE, MOVE_Y); _resize_mgr.Add(IDC_NOTIFY_AUTOHIDE,MOVE_Y);
_resize_mgr.Add(IDC_PICTURE, MOVE); _resize_mgr.Add(ID_SHOW_HIDDEN_ICONS,MOVE_Y);
_resize_mgr.Add(IDC_LABEL6, MOVE_Y); _resize_mgr.Add(IDC_LAST_CHANGE, MOVE_Y);
_resize_mgr.Add(IDOK, MOVE); _resize_mgr.Add(IDCANCEL, MOVE);
_resize_mgr.Resize(+150, +200);
Refresh();
SetTimer(_hwnd, 0, 3000, NULL); register_pretranslate(hwnd);}
TrayNotifyDlg::~TrayNotifyDlg(){ KillTimer(_hwnd, 0); unregister_pretranslate(_hwnd); ImageList_Destroy(_himl);}
void TrayNotifyDlg::Refresh(){ ///@todo refresh incrementally
HiddenWindow hide(_tree_ctrl);
TreeView_DeleteAllItems(_tree_ctrl);
TV_INSERTSTRUCT tvi;
tvi.hParent = 0; tvi.hInsertAfter = TVI_LAST;
TV_ITEM& tv = tvi.item; tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
ResString str_cur(IDS_ITEMS_CUR); tv.pszText = str_cur.str(); tv.iSelectedImage = tv.iImage = 0; // IDI_DOT _hitemCurrent = TreeView_InsertItem(_tree_ctrl, &tvi);
ResString str_conf(IDS_ITEMS_CONFIGURED); tv.pszText = str_conf.str(); tv.iSelectedImage = tv.iImage = 2; // IDI_DOT_RED _hitemConfig = TreeView_InsertItem(_tree_ctrl, &tvi);
tvi.hParent = _hitemCurrent;
ResString str_visible(IDS_ITEMS_VISIBLE); tv.pszText = str_visible.str(); tv.iSelectedImage = tv.iImage = 0; // IDI_DOT _hitemCurrent_visible = TreeView_InsertItem(_tree_ctrl, &tvi);
ResString str_hidden(IDS_ITEMS_HIDDEN); tv.pszText = str_hidden.str(); tv.iSelectedImage = tv.iImage = 1; // IDI_DOT_TRANS _hitemCurrent_hidden = TreeView_InsertItem(_tree_ctrl, &tvi);
if (_pNotifyArea) { _info.clear();
tv.mask |= TVIF_PARAM;
WindowCanvas canvas(_hwnd);
// insert current (visible and hidden) items for(NotifyIconMap::const_iterator it=_pNotifyArea->_icon_map.begin(); it!=_pNotifyArea->_icon_map.end(); ++it) { const NotifyInfo& entry = it->second;
InsertItem(entry._dwState&NIS_HIDDEN? _hitemCurrent_hidden: _hitemCurrent_visible, TVI_LAST, entry, canvas); }
// insert configured items in tree view const NotifyIconCfgList& cfg = _pNotifyArea->_cfg; for(NotifyIconCfgList::const_iterator it=cfg.begin(); it!=cfg.end(); ++it) { const NotifyIconConfig& cfg_entry = *it;
HICON hicon = 0;
if (!cfg_entry._modulePath.empty()) { if ((int)ExtractIconEx(cfg_entry._modulePath, 0, NULL, &hicon, 1) <= 0) hicon = 0;
if (!hicon) { SHFILEINFO sfi;
if (SHGetFileInfo(cfg_entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON)) hicon = sfi.hIcon; } }
InsertItem(_hitemConfig, TVI_SORT, cfg_entry, canvas, hicon, cfg_entry._mode);
if (hicon) DestroyIcon(hicon); }
CheckDlgButton(_hwnd, ID_SHOW_HIDDEN_ICONS, _pNotifyArea->_show_hidden? BST_CHECKED: BST_UNCHECKED); }
TreeView_Expand(_tree_ctrl, _hitemCurrent_visible, TVE_EXPAND); TreeView_Expand(_tree_ctrl, _hitemCurrent_hidden, TVE_EXPAND); TreeView_Expand(_tree_ctrl, _hitemCurrent, TVE_EXPAND); TreeView_Expand(_tree_ctrl, _hitemConfig, TVE_EXPAND);
TreeView_EnsureVisible(_tree_ctrl, _hitemCurrent_visible);}
void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyInfo& entry, HDC hdc){ InsertItem(hparent, after, entry, hdc, entry._hIcon, entry._mode);}
void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyIconDlgInfo& entry, HDC hdc, HICON hicon, NOTIFYICONMODE mode){ int idx = _info.size() + 1; _info[idx] = entry;
String mode_str = string_from_mode(mode);
switch(mode) { case NIM_SHOW: mode_str = ResString(IDS_NOTIFY_SHOW); break; case NIM_HIDE: mode_str = ResString(IDS_NOTIFY_HIDE); break; case NIM_AUTO: mode_str = ResString(IDS_NOTIFY_AUTOHIDE); }
FmtString txt(TEXT("%s - %s [%s]"), entry._tipText.c_str(), entry._windowTitle.c_str(), mode_str.c_str());
TV_INSERTSTRUCT tvi;
tvi.hParent = hparent; tvi.hInsertAfter = after;
TV_ITEM& tv = tvi.item; tv.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
tv.lParam = (LPARAM)idx; tv.pszText = txt.str(); tv.iSelectedImage = tv.iImage = ImageList_AddAlphaIcon(_himl, hicon, GetStockBrush(WHITE_BRUSH), hdc); (void)TreeView_InsertItem(_tree_ctrl, &tvi);}
LRESULT TrayNotifyDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam){ switch(nmsg) { case PM_TRANSLATE_MSG: { MSG* pmsg = (MSG*) lparam;
if (TranslateAccelerator(_hwnd, _haccel, pmsg)) return TRUE;
return FALSE;}
case WM_TIMER: Refresh(); break;
default: return super::WndProc(nmsg, wparam, lparam); }
return 0;}
int TrayNotifyDlg::Command(int id, int code){ if (code == BN_CLICKED) { switch(id) { case ID_REFRESH: Refresh(); break;
case IDC_NOTIFY_SHOW: SetIconMode(NIM_SHOW); break;
case IDC_NOTIFY_HIDE: SetIconMode(NIM_HIDE); break;
case IDC_NOTIFY_AUTOHIDE: SetIconMode(NIM_AUTO); break;
case ID_SHOW_HIDDEN_ICONS: if (_pNotifyArea) SendMessage(*_pNotifyArea, WM_COMMAND, MAKEWPARAM(id,code), 0); break;
case IDOK: EndDialog(_hwnd, id); break;
case IDCANCEL: // rollback changes if (_pNotifyArea) { // restore original icon states and configuration data _pNotifyArea->_cfg = _cfg_org; _pNotifyArea->_show_hidden = _show_hidden_org;
for(IconStateMap::const_iterator it=_icon_states_org.begin(); it!=_icon_states_org.end(); ++it) { NotifyInfo& info = _pNotifyArea->_icon_map[it->first];
info._mode = it->second.first; info._dwState = it->second.second; }
SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0); }
EndDialog(_hwnd, id); break; }
return 0; }
return 1;}
int TrayNotifyDlg::Notify(int id, NMHDR* pnmh){ switch(pnmh->code) { case TVN_SELCHANGED: { NMTREEVIEW* pnmtv = (NMTREEVIEW*)pnmh; int idx = pnmtv->itemNew.lParam;
if (idx) { RefreshProperties(_info[idx]); _selectedItem = pnmtv->itemNew.hItem; } else { /* SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, NULL); SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, NULL); SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, NULL); */ CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, 0); } break;} }
return 0;}
void TrayNotifyDlg::RefreshProperties(const NotifyIconDlgInfo& entry){ SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, entry._tipText); SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, entry._windowTitle); SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, entry._modulePath);
CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, IDC_NOTIFY_SHOW+entry._mode);
String change_str; if (entry._lastChange) change_str.printf(TEXT("before %d s"), (GetTickCount()-entry._lastChange+500)/1000); SetDlgItemText(_hwnd, IDC_LAST_CHANGE, change_str);
HICON hicon = 0; //get_window_icon_big(entry._hWnd, false);
// If we could not find an icon associated with the owner window, try to load one from the owning module. if (!hicon && !entry._modulePath.empty()) { hicon = ExtractIcon(g_Globals._hInstance, entry._modulePath, 0);
if (!hicon) { SHFILEINFO sfi;
if (SHGetFileInfo(entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_LARGEICON)) hicon = sfi.hIcon; } }
if (hicon) { SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, (LPARAM)hicon, 0); DestroyIcon(hicon); } else SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, 0, 0);}
void TrayNotifyDlg::SetIconMode(NOTIFYICONMODE mode){ int idx = TreeView_GetItemData(_tree_ctrl, _selectedItem);
if (!idx) return;
NotifyIconConfig& entry = _info[idx];
if (entry._mode != mode) { entry._mode = mode;
// trigger refresh in notify area and this dialog if (_pNotifyArea) SendMessage(*_pNotifyArea, PM_REFRESH, 0, 0); }
if (_pNotifyArea) { bool found = false;
NotifyIconCfgList& cfg = _pNotifyArea->_cfg; for(NotifyIconCfgList::iterator it=cfg.begin(); it!=cfg.end(); ++it) { NotifyIconConfig& cfg_entry = *it;
if (cfg_entry.match(entry)) { cfg_entry._mode = mode; ++found; break; } }
if (!found) { // insert new configuration entry NotifyIconConfig cfg_entry = entry;
cfg_entry._mode = mode;
_pNotifyArea->_cfg.push_back(cfg_entry); } }
Refresh(); ///@todo select treeview item at new position in tree view -> refresh HTREEITEM in _selectedItem}
ClockWindow::ClockWindow(HWND hwnd) : super(hwnd), _tooltip(hwnd){ *_time = TEXT('\0'); FormatTime();
_tooltip.add(_hwnd, _hwnd);}
HWND ClockWindow::Create(HWND hwndParent){ static BtnWindowClass wcClock(CLASSNAME_CLOCKWINDOW, CS_DBLCLKS);
ClientRect clnt(hwndParent);
WindowCanvas canvas(hwndParent); FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
RECT rect = {0, 0, 0, 0}; TCHAR buffer[16]; // Arbitrary high time so that the created clock window is big enough SYSTEMTIME st = { 1601, 1, 0, 1, 23, 59, 59, 999 };
if (!GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buffer, sizeof(buffer)/sizeof(TCHAR))) _tcscpy(buffer, TEXT("00:00"));
// Calculate the rectangle needed to draw the time (without actually drawing it) DrawText(canvas, buffer, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); int clockwindowWidth = rect.right-rect.left + 4;
return Window::Create(WINDOW_CREATOR(ClockWindow), 0, wcClock, NULL, WS_CHILD|WS_VISIBLE, clnt.right-(clockwindowWidth), 1, clockwindowWidth, clnt.bottom-2, hwndParent);}
LRESULT ClockWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam){ switch(nmsg) { case WM_PAINT: Paint(); break;
case WM_LBUTTONDBLCLK: launch_cpanel(_hwnd, TEXT("timedate.cpl")); break;
default: return super::WndProc(nmsg, wparam, lparam); }
return 0;}
int ClockWindow::Notify(int id, NMHDR* pnmh){ if (pnmh->code == TTN_GETDISPINFO) { LPNMTTDISPINFO pdi = (LPNMTTDISPINFO)pnmh;
SYSTEMTIME systime; TCHAR buffer[64];
GetLocalTime(&systime);
if (GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systime, NULL, buffer, 64)) _tcscpy(pdi->szText, buffer); else pdi->szText[0] = '\0'; }
return 0;}
void ClockWindow::TimerTick(){ if (FormatTime()) InvalidateRect(_hwnd, NULL, TRUE); // refresh displayed time}
bool ClockWindow::FormatTime(){ TCHAR buffer[16];
if (GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, NULL, buffer, sizeof(buffer)/sizeof(TCHAR))) if (_tcscmp(buffer, _time)) { _tcscpy(_time, buffer); return true; // The text to display has changed. }
return false; // no change}
void ClockWindow::Paint(){ PaintCanvas canvas(_hwnd);
FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE));
BkMode bkmode(canvas, TRANSPARENT); FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
DrawText(canvas, _time, -1, ClientRect(_hwnd), DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX);}