MainFrm.h
////////////////////////////////////////////////////////////////
// GRAYSUB 1998 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
////////////////
// Standard MFC main frame window
//
class CMainFrame : public CFrameWnd {
public:
virtual ~CMainFrame();
protected:
BOOL m_bEnableSubmenu;
BOOL m_bEnableFoo;
CMainFrame();
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnEnableSubmenu();
afx_msg void OnUpdateEnableSubmenu(CCmdUI *pCmdUI);
afx_msg void OnFoo();
afx_msg void OnUpdateFoo(CCmdUI *pCmdUI);
afx_msg void OnEnableFoo();
afx_msg void OnUpdateEnableFoo(CCmdUI *pCmdUI);
DECLARE_MESSAGE_MAP()
DECLARE_DYNCREATE(CMainFrame)
};
MainFrm.cpp
////////////////////////////////////////////////////////////////
// GRAYSUB 1998 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// GRAYSUB illustrates how to gray a submenu item.
// Compiles with Visual C++ 5.0 or later, under Windows 95.
//
#include "StdAfx.h"
#include "GraySub.h"
#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_ENABLE_SUBMENU, OnEnableSubmenu)
ON_UPDATE_COMMAND_UI(ID_ENABLE_SUBMENU, OnUpdateEnableSubmenu)
ON_COMMAND(ID_FOO, OnFoo)
ON_UPDATE_COMMAND_UI(ID_FOO, OnUpdateFoo)
ON_COMMAND(ID_ENABLE_FOO, OnEnableFoo)
ON_UPDATE_COMMAND_UI(ID_ENABLE_FOO, OnUpdateEnableFoo)
END_MESSAGE_MAP()
CMainFrame::CMainFrame()
{
m_bEnableSubmenu = FALSE;
m_bEnableFoo = FALSE;
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
//////////////////
// Set initial window size to 200 x 200
//
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
BOOL bRet = CFrameWnd::PreCreateWindow(cs);
cs.cx = cs.cy = 200;
return bRet;
}
//////////////////
// Enable/Disable the submenu
//
void CMainFrame::OnEnableSubmenu()
{
m_bEnableSubmenu = !m_bEnableSubmenu;
}
//////////////////
// Set text for File | Enable/Disable Submenu
//
void CMainFrame::OnUpdateEnableSubmenu(CCmdUI *pCmdUI)
{
pCmdUI->SetText(m_bEnableSubmenu ?
_T("&Disable Submenu") : _T("&Enable Submenu"));
}
//////////////////
// Handle File | Submenu | Foo
//
void CMainFrame::OnFoo()
{
AfxMessageBox(_T("Foo to you too."));
}
//////////////////
// Update handler for File | Submenu | Foo handles updating
// of the command and the submenu item itself.
//
void CMainFrame::OnUpdateFoo(CCmdUI *pCmdUI)
{
if (pCmdUI->m_pSubMenu) {
// update submenu item (File | Submenu)
UINT fGray = m_bEnableSubmenu ? 0 : MF_GRAYED;
pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
MF_BYPOSITION|fGray);
} else
// update command item (File | Submenu | Foo)
pCmdUI->Enable(m_bEnableFoo);
}
/////////////////
// Enable/Disable the File | Submenu | Foo command
//
void CMainFrame::OnEnableFoo()
{
m_bEnableFoo = !m_bEnableFoo;
}
/////////////////
// Set text for File | Submenu | Enable/Disable Foo
//
void CMainFrame::OnUpdateEnableFoo(CCmdUI *pCmdUI)
{
pCmdUI->SetText(m_bEnableFoo ?
_T("&Disable Foo") : _T("&Enable Foo"));
}
Figure 4 CTrayIcon
TrayIcon.h
////////////////////////////////////////////////////////////////
// CTrayIcon 1999 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
#ifndef _TRAYICON_H
#define _TRAYICON_H
#include "Subclass.h"
////////////////
// CTrayIcon manages an icon in the Windows 95 system tray.
// The sample program TRAYTEST shows how to use it.
//
class CTrayIcon : public CCmdTarget {
public:
CTrayIcon(UINT uID);
~CTrayIcon();
// Call this to receive tray notifications
void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);
// SetIcon functions. To remove icon, call SetIcon(0)
//
BOOL SetIcon(UINT uID); // main variant you want to use
BOOL SetIcon(HICON hicon, LPCTSTR lpTip);
BOOL SetIcon(LPCTSTR lpResName, LPCTSTR lpTip)
{ return SetIcon(lpResName ?
AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); }
BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCTSTR lpTip)
{ return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); }
// Following is obsolete. CTrayIcon does default handling automatically.
// virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
virtual LRESULT OnTrayNotify(WPARAM uID, LPARAM lEvent);
virtual LRESULT OnTaskBarCreate(WPARAM wp, LPARAM lp);
protected:
NOTIFYICONDATA m_nid; // struct for Shell_NotifyIcon args
// private class used to hook tray notification and taskbarcreated
class CTrayHook : public CSubclassWnd {
private:
CTrayIcon* m_pTrayIcon;
virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
friend CTrayIcon;
};
friend CTrayHook;
CTrayHook m_notifyHook; // trap tray notifications
CTrayHook m_parentHook; // trap taskbarcreated message
DECLARE_DYNAMIC(CTrayIcon)
};
#endif
TrayIcon.cpp
//////////////////////////////////////////////////////////////////
// 1999 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
#include "stdafx.h"
#include "trayicon.h"
#include <afxpriv.h> // for AfxLoadString
#define countof(x) (sizeof(x)/sizeof(x[0]))
//////////////////
// Windows sends this message when the taskbar is created. This can happen
// if it crashes and Windows has to restart it. CTrayIcon responds by
// re-installing its icon.
//
const UINT WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));
IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)
CTrayIcon::CTrayIcon(UINT uID)
{
// Initialize NOTIFYICONDATA
memset(&m_nid, 0 , sizeof(m_nid));
m_nid.cbSize = sizeof(m_nid);
m_nid.uID = uID; // never changes after construction
m_notifyHook.m_pTrayIcon = this; // notification window hook
m_parentHook.m_pTrayIcon = this; // parent window hook
// Use resource string as tip if there is one
AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
}
CTrayIcon::~CTrayIcon()
{
SetIcon(0); // remove icon from system tray
}
//////////////////
// Set notification window. It must be created already.
//
void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
// If the following assert fails, you're probably
// calling me before you created your window. Oops.
ASSERT(pNotifyWnd==NULL || ::IsWindow(pNotifyWnd->GetSafeHwnd()));
m_nid.hWnd = pNotifyWnd->GetSafeHwnd();
ASSERT(uCbMsg==0 || uCbMsg>=WM_USER);
m_nid.uCallbackMessage = uCbMsg;
CWnd* pParentWnd = pNotifyWnd ? pNotifyWnd->GetTopLevelParent() : NULL;
// Install window hooks. Must be different because
// taskbar creation message only goes to top-level parent.
m_notifyHook.HookWindow(pNotifyWnd);
if (pParentWnd!=pNotifyWnd)
m_parentHook.HookWindow(pParentWnd);
}
//////////////////
// This is the main variant for setting the icon.
// Sets both the icon and tooltip from resource ID
// To remove the icon, call SetIcon(0)
//
BOOL CTrayIcon::SetIcon(UINT uID)
{
HICON hicon=NULL;
if (uID) {
AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
hicon = AfxGetApp()->LoadIcon(uID);
}
return SetIcon(hicon, NULL);
}
//////////////////
// Common SetIcon for all overloads.
//
BOOL CTrayIcon::SetIcon(HICON hicon, LPCTSTR lpTip)
{
UINT msg;
m_nid.uFlags = 0;
// Set the icon
if (hicon) {
// Add or replace icon in system tray
msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
m_nid.hIcon = hicon;
m_nid.uFlags |= NIF_ICON;
} else { // remove icon from tray
if (m_nid.hIcon==NULL)
return TRUE; // already deleted
msg = NIM_DELETE;
}
// Use the tip, if any
if (lpTip)
_tcsncpy(m_nid.szTip, lpTip, countof(m_nid.szTip));
if (m_nid.szTip[0])
m_nid.uFlags |= NIF_TIP;
// Use callback if any
if (m_nid.uCallbackMessage && m_nid.hWnd)
m_nid.uFlags |= NIF_MESSAGE;
// Do it
BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
if (msg==NIM_DELETE || !bRet)
m_nid.hIcon = NULL; // failed
return bRet;
}
//////////////////
// Same hook class used for both notification window and top-level
// parent; hook function determines which.
//
LRESULT CTrayIcon::CTrayHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg==m_pTrayIcon->m_nid.uCallbackMessage &&
m_hWnd==m_pTrayIcon->m_nid.hWnd) {
m_pTrayIcon->OnTrayNotify(wp, lp);
} else if (msg==WM_TASKBARCREATED) {
m_pTrayIcon->OnTaskBarCreate(wp, lp);
}
return CSubclassWnd::WindowProc(msg, wp, lp);
}
/////////////////
// Default event handler handles right-menu and doubleclick.
// Override to do something different.
//
LRESULT CTrayIcon::OnTrayNotify(WPARAM wID, LPARAM lEvent)
{
if (wID!=m_nid.uID ||
(lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
return 0;
// If there's a resource menu with the same ID as the icon, use it as
// the right-button popup menu. CTrayIcon will interpret the first
// item in the menu as the default command for WM_LBUTTONDBLCLK
//
CMenu menu;
if (!menu.LoadMenu(m_nid.uID))
return 0;
CMenu* pSubMenu = menu.GetSubMenu(0);
if (!pSubMenu)
return 0;
if (lEvent==WM_RBUTTONUP) {
// Make first menu item the default (bold font)
::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);
// Display the menu at the current mouse location. There's a "bug"
// in Windows 95 that requires calling SetForegroundWindow.
// To find out more, search for Q135788 in MSDN.
//
CPoint mouse;
GetCursorPos(&mouse);
::SetForegroundWindow(m_nid.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0,
m_nid.hWnd, NULL);
} else // double click: execute first menu item
::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);
return 1; // handled
}
//////////////////
// Explorer had to restart the taskbar: add icons again
//
LRESULT CTrayIcon::OnTaskBarCreate(WPARAM wp, LPARAM lp)
{
// Reinstall taskbar icon
HICON hIcon = m_nid.hIcon;
m_nid.hIcon = NULL;
if (hIcon)
SetIcon(hIcon, NULL); // will reuse current tip
return 0;
}