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++

// 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();
}