You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
976 lines
21 KiB
C++
976 lines
21 KiB
C++
2 years ago
|
// XTPTrayIcon.cpp : implementation of the CXTPTrayIcon class.
|
||
|
//
|
||
|
// This file is a part of the XTREME CONTROLS MFC class library.
|
||
|
// (c)1998-2012 Codejock Software, All Rights Reserved.
|
||
|
//
|
||
|
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
|
||
|
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
|
||
|
// CONSENT OF CODEJOCK SOFTWARE.
|
||
|
//
|
||
|
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
|
||
|
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
|
||
|
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
|
||
|
// SINGLE COMPUTER.
|
||
|
//
|
||
|
// CONTACT INFORMATION:
|
||
|
// support@codejock.com
|
||
|
// http://www.codejock.com
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "Common/XTPVC80Helpers.h"
|
||
|
#include "Common/XTPSystemHelpers.h"
|
||
|
#include "Common/XTPDrawHelpers.h"
|
||
|
|
||
|
#include "../Util/XTPUtil.h"
|
||
|
#include "../Defines.h"
|
||
|
#include "XTPTrayIcon.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#define new DEBUG_NEW
|
||
|
#endif
|
||
|
|
||
|
const UINT XTP_WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));
|
||
|
|
||
|
#ifndef IDANI_CAPTION
|
||
|
#define IDANI_CAPTION 3
|
||
|
#endif//IDANI_CAPTION
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon
|
||
|
|
||
|
CXTPTrayIcon::CXTPTrayIcon()
|
||
|
{
|
||
|
SetDefaultValues();
|
||
|
XTPDrawHelpers()->RegisterWndClass(NULL, XTPTRAYICON_CLASSNAME, 0);
|
||
|
}
|
||
|
|
||
|
CXTPTrayIcon::~CXTPTrayIcon()
|
||
|
{
|
||
|
RemoveIcon();
|
||
|
DestroyWindow();
|
||
|
RemoveAnimationIcons();
|
||
|
}
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CXTPTrayIcon, CWnd)
|
||
|
//{{AFX_MSG_MAP(CXTPTrayIcon)
|
||
|
ON_WM_TIMER()
|
||
|
ON_WM_SETTINGCHANGE()
|
||
|
//}}AFX_MSG_MAP
|
||
|
ON_REGISTERED_MESSAGE(XTP_WM_TASKBARCREATED, OnTaskbarCreated)
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon initialization
|
||
|
|
||
|
void CXTPTrayIcon::SetDefaultValues()
|
||
|
{
|
||
|
::ZeroMemory(&m_niData, sizeof(NOTIFYICONDATAEX));
|
||
|
|
||
|
// Default initialization for the NOTIFYICONDATA base struct.
|
||
|
|
||
|
m_niData.cbSize = sizeof(NOTIFYICONDATAEX);
|
||
|
m_niData.hWnd = NULL;
|
||
|
m_niData.uID = 0;
|
||
|
m_niData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
|
||
|
m_niData.uCallbackMessage = TIN_XTP_TRAYICON;
|
||
|
m_niData.hIcon = NULL;
|
||
|
m_niData.szTip[0] = 0;
|
||
|
|
||
|
// Initialize the remaining member data.
|
||
|
|
||
|
m_iMaxTipSize = 64;
|
||
|
m_nIconID = 0;
|
||
|
m_strToolTip = _T("");
|
||
|
m_nIDEvent = 1001;
|
||
|
m_nCounter = 0;
|
||
|
m_uDefMenuItemID = 0;
|
||
|
m_bDefMenuItemByPos = true;
|
||
|
m_bHidden = true;
|
||
|
m_bRemoved = true;
|
||
|
m_bShowPending = false;
|
||
|
m_hWndNotify = NULL;
|
||
|
m_hIcon = NULL;
|
||
|
}
|
||
|
|
||
|
BOOL CXTPTrayIcon::IsShellVersion5() const
|
||
|
{
|
||
|
static BOOL bVersion5 = -1;
|
||
|
|
||
|
if (bVersion5 != -1)
|
||
|
return bVersion5;
|
||
|
|
||
|
CXTPModuleHandle hShell(_T("shell32.dll"));
|
||
|
bVersion5 = HIWORD(hShell.GetVersion()) >= 5;
|
||
|
|
||
|
return bVersion5;
|
||
|
}
|
||
|
|
||
|
BOOL CXTPTrayIcon::ShellNotify(DWORD dwMessage)
|
||
|
{
|
||
|
return ::Shell_NotifyIcon(dwMessage, (PNOTIFYICONDATA)&m_niData);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon notification window initialization
|
||
|
|
||
|
bool CXTPTrayIcon::SetNotificationWnd(CWnd* pWndNotify)
|
||
|
{
|
||
|
ASSERT_VALID(pWndNotify);
|
||
|
|
||
|
// Make sure Notification window is valid
|
||
|
if (!pWndNotify || !::IsWindow(pWndNotify->GetSafeHwnd()))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// assign values
|
||
|
m_niData.hWnd = pWndNotify->GetSafeHwnd();
|
||
|
m_niData.uFlags = 0;
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
CWnd* CXTPTrayIcon::GetNotificationWnd()
|
||
|
{
|
||
|
return CWnd::FromHandle(m_niData.hWnd);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon notification message handling
|
||
|
|
||
|
bool CXTPTrayIcon::SetCallbackMessage(UINT uNewCallbackMessage)
|
||
|
{
|
||
|
// Make sure we avoid conflict with other messages
|
||
|
ASSERT(m_niData.uCallbackMessage >= WM_APP);
|
||
|
|
||
|
m_niData.uCallbackMessage = uNewCallbackMessage;
|
||
|
m_niData.uFlags = NIF_MESSAGE;
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
UINT CXTPTrayIcon::GetCallbackMessage()
|
||
|
{
|
||
|
return m_niData.uCallbackMessage;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon icon initialization
|
||
|
|
||
|
bool CXTPTrayIcon::SetIcon(HICON hIcon)
|
||
|
{
|
||
|
// check to see if this icon was already set.
|
||
|
if (m_niData.hIcon == hIcon)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
m_niData.uFlags = NIF_ICON;
|
||
|
m_niData.hIcon = hIcon;
|
||
|
|
||
|
if (m_bHidden)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::SetIcon(LPCTSTR lpszIconName)
|
||
|
{
|
||
|
int cx = ::GetSystemMetrics(SM_CXSMICON);
|
||
|
int cy = ::GetSystemMetrics(SM_CYSMICON);
|
||
|
|
||
|
m_hIcon = (HICON)::LoadImage(AfxGetResourceHandle(),
|
||
|
lpszIconName, IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
|
||
|
|
||
|
return SetIcon(m_hIcon);
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::SetIcon(UINT nIDResource)
|
||
|
{
|
||
|
return SetIcon(MAKEINTRESOURCE(nIDResource));
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::RemoveAnimationIcons()
|
||
|
{
|
||
|
while (!m_arTrayIcons.IsEmpty())
|
||
|
{
|
||
|
TRAYICONDATA ti = m_arTrayIcons.RemoveHead();
|
||
|
::DestroyIcon(ti.hIcon);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::SetAnimationIcons(const UINT* lpIDArray, int nIDCount, const CString* lpStrTipArray/*= NULL*/)
|
||
|
{
|
||
|
RemoveAnimationIcons();
|
||
|
|
||
|
int cx = ::GetSystemMetrics(SM_CXSMICON);
|
||
|
int cy = ::GetSystemMetrics(SM_CYSMICON);
|
||
|
|
||
|
int iIcon;
|
||
|
for (iIcon = 0; iIcon < nIDCount; ++iIcon)
|
||
|
{
|
||
|
TRAYICONDATA ti;
|
||
|
|
||
|
if (lpStrTipArray != NULL)
|
||
|
ti.strToolTip = lpStrTipArray[ iIcon ];
|
||
|
|
||
|
ti.hIcon = (HICON)::LoadImage(AfxGetResourceHandle(),
|
||
|
MAKEINTRESOURCE(lpIDArray[ iIcon ]), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
|
||
|
|
||
|
if (ti.hIcon == NULL)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_arTrayIcons.AddTail(ti);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
HICON CXTPTrayIcon::GetIcon() const
|
||
|
{
|
||
|
return m_niData.hIcon;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon tooltip initialization
|
||
|
|
||
|
bool CXTPTrayIcon::SetTooltipText(LPCTSTR lpszTipText)
|
||
|
{
|
||
|
m_strToolTip = lpszTipText;
|
||
|
|
||
|
if (m_strToolTip.GetLength() >= (int)m_iMaxTipSize)
|
||
|
m_strToolTip = m_strToolTip.Left((int)m_iMaxTipSize-1);
|
||
|
|
||
|
return SetShellTooltip(m_strToolTip);
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::SetShellTooltip(LPCTSTR lpszTipText)
|
||
|
{
|
||
|
ASSERT(AfxIsValidString(lpszTipText));
|
||
|
ASSERT(_tcslen(lpszTipText) < m_iMaxTipSize);
|
||
|
|
||
|
m_niData.uFlags = NIF_TIP;
|
||
|
STRNCPY_S(m_niData.szTip, _countof(m_niData.szTip), lpszTipText, m_iMaxTipSize-1);
|
||
|
|
||
|
if (m_bHidden)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::SetTooltipText(UINT nTipText)
|
||
|
{
|
||
|
CString strTipText;
|
||
|
if (!strTipText.LoadString(nTipText))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return SetTooltipText(strTipText);
|
||
|
}
|
||
|
|
||
|
CString CXTPTrayIcon::GetTooltipText() const
|
||
|
{
|
||
|
return m_strToolTip;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon manipulation
|
||
|
|
||
|
bool CXTPTrayIcon::AddIcon()
|
||
|
{
|
||
|
if (!m_bRemoved)
|
||
|
{
|
||
|
RemoveIcon();
|
||
|
}
|
||
|
|
||
|
m_niData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
|
||
|
if (!ShellNotify(NIM_ADD))
|
||
|
{
|
||
|
m_bShowPending = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_bRemoved = m_bHidden = false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::RemoveIcon()
|
||
|
{
|
||
|
m_bShowPending = false;
|
||
|
|
||
|
if (m_bRemoved)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
m_niData.uFlags = 0;
|
||
|
if (!ShellNotify(NIM_DELETE))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_bRemoved = m_bHidden = true;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::HideIcon()
|
||
|
{
|
||
|
if (m_bRemoved || m_bHidden)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (IsShellVersion5())
|
||
|
{
|
||
|
m_niData.uFlags = NIF_STATE;
|
||
|
m_niData.dwState = NIS_HIDDEN;
|
||
|
m_niData.dwStateMask = NIS_HIDDEN;
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
m_bHidden = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_bHidden = true;
|
||
|
}
|
||
|
|
||
|
return m_bHidden;
|
||
|
}
|
||
|
|
||
|
return RemoveIcon();
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::ShowIcon()
|
||
|
{
|
||
|
if (m_bRemoved)
|
||
|
return AddIcon();
|
||
|
|
||
|
if (!m_bHidden)
|
||
|
return TRUE;
|
||
|
|
||
|
if (IsShellVersion5())
|
||
|
{
|
||
|
m_niData.uFlags = NIF_STATE;
|
||
|
m_niData.dwState = 0;
|
||
|
m_niData.dwStateMask = NIS_HIDDEN;
|
||
|
|
||
|
if (!ShellNotify(NIM_MODIFY))
|
||
|
return false;
|
||
|
|
||
|
m_bRemoved = m_bHidden = false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return AddIcon();
|
||
|
}
|
||
|
|
||
|
BOOL CXTPTrayIcon::ShowIcon(BOOL bShow)
|
||
|
{
|
||
|
if (bShow)
|
||
|
{
|
||
|
return (BOOL)ShowIcon();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (BOOL)HideIcon();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon menu initialization
|
||
|
|
||
|
bool CXTPTrayIcon::SetDefaultMenuItem(UINT uItem, bool bByPos)
|
||
|
{
|
||
|
// if the values already exist, return true.
|
||
|
if ((m_uDefMenuItemID == uItem) && (m_bDefMenuItemByPos == bByPos))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
m_uDefMenuItemID = uItem;
|
||
|
m_bDefMenuItemByPos = bByPos;
|
||
|
|
||
|
// verify that we can load the menu defined by uID.
|
||
|
CMenu menu;
|
||
|
if (!menu.LoadMenu(m_niData.uID))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// see if we can access the submenu
|
||
|
CMenu* pSubMenu = menu.GetSubMenu(0);
|
||
|
if (!pSubMenu)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// check to see if we can set the submenu for the popup. This is just a check to ensure
|
||
|
// that everything has been correctly set.
|
||
|
if (!::SetMenuDefaultItem(pSubMenu->m_hMenu, m_uDefMenuItemID, m_bDefMenuItemByPos))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::GetDefaultMenuItem(UINT& uItem, bool& bByPos)
|
||
|
{
|
||
|
uItem = m_uDefMenuItemID;
|
||
|
bByPos = m_bDefMenuItemByPos;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon message handlers
|
||
|
|
||
|
void CXTPTrayIcon::OnTimer(UINT_PTR nIDEvent)
|
||
|
{
|
||
|
// Update the tray icon and tooltip text.
|
||
|
if (nIDEvent == m_nIDEvent)
|
||
|
{
|
||
|
POSITION pos = m_arTrayIcons.FindIndex(m_nCounter);
|
||
|
if (pos != NULL)
|
||
|
{
|
||
|
TRAYICONDATA& ti = m_arTrayIcons.GetAt(pos);
|
||
|
|
||
|
if (!ti.strToolTip.IsEmpty())
|
||
|
{
|
||
|
SetShellTooltip(ti.strToolTip);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetShellTooltip(m_strToolTip);
|
||
|
}
|
||
|
|
||
|
if (ti.hIcon != NULL)
|
||
|
{
|
||
|
SetIcon(ti.hIcon);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_nCounter = (m_nCounter + 1) % (int)m_arTrayIcons.GetCount();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::Create(
|
||
|
LPCTSTR lpszCaption,
|
||
|
CWnd* pParentWnd,
|
||
|
UINT nIconID,
|
||
|
UINT uMenuID/*= 0*/,
|
||
|
UINT uDefMenuItemID/*= 0*/,
|
||
|
bool bDefMenuItemByPos/*= false*/)
|
||
|
{
|
||
|
m_nIconID = nIconID;
|
||
|
m_strToolTip = lpszCaption;
|
||
|
m_hWndNotify = pParentWnd->GetSafeHwnd();
|
||
|
|
||
|
m_iMaxTipSize = _countof(m_niData.szTip) - 1;
|
||
|
|
||
|
// Set the tray icon and tooltip text
|
||
|
SetIcon(m_nIconID);
|
||
|
SetTooltipText(m_strToolTip);
|
||
|
|
||
|
// Create an invisible window
|
||
|
CWnd::CreateEx(0, XTPTRAYICON_CLASSNAME, lpszCaption, WS_POPUP, 0, 0, 0, 0, NULL, 0);
|
||
|
|
||
|
m_niData.hWnd = m_hWnd;
|
||
|
m_niData.uID = uMenuID;
|
||
|
m_uDefMenuItemID = uDefMenuItemID;
|
||
|
m_bDefMenuItemByPos = bDefMenuItemByPos;
|
||
|
|
||
|
m_uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
|
||
|
|
||
|
return AddIcon();
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::ShowBalloonTip(LPCTSTR lpszInfo, LPCTSTR lpszInfoTitle/*= NULL*/, DWORD dwInfoFlags/*= NIIF_NONE*/, UINT uTimeout/*= 10*/)
|
||
|
{
|
||
|
bool bResult = false;
|
||
|
|
||
|
if (IsShellVersion5() && lpszInfo)
|
||
|
{
|
||
|
// The balloon tooltip text can be up to 255 chars long.
|
||
|
ASSERT(AfxIsValidString(lpszInfo));
|
||
|
ASSERT(lstrlen(lpszInfo) < 256);
|
||
|
|
||
|
// The balloon title text can be up to 63 chars long.
|
||
|
if (lpszInfoTitle)
|
||
|
{
|
||
|
ASSERT(AfxIsValidString(lpszInfoTitle));
|
||
|
ASSERT(lstrlen(lpszInfoTitle) < 64);
|
||
|
}
|
||
|
|
||
|
// dwInfoFlags must be valid.
|
||
|
ASSERT(NIIF_NONE == dwInfoFlags ||
|
||
|
NIIF_INFO == dwInfoFlags ||
|
||
|
NIIF_WARNING == dwInfoFlags ||
|
||
|
NIIF_ERROR == dwInfoFlags);
|
||
|
|
||
|
// The timeout must be between 10 and 30 seconds.
|
||
|
ASSERT(uTimeout >= 10 && uTimeout <= 30);
|
||
|
|
||
|
m_niData.uFlags |= NIF_INFO;
|
||
|
|
||
|
STRNCPY_S(m_niData.szInfo, _countof(m_niData.szInfo), lpszInfo, 255);
|
||
|
|
||
|
if (lpszInfoTitle)
|
||
|
{
|
||
|
STRNCPY_S(m_niData.szInfoTitle, _countof(m_niData.szInfoTitle), lpszInfoTitle, 63);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_niData.szInfoTitle[0] = _T('\0');
|
||
|
}
|
||
|
|
||
|
m_niData.uTimeout = (uTimeout * 1000); // convert time to millisecs
|
||
|
m_niData.dwInfoFlags = dwInfoFlags;
|
||
|
|
||
|
if (ShellNotify(NIM_MODIFY))
|
||
|
{
|
||
|
bResult = true;
|
||
|
}
|
||
|
|
||
|
// Zero out the balloon text string so that later operations won't redisplay
|
||
|
// the balloon.
|
||
|
m_niData.szInfo[0] = _T('\0');
|
||
|
}
|
||
|
|
||
|
return bResult;
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::StartAnimation(UINT uElapse/*= 500*/)
|
||
|
{
|
||
|
CWnd::SetTimer(m_nIDEvent, uElapse, NULL);
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::StopAnimation()
|
||
|
{
|
||
|
CWnd::KillTimer(m_nIDEvent);
|
||
|
|
||
|
SetShellTooltip(m_strToolTip);
|
||
|
SetIcon(m_nIconID);
|
||
|
}
|
||
|
|
||
|
LRESULT CXTPTrayIcon::OnTrayNotification(WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
// Return quickly if its not for this tray icon
|
||
|
if (wParam != m_niData.uID)
|
||
|
{
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
|
if (m_hWndNotify == NULL)
|
||
|
{
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
|
// Check to see if our notification window has already handled this
|
||
|
// message, if so then return success.
|
||
|
if (::SendMessage(m_hWndNotify, TIN_XTP_TRAYICON, wParam, lParam))
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
switch (LOWORD(lParam))
|
||
|
{
|
||
|
|
||
|
// Sent when the balloon is shown (balloons are queued).
|
||
|
case NIN_BALLOONSHOW:
|
||
|
break;
|
||
|
|
||
|
// Sent when the balloon disappears-for example, when the
|
||
|
// icon is deleted. This message is not sent if the balloon
|
||
|
// is dismissed because of a timeout or a mouse click.
|
||
|
case NIN_BALLOONHIDE:
|
||
|
break;
|
||
|
|
||
|
// Sent when the balloon is dismissed because of a timeout.
|
||
|
case NIN_BALLOONTIMEOUT:
|
||
|
break;
|
||
|
|
||
|
// Sent when the balloon is dismissed because of a mouse click.
|
||
|
case NIN_BALLOONUSERCLICK:
|
||
|
break;
|
||
|
|
||
|
// The version 5.0 Shell sends the associated application a NIN_SELECT message
|
||
|
// if a user selects a notify icon with the mouse and activates it with the ENTER key
|
||
|
|
||
|
case NIN_SELECT:
|
||
|
// intentional fall thru...
|
||
|
|
||
|
// The version 5.0 Shell sends the associated application a NIN_KEYSELECT message
|
||
|
// if a user selects a notify icon with the keyboard and activates it with the space bar or ENTER key
|
||
|
|
||
|
case NIN_KEYSELECT:
|
||
|
// intentional fall thru...
|
||
|
|
||
|
// The version 5.0 Shell sends the associated application a WM_CONTEXTMENU
|
||
|
// If a user requests a notify icon's shortcut menu with the keyboard
|
||
|
case WM_CONTEXTMENU:
|
||
|
// intentional fall thru...
|
||
|
|
||
|
case WM_RBUTTONUP:
|
||
|
{
|
||
|
CMenu menu;
|
||
|
if (!menu.LoadMenu(m_niData.uID))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
CMenu* pSubMenu = menu.GetSubMenu(0);
|
||
|
if (pSubMenu == NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Make chosen menu item the default (bold font)
|
||
|
::SetMenuDefaultItem(pSubMenu->m_hMenu,
|
||
|
m_uDefMenuItemID, m_bDefMenuItemByPos);
|
||
|
|
||
|
// Display the menu at the current mouse location. There's a "bug"
|
||
|
// (Microsoft calls it a feature) in Windows 95 that requires calling
|
||
|
// SetForegroundWindow. To find out more, search for Q135788 in MSDN.
|
||
|
//
|
||
|
CPoint pos;
|
||
|
GetCursorPos(&pos);
|
||
|
::SetForegroundWindow(m_hWndNotify);
|
||
|
|
||
|
::TrackPopupMenu(pSubMenu->m_hMenu, 0, pos.x, pos.y,
|
||
|
0, m_hWndNotify, NULL);
|
||
|
|
||
|
::PostMessage(m_hWndNotify, WM_NULL, 0, 0);
|
||
|
|
||
|
menu.DestroyMenu();
|
||
|
|
||
|
ShellNotify(NIM_SETFOCUS);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_LBUTTONDBLCLK:
|
||
|
{
|
||
|
// double click received, the default action is to execute default menu item
|
||
|
::SetForegroundWindow(m_hWndNotify);
|
||
|
|
||
|
UINT uItem;
|
||
|
if (m_bDefMenuItemByPos)
|
||
|
{
|
||
|
CMenu menu;
|
||
|
if (!menu.LoadMenu(m_niData.uID))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
CMenu* pSubMenu = menu.GetSubMenu(0);
|
||
|
if (pSubMenu == NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uItem = pSubMenu->GetMenuItemID(m_uDefMenuItemID);
|
||
|
|
||
|
menu.DestroyMenu();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uItem = m_uDefMenuItemID;
|
||
|
}
|
||
|
|
||
|
::SendMessage(m_hWndNotify, WM_COMMAND, uItem, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
LRESULT CXTPTrayIcon::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
if (message == m_niData.uCallbackMessage)
|
||
|
{
|
||
|
return OnTrayNotification(wParam, lParam);
|
||
|
}
|
||
|
|
||
|
return CWnd::WindowProc(message, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
// This is called whenever the taskbar is created (eg after explorer crashes
|
||
|
// and restarts. Please note that the WM_TASKBARCREATED message is only passed
|
||
|
// to TOP LEVEL windows (like WM_QUERYNEWPALETTE)
|
||
|
|
||
|
LRESULT CXTPTrayIcon::OnTaskbarCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
|
||
|
{
|
||
|
InstallIconPending();
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
|
||
|
{
|
||
|
CWnd::OnSettingChange(uFlags, lpszSection);
|
||
|
|
||
|
if (uFlags == SPI_SETWORKAREA && m_bShowPending)
|
||
|
{
|
||
|
InstallIconPending();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::InstallIconPending()
|
||
|
{
|
||
|
// Is the icon display pending, and it's not been set as "hidden"?
|
||
|
if (m_bRemoved || m_bHidden)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Reset the flags to what was used at creation
|
||
|
m_niData.uFlags = m_uFlags;
|
||
|
|
||
|
// Try and recreate the icon
|
||
|
m_bShowPending = (ShellNotify(NIM_ADD) == FALSE);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon minimize and restore helper functions
|
||
|
|
||
|
BOOL CALLBACK CXTPTrayIcon::FindTrayWnd(HWND hWnd, LPARAM lParam)
|
||
|
{
|
||
|
TCHAR szClassName[256];
|
||
|
::GetClassName(hWnd, szClassName, 255);
|
||
|
|
||
|
// Did we find the main system tray ? If so, then get its size and keep going
|
||
|
if (_tcscmp(szClassName, _T("TrayNotifyWnd")) == 0)
|
||
|
{
|
||
|
CRect *pRect = (CRect*) lParam;
|
||
|
::GetWindowRect(hWnd, pRect);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Did we find the System Clock ? If so, then adjust the size of the rectangle
|
||
|
// we have and quit (clock will be found after the system tray)
|
||
|
if (_tcscmp(szClassName, _T("TrayClockWClass")) == 0)
|
||
|
{
|
||
|
CRect *pRect = (CRect*)lParam;
|
||
|
CRect rectClock;
|
||
|
::GetWindowRect(hWnd, rectClock);
|
||
|
|
||
|
// if clock is above system tray adjust accordingly
|
||
|
if (rectClock.bottom < pRect->bottom-5) // 10 = random fudge factor.
|
||
|
{
|
||
|
pRect->top = rectClock.bottom;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pRect->right = rectClock.left;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::GetTrayWindowRect(CRect& rect)
|
||
|
{
|
||
|
int cx = 150;
|
||
|
int cy = 30;
|
||
|
|
||
|
HWND hWndTray = ::FindWindow(_T("Shell_TrayWnd"), NULL);
|
||
|
if (hWndTray)
|
||
|
{
|
||
|
::GetWindowRect(hWndTray, &rect);
|
||
|
::EnumChildWindows(hWndTray, FindTrayWnd, (LPARAM)&rect);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// OK, we failed to get the rect from the quick hack. Either explorer isn't
|
||
|
// running or it's a new version of the shell with the window class names
|
||
|
// changed (how dare Microsoft change these undocumented class names!) So, we
|
||
|
// try to find out what side of the screen the taskbar is connected to. We
|
||
|
// know that the system tray is either on the right or the bottom of the
|
||
|
// taskbar, so we can make a good guess at where to minimize to
|
||
|
|
||
|
APPBARDATA ad;
|
||
|
::ZeroMemory(&ad, sizeof(APPBARDATA));
|
||
|
ad.cbSize = sizeof(ad);
|
||
|
|
||
|
if (::SHAppBarMessage(ABM_GETTASKBARPOS, &ad))
|
||
|
{
|
||
|
// We know the edge the taskbar is connected to, so guess the rect of the
|
||
|
// system tray. Use various fudge factor to make it look good
|
||
|
|
||
|
switch (ad.uEdge)
|
||
|
{
|
||
|
case ABE_LEFT:
|
||
|
case ABE_RIGHT:
|
||
|
// We want to minimize to the bottom of the taskbar
|
||
|
rect.top = ad.rc.bottom-100;
|
||
|
rect.bottom = ad.rc.bottom-16;
|
||
|
rect.left = ad.rc.left;
|
||
|
rect.right = ad.rc.right;
|
||
|
break;
|
||
|
|
||
|
case ABE_TOP:
|
||
|
case ABE_BOTTOM:
|
||
|
// We want to minimize to the right of the taskbar
|
||
|
rect.top = ad.rc.top;
|
||
|
rect.bottom = ad.rc.bottom;
|
||
|
rect.left = ad.rc.right-100;
|
||
|
rect.right = ad.rc.right-16;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Blimey, we really aren't in luck. It's possible that a third party shell
|
||
|
// is running instead of explorer. This shell might provide support for the
|
||
|
// system tray, by providing a Shell_TrayWnd window (which receives the
|
||
|
// messages for the icons) So, look for a Shell_TrayWnd window and work out
|
||
|
// the rect from that. Remember that explorer's taskbar is the Shell_TrayWnd,
|
||
|
// and stretches either the width or the height of the screen. We can't rely
|
||
|
// on the 3rd party shell's Shell_TrayWnd doing the same, in fact, we can't
|
||
|
// rely on it being any size. The best we can do is just blindly use the
|
||
|
// window rect, perhaps limiting the width and height to, say 150 square.
|
||
|
// Note that if the 3rd party shell supports the same configuration as
|
||
|
// explorer (the icons hosted in NotifyTrayWnd, which is a child window of
|
||
|
// Shell_TrayWnd), we would already have caught it above
|
||
|
|
||
|
if (hWndTray)
|
||
|
{
|
||
|
::GetWindowRect(hWndTray, &rect);
|
||
|
|
||
|
if (rect.right - rect.left > cx)
|
||
|
{
|
||
|
rect.left = rect.right - cx;
|
||
|
}
|
||
|
if (rect.bottom - rect.top > cy)
|
||
|
{
|
||
|
rect.top = rect.bottom - cy;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// OK. Haven't found a thing. Provide a default rect based on the current work
|
||
|
// area
|
||
|
if (::SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
|
||
|
{
|
||
|
rect.left = rect.right - cx;
|
||
|
rect.top = rect.bottom - cy;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CXTPTrayIcon::CanAnimate() const
|
||
|
{
|
||
|
ANIMATIONINFO ai;
|
||
|
::ZeroMemory(&ai, sizeof(ANIMATIONINFO));
|
||
|
ai.cbSize = sizeof(ANIMATIONINFO);
|
||
|
|
||
|
::SystemParametersInfo(SPI_GETANIMATION,
|
||
|
sizeof(ANIMATIONINFO), &ai, 0);
|
||
|
|
||
|
return ai.iMinAnimate ? true : false;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CXTPTrayIcon minimize and restore functions
|
||
|
|
||
|
bool CXTPTrayIcon::CreateMinimizeWnd(CWnd* pWndApp)
|
||
|
{
|
||
|
// Create the minimize window
|
||
|
if (!::IsWindow(m_wndMinimize.m_hWnd))
|
||
|
{
|
||
|
if (!m_wndMinimize.CreateEx(0, AfxRegisterWndClass(0), _T(""), WS_POPUP,
|
||
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pWndApp->SetParent(&m_wndMinimize);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::MinimizeToTray(CWnd* pWndApp)
|
||
|
{
|
||
|
if (CanAnimate())
|
||
|
{
|
||
|
CRect rectFrom, rectTo;
|
||
|
|
||
|
pWndApp->GetWindowRect(rectFrom);
|
||
|
GetTrayWindowRect(rectTo);
|
||
|
|
||
|
::DrawAnimatedRects(pWndApp->m_hWnd,
|
||
|
IDANI_CAPTION, rectFrom, rectTo);
|
||
|
}
|
||
|
|
||
|
CreateMinimizeWnd(pWndApp);
|
||
|
pWndApp->ShowWindow(SW_HIDE);
|
||
|
}
|
||
|
|
||
|
void CXTPTrayIcon::MaximizeFromTray(CWnd* pWndApp)
|
||
|
{
|
||
|
if (CanAnimate())
|
||
|
{
|
||
|
CRect rectTo;
|
||
|
pWndApp->GetWindowRect(rectTo);
|
||
|
|
||
|
CRect rectFrom;
|
||
|
GetTrayWindowRect(rectFrom);
|
||
|
|
||
|
pWndApp->SetParent(NULL);
|
||
|
::DrawAnimatedRects(pWndApp->m_hWnd,
|
||
|
IDANI_CAPTION, rectFrom, rectTo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pWndApp->SetParent(NULL);
|
||
|
}
|
||
|
|
||
|
pWndApp->SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||
|
|
||
|
// Move focus away and back again to ensure taskbar icon is recreated
|
||
|
if (::IsWindow(m_wndMinimize.m_hWnd))
|
||
|
{
|
||
|
m_wndMinimize.SetActiveWindow();
|
||
|
}
|
||
|
|
||
|
pWndApp->SetActiveWindow();
|
||
|
pWndApp->SetForegroundWindow();
|
||
|
}
|