mirror of
https://github.com/reactos/reactos.git
synced 2024-11-05 14:19:25 +00:00
f9b5ca3c79
CORE-9198 #resolve svn path=/trunk/; revision=66372
1354 lines
34 KiB
C++
1354 lines
34 KiB
C++
/*
|
|
* 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 position
|
|
NotifyIconSet::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);
|
|
}
|