Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631/* * ReactOS Explorer * * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org> * Copyright 2018 Ged Murphy <gedmurphy@reactos.org> * * 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 */
#include "precomp.h"
#define BALLOON_MAXWIDTH 340
struct InternalIconData : NOTIFYICONDATA{ // Must keep a separate copy since the original is unioned with uTimeout. UINT uVersionCopy;};
struct IconWatcherData{ HANDLE hProcess; DWORD ProcessId; NOTIFYICONDATA IconData;
IconWatcherData(CONST NOTIFYICONDATA *iconData) : hProcess(NULL), ProcessId(0) { IconData.cbSize = sizeof(NOTIFYICONDATA); IconData.hWnd = iconData->hWnd; IconData.uID = iconData->uID; IconData.guidItem = iconData->guidItem; }
~IconWatcherData() { if (hProcess) { CloseHandle(hProcess); } }};
class CIconWatcher{ CAtlList<IconWatcherData *> m_WatcherList; CRITICAL_SECTION m_ListLock; HANDLE m_hWatcherThread; HANDLE m_WakeUpEvent; HWND m_hwndSysTray; bool m_Loop;
public: CIconWatcher();
virtual ~CIconWatcher();
bool Initialize(_In_ HWND hWndParent); void Uninitialize();
bool AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData); bool RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData);
IconWatcherData* GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove);
private:
static UINT WINAPI WatcherThread(_In_opt_ LPVOID lpParam);};
class CNotifyToolbar;
class CBalloonQueue{public: static const int TimerInterval = 2000; static const int BalloonsTimerId = 1; static const int MinTimeout = 10000; static const int MaxTimeout = 30000; static const int CooldownBetweenBalloons = 2000;
private: struct Info { InternalIconData * pSource; WCHAR szInfo[256]; WCHAR szInfoTitle[64]; WPARAM uIcon; UINT uTimeout;
Info(InternalIconData * source) { pSource = source; StringCchCopy(szInfo, _countof(szInfo), source->szInfo); StringCchCopy(szInfoTitle, _countof(szInfoTitle), source->szInfoTitle); uIcon = source->dwInfoFlags & NIIF_ICON_MASK; if (source->dwInfoFlags == NIIF_USER) uIcon = reinterpret_cast<WPARAM>(source->hIcon); uTimeout = source->uTimeout; } };
HWND m_hwndParent;
CTooltips * m_tooltips;
CAtlList<Info> m_queue;
CNotifyToolbar * m_toolbar;
InternalIconData * m_current; bool m_currentClosed;
int m_timer;
public: CBalloonQueue();
void Init(HWND hwndParent, CNotifyToolbar * toolbar, CTooltips * balloons); void Deinit();
bool OnTimer(int timerId); void UpdateInfo(InternalIconData * notifyItem); void RemoveInfo(InternalIconData * notifyItem); void CloseCurrent();
private:
int IndexOf(InternalIconData * pdata); void SetTimer(int length); void Show(Info& info); void Close(IN OUT InternalIconData * notifyItem, IN UINT uReason);};
class CNotifyToolbar : public CWindowImplBaseT< CToolbar<InternalIconData>, CControlWinTraits >{ HIMAGELIST m_ImageList; int m_VisibleButtonCount;
CBalloonQueue * m_BalloonQueue;
public: CNotifyToolbar(); virtual ~CNotifyToolbar();
int GetVisibleButtonCount(); int FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata); int FindExistingSharedIcon(HICON handle); BOOL AddButton(IN CONST NOTIFYICONDATA *iconData); BOOL SwitchVersion(IN CONST NOTIFYICONDATA *iconData); BOOL UpdateButton(IN CONST NOTIFYICONDATA *iconData); BOOL RemoveButton(IN CONST NOTIFYICONDATA *iconData); VOID ResizeImagelist(); bool SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg); void RefreshToolbarMetrics(BOOL bForceRefresh);
private: LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam); LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled);
public: BEGIN_MSG_MAP(CNotifyToolbar) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent) NOTIFY_CODE_HANDLER(TTN_SHOW, OnTooltipShow) END_MSG_MAP()
void Initialize(HWND hWndParent, CBalloonQueue * queue);};
static const WCHAR szSysPagerWndClass[] = L"SysPager";
class CSysPagerWnd : public CComCoClass<CSysPagerWnd>, public CComObjectRootEx<CComMultiThreadModelNoCS>, public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >, public IOleWindow, public CIconWatcher{ CNotifyToolbar Toolbar; CTooltips m_Balloons; CBalloonQueue m_BalloonQueue;
public: CSysPagerWnd(); virtual ~CSysPagerWnd();
LRESULT DrawBackground(HDC hdc); LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnBalloonPop(UINT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
public: // *** IOleWindow methods ***
STDMETHODIMP GetWindow(HWND* phwnd) override { if (!phwnd) return E_INVALIDARG; *phwnd = m_hWnd; return S_OK; }
STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override { return E_NOTIMPL; }
DECLARE_NOT_AGGREGATABLE(CSysPagerWnd)
DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSysPagerWnd) COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) END_COM_MAP()
BOOL NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData); void GetSize(IN BOOL IsHorizontal, IN PSIZE size);
DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE)
BEGIN_MSG_MAP(CSysPagerWnd) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_COMMAND, OnCommand) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) MESSAGE_HANDLER(WM_TIMER, OnTimer) MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize) NOTIFY_CODE_HANDLER(TTN_POP, OnBalloonPop) NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW, OnGetInfoTip) NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) END_MSG_MAP()
HRESULT Initialize(IN HWND hWndParent);};
/* * IconWatcher */
CIconWatcher::CIconWatcher() : m_hWatcherThread(NULL), m_WakeUpEvent(NULL), m_hwndSysTray(NULL), m_Loop(false){}
CIconWatcher::~CIconWatcher(){ Uninitialize(); DeleteCriticalSection(&m_ListLock);
if (m_WakeUpEvent) CloseHandle(m_WakeUpEvent); if (m_hWatcherThread) CloseHandle(m_hWatcherThread);}
bool CIconWatcher::Initialize(_In_ HWND hWndParent){ m_hwndSysTray = hWndParent;
InitializeCriticalSection(&m_ListLock); m_WakeUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (m_WakeUpEvent == NULL) return false;
m_hWatcherThread = (HANDLE)_beginthreadex(NULL, 0, WatcherThread, (LPVOID)this, 0, NULL); if (m_hWatcherThread == NULL) return false;
return true;}
void CIconWatcher::Uninitialize(){ m_Loop = false; if (m_WakeUpEvent) SetEvent(m_WakeUpEvent);
EnterCriticalSection(&m_ListLock);
POSITION Pos; for (size_t i = 0; i < m_WatcherList.GetCount(); i++) { Pos = m_WatcherList.FindIndex(i); if (Pos) { IconWatcherData *Icon; Icon = m_WatcherList.GetAt(Pos); delete Icon; } } m_WatcherList.RemoveAll();
LeaveCriticalSection(&m_ListLock);}
bool CIconWatcher::AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData){ DWORD ProcessId; (void)GetWindowThreadProcessId(iconData->hWnd, &ProcessId);
HANDLE hProcess; hProcess = OpenProcess(SYNCHRONIZE, FALSE, ProcessId); if (hProcess == NULL) { return false; }
IconWatcherData *Icon = new IconWatcherData(iconData); Icon->hProcess = hProcess; Icon->ProcessId = ProcessId;
bool Added = false; EnterCriticalSection(&m_ListLock);
// The likelyhood of someone having more than 64 icons in their tray is // pretty slim. We could spin up a new thread for each multiple of 64, but // it's not worth the effort, so we just won't bother watching those icons if (m_WatcherList.GetCount() < MAXIMUM_WAIT_OBJECTS) { m_WatcherList.AddTail(Icon); SetEvent(m_WakeUpEvent); Added = true; }
LeaveCriticalSection(&m_ListLock);
if (!Added) { delete Icon; }
return Added;}
bool CIconWatcher::RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData){ EnterCriticalSection(&m_ListLock);
IconWatcherData *Icon; Icon = GetListEntry(iconData, NULL, true);
SetEvent(m_WakeUpEvent); LeaveCriticalSection(&m_ListLock);
delete Icon; return true;}
IconWatcherData* CIconWatcher::GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove){ IconWatcherData *Entry = NULL; POSITION NextPosition = m_WatcherList.GetHeadPosition(); POSITION Position; do { Position = NextPosition;
Entry = m_WatcherList.GetNext(NextPosition); if (Entry) { if ((iconData && ((Entry->IconData.hWnd == iconData->hWnd) && (Entry->IconData.uID == iconData->uID))) || (hProcess && (Entry->hProcess == hProcess))) { if (Remove) m_WatcherList.RemoveAt(Position); break; } } Entry = NULL;
} while (NextPosition != NULL);
return Entry;}
UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam){ CIconWatcher* This = reinterpret_cast<CIconWatcher *>(lpParam); HANDLE *WatchList = NULL;
This->m_Loop = true; while (This->m_Loop) { EnterCriticalSection(&This->m_ListLock);
DWORD Size; Size = This->m_WatcherList.GetCount() + 1; ASSERT(Size <= MAXIMUM_WAIT_OBJECTS);
if (WatchList) delete[] WatchList; WatchList = new HANDLE[Size]; WatchList[0] = This->m_WakeUpEvent;
POSITION Pos; for (size_t i = 0; i < This->m_WatcherList.GetCount(); i++) { Pos = This->m_WatcherList.FindIndex(i); if (Pos) { IconWatcherData *Icon; Icon = This->m_WatcherList.GetAt(Pos); WatchList[i + 1] = Icon->hProcess; } }
LeaveCriticalSection(&This->m_ListLock);
DWORD Status; Status = WaitForMultipleObjects(Size, WatchList, FALSE, INFINITE); if (Status == WAIT_OBJECT_0) { // We've been kicked, we have updates to our list (or we're exiting the thread) if (This->m_Loop) TRACE("Updating watched icon list\n"); } else if ((Status >= WAIT_OBJECT_0 + 1) && (Status < Size)) { IconWatcherData *Icon; Icon = This->GetListEntry(NULL, WatchList[Status], false);
TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf\n", Icon->ProcessId);
TRAYNOTIFYDATAW tnid = {0}; tnid.dwSignature = NI_NOTIFY_SIG; tnid.dwMessage = NIM_DELETE; CopyMemory(&tnid.nid, &Icon->IconData, Icon->IconData.cbSize);
COPYDATASTRUCT data; data.dwData = 1; data.cbData = sizeof(tnid); data.lpData = &tnid;
BOOL Success = ::SendMessage(This->m_hwndSysTray, WM_COPYDATA, (WPARAM)&Icon->IconData, (LPARAM)&data); if (!Success) { // If we failed to handle the delete message, forcibly remove it This->RemoveIconFromWatcher(&Icon->IconData); } } else { if (Status == WAIT_FAILED) { Status = GetLastError(); } ERR("Failed to wait on process handles : %lu\n", Status); This->Uninitialize(); } }
if (WatchList) delete[] WatchList;
return 0;}
/* * BalloonQueue */
CBalloonQueue::CBalloonQueue() : m_hwndParent(NULL), m_tooltips(NULL), m_toolbar(NULL), m_current(NULL), m_currentClosed(false), m_timer(-1){}
void CBalloonQueue::Init(HWND hwndParent, CNotifyToolbar * toolbar, CTooltips * balloons){ m_hwndParent = hwndParent; m_toolbar = toolbar; m_tooltips = balloons;}
void CBalloonQueue::Deinit(){ if (m_timer >= 0) { ::KillTimer(m_hwndParent, m_timer); }}
bool CBalloonQueue::OnTimer(int timerId){ if (timerId != m_timer) return false;
::KillTimer(m_hwndParent, m_timer); m_timer = -1;
if (m_current && !m_currentClosed) { Close(m_current, NIN_BALLOONTIMEOUT); } else { m_current = NULL; m_currentClosed = false; if (!m_queue.IsEmpty()) { Info info = m_queue.RemoveHead(); Show(info); } }
return true;}
void CBalloonQueue::UpdateInfo(InternalIconData * notifyItem){ size_t len = 0; HRESULT hr = StringCchLength(notifyItem->szInfo, _countof(notifyItem->szInfo), &len); if (SUCCEEDED(hr) && len > 0) { Info info(notifyItem);
// If m_current == notifyItem, we want to replace the previous balloon even if there is a queue. if (m_current != notifyItem && (m_current != NULL || !m_queue.IsEmpty())) { m_queue.AddTail(info); } else { Show(info); } } else { Close(notifyItem, NIN_BALLOONHIDE); }}
void CBalloonQueue::RemoveInfo(InternalIconData * notifyItem){ Close(notifyItem, NIN_BALLOONHIDE);
POSITION position = m_queue.GetHeadPosition(); while(position != NULL) { Info& info = m_queue.GetNext(position); if (info.pSource == notifyItem) { m_queue.RemoveAt(position); } }}
void CBalloonQueue::CloseCurrent(){ if (m_current != NULL) { Close(m_current, NIN_BALLOONTIMEOUT); }}
int CBalloonQueue::IndexOf(InternalIconData * pdata){ int count = m_toolbar->GetButtonCount(); for (int i = 0; i < count; i++) { if (m_toolbar->GetItemData(i) == pdata) return i; } return -1;}
void CBalloonQueue::SetTimer(int length){ m_timer = ::SetTimer(m_hwndParent, BalloonsTimerId, length, NULL);}
void CBalloonQueue::Show(Info& info){ TRACE("ShowBalloonTip called for flags=%x text=%ws; title=%ws\n", info.uIcon, info.szInfo, info.szInfoTitle);
// TODO: NIF_REALTIME, NIIF_NOSOUND, other Vista+ flags
m_current = info.pSource; RECT rc; m_toolbar->GetItemRect(IndexOf(m_current), &rc); m_toolbar->ClientToScreen(&rc); const WORD x = (rc.left + rc.right) / 2; const WORD y = (rc.top + rc.bottom) / 2;
m_tooltips->SetTitle(info.szInfoTitle, info.uIcon); m_tooltips->TrackPosition(x, y); m_tooltips->SetMaxTipWidth(BALLOON_MAXWIDTH); m_tooltips->UpdateTipText(m_hwndParent, reinterpret_cast<LPARAM>(m_toolbar->m_hWnd), info.szInfo); m_tooltips->TrackActivate(m_hwndParent, reinterpret_cast<LPARAM>(m_toolbar->m_hWnd));
int timeout = info.uTimeout; if (timeout < MinTimeout) timeout = MinTimeout; if (timeout > MaxTimeout) timeout = MaxTimeout;
SetTimer(timeout);
m_toolbar->SendNotifyCallback(m_current, NIN_BALLOONSHOW);}
void CBalloonQueue::Close(IN OUT InternalIconData * notifyItem, IN UINT uReason){ TRACE("HideBalloonTip called\n");
if (m_current == notifyItem && !m_currentClosed) { m_toolbar->SendNotifyCallback(m_current, uReason);
// Prevent Re-entry m_currentClosed = true; m_tooltips->TrackDeactivate(); SetTimer(CooldownBetweenBalloons); }}
/* * NotifyToolbar */
CNotifyToolbar::CNotifyToolbar() : m_ImageList(NULL), m_VisibleButtonCount(0), m_BalloonQueue(NULL){}
CNotifyToolbar::~CNotifyToolbar(){}
int CNotifyToolbar::GetVisibleButtonCount(){ return m_VisibleButtonCount;}
int CNotifyToolbar::FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata){ int count = GetButtonCount();
for (int i = 0; i < count; i++) { InternalIconData * data = GetItemData(i);
if (data->hWnd == hWnd && data->uID == uID) { if (pdata) *pdata = data; return i; } }
return -1;}
int CNotifyToolbar::FindExistingSharedIcon(HICON handle){ int count = GetButtonCount(); for (int i = 0; i < count; i++) { InternalIconData * data = GetItemData(i); if (data->hIcon == handle) { TBBUTTON btn; GetButton(i, &btn); return btn.iBitmap; } }
return -1;}
BOOL CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA *iconData){ TBBUTTON tbBtn = { 0 }; InternalIconData * notifyItem; WCHAR text[] = L"";
TRACE("Adding icon %d from hWnd %08x flags%s%s state%s%s\n", iconData->uID, iconData->hWnd, (iconData->uFlags & NIF_ICON) ? " ICON" : "", (iconData->uFlags & NIF_STATE) ? " STATE" : "", (iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "", (iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); if (index >= 0) { TRACE("Icon %d from hWnd %08x ALREADY EXISTS!\n", iconData->uID, iconData->hWnd); return FALSE; }
notifyItem = new InternalIconData(); ZeroMemory(notifyItem, sizeof(*notifyItem));
notifyItem->hWnd = iconData->hWnd; notifyItem->uID = iconData->uID;
tbBtn.fsState = TBSTATE_ENABLED; tbBtn.fsStyle = BTNS_NOPREFIX; tbBtn.dwData = (DWORD_PTR)notifyItem; tbBtn.iString = (INT_PTR) text; tbBtn.idCommand = GetButtonCount(); tbBtn.iBitmap = -1;
if (iconData->uFlags & NIF_STATE) { notifyItem->dwState = iconData->dwState & iconData->dwStateMask; }
if (iconData->uFlags & NIF_MESSAGE) { notifyItem->uCallbackMessage = iconData->uCallbackMessage; }
if (iconData->uFlags & NIF_ICON) { notifyItem->hIcon = iconData->hIcon; BOOL hasSharedIcon = notifyItem->dwState & NIS_SHAREDICON; if (hasSharedIcon) { INT iIcon = FindExistingSharedIcon(notifyItem->hIcon); if (iIcon < 0) { notifyItem->hIcon = NULL; TRACE("Shared icon requested, but HICON not found!!!\n"); } tbBtn.iBitmap = iIcon; } else { tbBtn.iBitmap = ImageList_AddIcon(m_ImageList, notifyItem->hIcon); } }
if (iconData->uFlags & NIF_TIP) { StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip); }
if (iconData->uFlags & NIF_INFO) { // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always. StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo); StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle); notifyItem->dwInfoFlags = iconData->dwInfoFlags; notifyItem->uTimeout = iconData->uTimeout; }
if (notifyItem->dwState & NIS_HIDDEN) { tbBtn.fsState |= TBSTATE_HIDDEN; } else { m_VisibleButtonCount++; }
/* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */
CToolbar::AddButton(&tbBtn); SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
if (iconData->uFlags & NIF_INFO) { m_BalloonQueue->UpdateInfo(notifyItem); }
return TRUE;}
BOOL CNotifyToolbar::SwitchVersion(_In_ CONST NOTIFYICONDATA *iconData){ InternalIconData * notifyItem; int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); if (index < 0) { WARN("Icon %d from hWnd %08x DOES NOT EXIST!\n", iconData->uID, iconData->hWnd); return FALSE; }
if (iconData->uVersion != 0 && iconData->uVersion != NOTIFYICON_VERSION) { WARN("Tried to set the version of icon %d from hWnd %08x, to an unknown value %d. Vista+ program?\n", iconData->uID, iconData->hWnd, iconData->uVersion); return FALSE; }
// We can not store the version in the uVersion field, because it's union'd with uTimeout, // which we also need to keep track of. notifyItem->uVersionCopy = iconData->uVersion;
return TRUE;}
BOOL CNotifyToolbar::UpdateButton(_In_ CONST NOTIFYICONDATA *iconData){ InternalIconData * notifyItem; TBBUTTONINFO tbbi = { 0 };
TRACE("Updating icon %d from hWnd %08x flags%s%s state%s%s\n", iconData->uID, iconData->hWnd, (iconData->uFlags & NIF_ICON) ? " ICON" : "", (iconData->uFlags & NIF_STATE) ? " STATE" : "", (iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "", (iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); if (index < 0) { WARN("Icon %d from hWnd %08x DOES NOT EXIST!\n", iconData->uID, iconData->hWnd); return AddButton(iconData); }
TBBUTTON btn; GetButton(index, &btn); int oldIconIndex = btn.iBitmap;
tbbi.cbSize = sizeof(tbbi); tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND; tbbi.idCommand = index;
if (iconData->uFlags & NIF_STATE) { if (iconData->dwStateMask & NIS_HIDDEN && (notifyItem->dwState & NIS_HIDDEN) != (iconData->dwState & NIS_HIDDEN)) { tbbi.dwMask |= TBIF_STATE; if (iconData->dwState & NIS_HIDDEN) { tbbi.fsState |= TBSTATE_HIDDEN; m_VisibleButtonCount--; } else { tbbi.fsState &= ~TBSTATE_HIDDEN; m_VisibleButtonCount++; } }
notifyItem->dwState &= ~iconData->dwStateMask; notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask); }
if (iconData->uFlags & NIF_MESSAGE) { notifyItem->uCallbackMessage = iconData->uCallbackMessage; }
if (iconData->uFlags & NIF_ICON) { BOOL hasSharedIcon = notifyItem->dwState & NIS_SHAREDICON; if (hasSharedIcon) { INT iIcon = FindExistingSharedIcon(iconData->hIcon); if (iIcon >= 0) { notifyItem->hIcon = iconData->hIcon; tbbi.dwMask |= TBIF_IMAGE; tbbi.iImage = iIcon; } else { TRACE("Shared icon requested, but HICON not found!!! IGNORING!\n"); } } else { notifyItem->hIcon = iconData->hIcon; tbbi.dwMask |= TBIF_IMAGE; tbbi.iImage = ImageList_ReplaceIcon(m_ImageList, oldIconIndex, notifyItem->hIcon); } }
if (iconData->uFlags & NIF_TIP) { StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip); }
if (iconData->uFlags & NIF_INFO) { // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always. StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo); StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle); notifyItem->dwInfoFlags = iconData->dwInfoFlags; notifyItem->uTimeout = iconData->uTimeout; }
/* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */
SetButtonInfo(index, &tbbi);
if (iconData->uFlags & NIF_INFO) { m_BalloonQueue->UpdateInfo(notifyItem); }
return TRUE;}
BOOL CNotifyToolbar::RemoveButton(_In_ CONST NOTIFYICONDATA *iconData){ InternalIconData * notifyItem;
TRACE("Removing icon %d from hWnd %08x\n", iconData->uID, iconData->hWnd);
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); if (index < 0) { TRACE("Icon %d from hWnd %08x ALREADY MISSING!\n", iconData->uID, iconData->hWnd);
return FALSE; }
if (!(notifyItem->dwState & NIS_HIDDEN)) { m_VisibleButtonCount--; }
if (!(notifyItem->dwState & NIS_SHAREDICON)) { TBBUTTON btn; GetButton(index, &btn); int oldIconIndex = btn.iBitmap; ImageList_Remove(m_ImageList, oldIconIndex);
// Update other icons! int count = GetButtonCount(); for (int i = 0; i < count; i++) { TBBUTTON btn; GetButton(i, &btn);
if (btn.iBitmap > oldIconIndex) { TBBUTTONINFO tbbi2 = { 0 }; tbbi2.cbSize = sizeof(tbbi2); tbbi2.dwMask = TBIF_BYINDEX | TBIF_IMAGE; tbbi2.iImage = btn.iBitmap-1; SetButtonInfo(i, &tbbi2); } } }
m_BalloonQueue->RemoveInfo(notifyItem);
DeleteButton(index);
delete notifyItem;
return TRUE;}
VOID CNotifyToolbar::ResizeImagelist(){ int cx, cy; HIMAGELIST iml;
if (!ImageList_GetIconSize(m_ImageList, &cx, &cy)) return;
if (cx == GetSystemMetrics(SM_CXSMICON) && cy == GetSystemMetrics(SM_CYSMICON)) return;
iml = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000); if (!iml) return;
ImageList_Destroy(m_ImageList); m_ImageList = iml; SetImageList(m_ImageList);
int count = GetButtonCount(); for (int i = 0; i < count; i++) { InternalIconData * data = GetItemData(i); BOOL hasSharedIcon = data->dwState & NIS_SHAREDICON; INT iIcon = hasSharedIcon ? FindExistingSharedIcon(data->hIcon) : -1; if (iIcon < 0) iIcon = ImageList_AddIcon(iml, data->hIcon); TBBUTTONINFO tbbi = { sizeof(tbbi), TBIF_BYINDEX | TBIF_IMAGE, 0, iIcon}; SetButtonInfo(i, &tbbi); }
SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));}
LRESULT CNotifyToolbar::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (m_ImageList) { ImageList_Destroy(m_ImageList); m_ImageList = NULL; } return 0;}
LRESULT CNotifyToolbar::OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ bHandled = FALSE;
/* * WM_CONTEXTMENU message can be generated either by the mouse, * in which case lParam encodes the mouse coordinates where the * user right-clicked the mouse, or can be generated by (Shift-)F10 * keyboard press, in which case lParam equals -1. */ INT iBtn = GetHotItem(); if (iBtn < 0) return 0;
InternalIconData* notifyItem = GetItemData(iBtn);
if (!::IsWindow(notifyItem->hWnd)) return 0;
if (notifyItem->uVersionCopy >= NOTIFYICON_VERSION) { /* Transmit the WM_CONTEXTMENU message if the notification icon supports it */ ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, WM_CONTEXTMENU); } else if (lParam == -1) { /* * Otherwise, and only if the WM_CONTEXTMENU message was generated * from the keyboard, simulate right-click mouse messages. This is * not needed if the message came from the mouse because in this * case the right-click mouse messages were already sent together. */ ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, WM_RBUTTONDOWN); ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, WM_RBUTTONUP); }
return 0;}
bool CNotifyToolbar::SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg){ if (!::IsWindow(notifyItem->hWnd)) { // We detect and destroy icons with invalid handles only on mouse move over systray, same as MS does. // Alternatively we could search for them periodically (would waste more resources). TRACE("Destroying icon %d with invalid handle hWnd=%08x\n", notifyItem->uID, notifyItem->hWnd);
RemoveButton(notifyItem);
/* Ask the parent to resize */ NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN}; GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
return true; }
DWORD pid; GetWindowThreadProcessId(notifyItem->hWnd, &pid);
if (pid == GetCurrentProcessId() || (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)) { ::PostMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg); } else { ::SendMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg); } return false;}
VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam){ static LPCWSTR eventNames [] = { L"WM_MOUSEMOVE", L"WM_LBUTTONDOWN", L"WM_LBUTTONUP", L"WM_LBUTTONDBLCLK", L"WM_RBUTTONDOWN", L"WM_RBUTTONUP", L"WM_RBUTTONDBLCLK", L"WM_MBUTTONDOWN", L"WM_MBUTTONUP", L"WM_MBUTTONDBLCLK", L"WM_MOUSEWHEEL", L"WM_XBUTTONDOWN", L"WM_XBUTTONUP", L"WM_XBUTTONDBLCLK" };
InternalIconData * notifyItem = GetItemData(wIndex);
if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) { TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n", eventNames[uMsg - WM_MOUSEFIRST], wIndex, notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg); }
SendNotifyCallback(notifyItem, uMsg);}
LRESULT CNotifyToolbar::OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; INT iBtn = HitTest(&pt);
if (iBtn >= 0) { SendMouseEvent(iBtn, uMsg, wParam); }
bHandled = FALSE; return FALSE;}
static VOID GetTooltipText(LPARAM data, LPTSTR szTip, DWORD cchTip){ InternalIconData * notifyItem = reinterpret_cast<InternalIconData *>(data); if (notifyItem) { StringCchCopy(szTip, cchTip, notifyItem->szTip); } else { StringCchCopy(szTip, cchTip, L""); }}
LRESULT CNotifyToolbar::OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled){ RECT rcTip, rcItem; ::GetWindowRect(hdr->hwndFrom, &rcTip);
SIZE szTip = { rcTip.right - rcTip.left, rcTip.bottom - rcTip.top };
INT iBtn = GetHotItem();
if (iBtn >= 0) { MONITORINFO monInfo = { 0 }; HMONITOR hMon = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
monInfo.cbSize = sizeof(monInfo);
if (hMon) GetMonitorInfo(hMon, &monInfo); else ::GetWindowRect(GetDesktopWindow(), &monInfo.rcMonitor);
GetItemRect(iBtn, &rcItem);
POINT ptItem = { rcItem.left, rcItem.top }; SIZE szItem = { rcItem.right - rcItem.left, rcItem.bottom - rcItem.top }; ClientToScreen(&ptItem);
ptItem.x += szItem.cx / 2; ptItem.y -= szTip.cy;
if (ptItem.x + szTip.cx > monInfo.rcMonitor.right) ptItem.x = monInfo.rcMonitor.right - szTip.cx;
if (ptItem.y + szTip.cy > monInfo.rcMonitor.bottom) ptItem.y = monInfo.rcMonitor.bottom - szTip.cy;
if (ptItem.x < monInfo.rcMonitor.left) ptItem.x = monInfo.rcMonitor.left;
if (ptItem.y < monInfo.rcMonitor.top) ptItem.y = monInfo.rcMonitor.top;
TRACE("ptItem { %d, %d }\n", ptItem.x, ptItem.y);
::SetWindowPos(hdr->hwndFrom, NULL, ptItem.x, ptItem.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
return TRUE; }
bHandled = FALSE; return 0;}
void CNotifyToolbar::Initialize(HWND hWndParent, CBalloonQueue * queue){ m_BalloonQueue = queue;
DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_TRANSPARENT | CCS_TOP | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER;
// HACK & FIXME: CORE-18016 HWND hwnd = CToolbar::Create(hWndParent, styles); m_hWnd = NULL; SubclassWindow(hwnd);
// Force the toolbar tooltips window to always show tooltips even if not foreground HWND tooltipsWnd = (HWND)SendMessageW(TB_GETTOOLTIPS); if (tooltipsWnd) { ::SetWindowLong(tooltipsWnd, GWL_STYLE, ::GetWindowLong(tooltipsWnd, GWL_STYLE) | TTS_ALWAYSTIP); }
SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
m_ImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000); SetImageList(m_ImageList);
RefreshToolbarMetrics(TRUE);
SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));}
void CNotifyToolbar::RefreshToolbarMetrics(BOOL bForceRefresh = FALSE){ // Toolbar metrics only needs to be refreshed for the automatic setting and first launch if (g_TaskbarSettings.eCompactTrayIcons == TrayIconsMode::TIM_Default || bForceRefresh) { TBMETRICS tbm = {sizeof(tbm)}; tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING | TBMF_PAD; tbm.cxPad = 1; tbm.cyPad = 1; if (!g_TaskbarSettings.UseCompactTrayIcons()) { tbm.cxPad = GetSystemMetrics(SM_CXSMICON) / 2; tbm.cyPad = GetSystemMetrics(SM_CYSMICON) / 2; } tbm.cxBarPad = 1; tbm.cyBarPad = 1; tbm.cxButtonSpacing = 1; tbm.cyButtonSpacing = 1; SetMetrics(&tbm); }}
/* * SysPagerWnd */
CSysPagerWnd::CSysPagerWnd() {}
CSysPagerWnd::~CSysPagerWnd() {}
LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ HDC hdc = (HDC) wParam;
if (!IsAppThemed()) { bHandled = FALSE; return 0; }
RECT rect; GetClientRect(&rect); DrawThemeParentBackground(m_hWnd, hdc, &rect);
return TRUE;}
LRESULT CSysPagerWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ Toolbar.Initialize(m_hWnd, &m_BalloonQueue); CIconWatcher::Initialize(m_hWnd);
HWND hWndTop = GetAncestor(m_hWnd, GA_ROOT);
m_Balloons.Create(hWndTop, TTS_NOPREFIX | TTS_BALLOON | TTS_CLOSE);
TOOLINFOW ti = { 0 }; ti.cbSize = TTTOOLINFOW_V1_SIZE; ti.uFlags = TTF_TRACK | TTF_IDISHWND; ti.uId = reinterpret_cast<UINT_PTR>(Toolbar.m_hWnd); ti.hwnd = m_hWnd; ti.lpszText = NULL; ti.lParam = NULL;
BOOL ret = m_Balloons.AddTool(&ti); if (!ret) { WARN("AddTool failed, LastError=%d (probably meaningless unless non-zero)\n", GetLastError()); }
m_BalloonQueue.Init(m_hWnd, &Toolbar, &m_Balloons);
// Explicitly request running applications to re-register their systray icons ::SendNotifyMessageW(HWND_BROADCAST, RegisterWindowMessageW(L"TaskbarCreated"), 0, 0);
return TRUE;}
LRESULT CSysPagerWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ m_BalloonQueue.Deinit(); CIconWatcher::Uninitialize(); return TRUE;}
BOOL CSysPagerWnd::NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData){ BOOL ret = FALSE;
int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
TRACE("NotifyIcon received. Code=%d\n", dwMessage); switch (dwMessage) { case NIM_ADD: ret = Toolbar.AddButton(iconData); if (ret == TRUE) { (void)AddIconToWatcher(iconData); } break;
case NIM_MODIFY: ret = Toolbar.UpdateButton(iconData); break;
case NIM_DELETE: ret = Toolbar.RemoveButton(iconData); if (ret == TRUE) { (void)RemoveIconFromWatcher(iconData); } break;
case NIM_SETFOCUS: Toolbar.SetFocus(); ret = TRUE; break;
case NIM_SETVERSION: ret = Toolbar.SwitchVersion(iconData); break;
default: TRACE("NotifyIcon received with unknown code %d.\n", dwMessage); return FALSE; }
if (VisibleButtonCount != Toolbar.GetVisibleButtonCount()) { /* Ask the parent to resize */ NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN}; GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh); }
return ret;}
void CSysPagerWnd::GetSize(IN BOOL IsHorizontal, IN PSIZE size){ /* Get the ideal height or width */#if 0 /* Unfortunately this doens't work correctly in ros */ Toolbar.GetIdealSize(!IsHorizontal, size);
/* Make the reference dimension an exact multiple of the icon size */ if (IsHorizontal) size->cy -= size->cy % GetSystemMetrics(SM_CYSMICON); else size->cx -= size->cx % GetSystemMetrics(SM_CXSMICON);
#else INT rows = 0; INT columns = 0; INT cyButton = GetSystemMetrics(SM_CYSMICON) + 2; INT cxButton = GetSystemMetrics(SM_CXSMICON) + 2; if (!g_TaskbarSettings.UseCompactTrayIcons()) { cyButton = MulDiv(GetSystemMetrics(SM_CYSMICON), 3, 2); cxButton = MulDiv(GetSystemMetrics(SM_CXSMICON), 3, 2); } int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
if (IsHorizontal) { if (!g_TaskbarSettings.UseCompactTrayIcons()) rows = max(size->cy / MulDiv(cyButton, 3, 2), 1); else rows = max(size->cy / cyButton, 1); columns = (VisibleButtonCount + rows - 1) / rows; } else { columns = max(size->cx / cxButton, 1); rows = (VisibleButtonCount + columns - 1) / columns; } size->cx = columns * cxButton; size->cy = rows * cyButton;#endif}
LRESULT CSysPagerWnd::OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled){ NMTBGETINFOTIPW * nmtip = (NMTBGETINFOTIPW *) hdr; GetTooltipText(nmtip->lParam, nmtip->pszText, nmtip->cchTextMax); return TRUE;}
LRESULT CSysPagerWnd::OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled){ NMCUSTOMDRAW * cdraw = (NMCUSTOMDRAW *) hdr; switch (cdraw->dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT: return TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | TBCDRF_NOETCHEDEFFECT; } return TRUE;}
LRESULT CSysPagerWnd::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ bHandled = FALSE;
/* Handles the BN_CLICKED notifications sent by the CNotifyToolbar member */ if (HIWORD(wParam) != BN_CLICKED) return 0;
INT iBtn = LOWORD(wParam); if (iBtn < 0) return 0;
InternalIconData* notifyItem = Toolbar.GetItemData(iBtn);
if (!::IsWindow(notifyItem->hWnd)) return 0;
// TODO: Improve keyboard handling by looking whether one presses // on ENTER, etc..., which roughly translates into "double-clicking".
if (notifyItem->uVersionCopy >= NOTIFYICON_VERSION) { /* Use new-style notifications if the notification icon supports them */ ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, NIN_SELECT); // TODO: Distinguish with NIN_KEYSELECT } else if (lParam == -1) { /* * Otherwise, and only if the icon was selected via the keyboard, * simulate right-click mouse messages. This is not needed if the * selection was done by mouse because in this case the mouse * messages were already sent. */ ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, WM_LBUTTONDOWN); // TODO: Distinguish with double-click WM_LBUTTONDBLCLK ::SendNotifyMessage(notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, WM_LBUTTONUP); }
return 0;}
LRESULT CSysPagerWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ LRESULT Ret = TRUE; SIZE szClient; szClient.cx = LOWORD(lParam); szClient.cy = HIWORD(lParam);
Ret = DefWindowProc(uMsg, wParam, lParam);
if (Toolbar) { Toolbar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER); Toolbar.AutoSize();
RECT rc; Toolbar.GetClientRect(&rc);
SIZE szBar = { rc.right - rc.left, rc.bottom - rc.top };
INT xOff = (szClient.cx - szBar.cx) / 2; INT yOff = (szClient.cy - szBar.cy) / 2;
Toolbar.SetWindowPos(NULL, xOff, yOff, szBar.cx, szBar.cy, SWP_NOZORDER); Toolbar.RefreshToolbarMetrics(); } return Ret;}
LRESULT CSysPagerWnd::OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ bHandled = TRUE; return 0;}
LRESULT CSysPagerWnd::OnBalloonPop(UINT uCode, LPNMHDR hdr , BOOL& bHandled){ m_BalloonQueue.CloseCurrent(); bHandled = TRUE; return 0;}
LRESULT CSysPagerWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (m_BalloonQueue.OnTimer(wParam)) { bHandled = TRUE; }
return 0;}
LRESULT CSysPagerWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam; if (cpData->dwData == TABDMC_NOTIFY) { /* A taskbar NotifyIcon notification */ PTRAYNOTIFYDATAW pData = (PTRAYNOTIFYDATAW)cpData->lpData; if (pData->dwSignature == NI_NOTIFY_SIG) return NotifyIcon(pData->dwMessage, &pData->nid); } else if (cpData->dwData == TABDMC_LOADINPROC) { FIXME("Taskbar Load In Proc\n"); }
return FALSE;}
LRESULT CSysPagerWnd::OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (wParam == SPI_SETNONCLIENTMETRICS) { Toolbar.ResizeImagelist(); } return 0;}
LRESULT CSysPagerWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ GetSize((BOOL)wParam, (PSIZE)lParam); return 0;}
HRESULT CSysPagerWnd::Initialize(IN HWND hWndParent){ /* Create the window. The tray window is going to move it to the correct position and resize it as needed. */ DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE; Create(hWndParent, 0, NULL, dwStyle); if (!m_hWnd) return E_FAIL;
SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
return S_OK;}
HRESULT CSysPagerWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv){ return ShellObjectCreatorInit<CSysPagerWnd>(hwndParent, riid, ppv);}