MainFrm.h
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 4.1 on Windows 95
// See Virgil.cpp
//
#include "Dib.h"
#include "PalHook.h" // to handle palette messages
class CMainFrame : public CFrameWnd {
DECLARE_DYNCREATE(CMainFrame)
public:
CMainFrame();
virtual ~CMainFrame();
protected:
CStatusBar m_wndStatusBar; // status bar
CPalMsgHandler m_palMsgHandler; // handles palette messages
CDib m_dibImage; // image bitmap
CDib m_dibMask; // mask bitmap
BOOL m_bViewMask; // whether to view mask or dib
CString GetWhoItIs(CPoint pt) const; // Helper
// Override for finding tool tip
virtual int OnToolHitTest(CPoint point, TOOLINFO* pTI) const;
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnIndicatorWho(CCmdUI* pCmdUI);
afx_msg void OnGetTooltipText(NMHDR* pNMHDR, LRESULT* plRes);
afx_msg void OnViewMaskOrImage();
afx_msg void OnUpdateViewMaskOrImage(CCmdUI* pCmdUI);
afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
};
MainFrm.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 4.1 on Windows 95
// See Virgil.cpp
//
#include "StdAfx.h"
#include "Virgil.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_WM_PAINT()
ON_UPDATE_COMMAND_UI(ID_INDICATOR_WHO, OnIndicatorWho)
ON_NOTIFY( TTN_NEEDTEXT, 0, OnGetTooltipText)
ON_COMMAND(ID_VIEW_MASK, OnViewMaskOrImage)
ON_UPDATE_COMMAND_UI(ID_VIEW_MASK, OnUpdateViewMaskOrImage)
ON_WM_PALETTECHANGED()
ON_WM_QUERYNEWPALETTE()
ON_WM_GETMINMAXINFO()
END_MESSAGE_MAP()
static UINT indicators[] = {
ID_SEPARATOR, // status line indicator
ID_INDICATOR_WHO // "who" indicator
};
CMainFrame::CMainFrame()
{
m_bViewMask = FALSE;
}
CMainFrame::~CMainFrame()
{
}
/////////////////
// Create main window. In addition to the usual stuff:
//
// - load image and mask bitmaps
// - set window size to be exactly same as bitmap
// - install palette message handler
// - enable tooltips
//
// Note: in order to make drawing simpler, so I don't have to calculate the
// client area each time, I created this window with no toolbar. There are
// no commands to put there anyway.
//
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) {
return -1;
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT))) {
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// Load image bitmap
if (!m_dibImage.Load(AfxGetResourceHandle(), IDB_BITMAP)) {
TRACE("*** Failed to load bitmap\n");
return -1;
}
// Load mask bitmap
if (!m_dibMask.Load(AfxGetResourceHandle(), IDB_BITMAPMASK)) {
TRACE("*** Failed to load mask\n");
return -1;
}
ASSERT(m_dibMask.GetSize() == m_dibImage.GetSize());
// Readjust initial size
MINMAXINFO mmi;
OnGetMinMaxInfo(&mmi);
SetWindowPos(NULL, 0, 0, mmi.ptMaxSize.x, mmi.ptMaxSize.y,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
// Install palette message handler
m_palMsgHandler.Install(this, m_dibImage.GetPalette());
EnableToolTips();
return 0;
}
//////////////////
// Paint function uses CDib to draw either image or mask
//
void CMainFrame::OnPaint()
{
CPaintDC dc(this);
CDib* pDIB = m_bViewMask ? &m_dibMask : &m_dibImage;
pDIB->Draw(dc);
}
////////////////
// Update "who" indicator.
//
void CMainFrame::OnIndicatorWho(CCmdUI* pCmdUI)
{
CPoint pt;
GetCursorPos(&pt); // current mouse pos
ScreenToClient(&pt); // in client coords
pCmdUI->SetText(GetWhoItIs(pt)); // find who and set text
}
//////////////////
// Figure out what "tool"--i.e. person--the mouse is over, if any.
//
int CMainFrame::OnToolHitTest(CPoint pt, TOOLINFO* pTI) const
{
int nHit = -1; // assume none
if (pTI && !(GetWhoItIs(pt).IsEmpty()) ) {
// Need to return a unique value from last time:
// so just return the point as an integer
//
nHit = MAKELONG(pt.x, pt.y);
// setup TOOLINFO structure
pTI->hwnd = m_hWnd;
pTI->uId = nHit;
pTI->rect = CRect(CPoint(pt.x-1,pt.y-1),CSize(2,2));
pTI->lpszText = LPSTR_TEXTCALLBACK;
pTI->uFlags |= TTF_NOTBUTTON; // OR with TTF_ALWAYSTIP to show
// tip even when window not active
}
return nHit;
}
//////////////////
// Get tooltip text. idFrom is whatever I returned above as uID:
// namely, the mouse point coded as integer.
//
void CMainFrame::OnGetTooltipText(NMHDR* pNMHDR, LRESULT* plRes)
{
TOOLTIPTEXT& ttt = *((TOOLTIPTEXT*)pNMHDR);
strncpy(ttt.szText,
GetWhoItIs(CPoint (pNMHDR->idFrom)),
sizeof(ttt.szText));
}
//////////////////
// Main helper to figure out who it is under a given point in the image.
//
CString CMainFrame::GetWhoItIs(CPoint pt) const
{
static struct {
COLORREF color;
LPCTSTR name;
} data [] = {
// The entries in this table must match colors in the mask.
// Each color identifies a person/object in the painting.
//
{ RGB(255, 0, 0), "Virgil" },
{ RGB( 0,255, 0), "Octavia" },
{ RGB( 0, 0,255), "Octavia's Maiden" },
{ RGB( 0, 66, 66), "Unknown Character" },
{ RGB(255,255, 0), "Book 6 of the Aeneid" },
{ RGB(132, 0,132), "Unknown Character" },
{ RGB( 0, 0,132), "Ionic Column" },
{ RGB( 0,132,255), "Unknown Character" },
{ RGB(132, 66, 0), "Pretorian Guard" },
{ RGB( 0,132, 0), "Caesar Augustus" },
{ RGB( 0,132,132), "Drapes" },
{ 0, NULL }
};
// Create memory DC, select mask bitmap into it and call CDC::GetPixel
// to get the color of the corresponding pixel in the mask
//
CDC dc;
dc.CreateCompatibleDC(NULL);
CBitmap * pOldBm = dc.SelectObject((CDib*)&m_dibMask);
COLORREF color = dc.GetPixel(pt); // mask pixel
dc.SelectObject(pOldBm); // restore
// Computee text. If I'm viewing the mask, generate
// "RGB(x,y) = (R,G,B)" before name of character.
//
CString s;
if (m_bViewMask) {
s.Format("RGB(%d,%d)=(%d,%d,%d) ", pt.x, pt.y,
GetRValue(color), GetGValue(color), GetBValue(color));
}
if (color != RGB(255,255,255)) { // white = nothing
// Do linear search through table
for (int i=0; data[i].name; i++) {
if (data[i].color==color)
s += data[i].name;
}
}
return s;
}
//////////////////
// View Mask/View Image command: toggle flag
//
void CMainFrame::OnViewMaskOrImage()
{
m_bViewMask = !m_bViewMask;
Invalidate();
}
void CMainFrame::OnUpdateViewMaskOrImage(CCmdUI* pCmdUI)
{
pCmdUI->SetText(m_bViewMask ? "&Image" : "&Mask");
}
//////////////////
// Force window same size as image
//
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
if (m_dibImage.m_hObject) {
CSize sz = m_dibImage.GetSize(); // size of bitmap
CRect rcWin, rcClient;
GetWindowRect(&rcWin);
GetClientRect(&rcClient);
lpMMI->ptMaxSize = lpMMI->ptMinTrackSize = lpMMI->ptMaxTrackSize =
CPoint(sz.cx + rcWin.Width() - rcClient.Width(),
sz.cy + rcWin.Height()- rcClient.Height());
}
}
Virgil.h
////////////////////////////////////////////////////////////////
// VIRGIL 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// See Virgil.cpp
//
#include "resource.h"
class CMyApp : public CWinApp {
public:
CMyApp();
virtual BOOL InitInstance();
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnAppAbout();
};
Virgil.cpp
////////////////////////////////////////////////////////////////
// VIRGIL 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// VIRGIL illustrates how to implement a bitmap with arbitrarily shaped
// "hot" regions that are detectable by clicking. It also shows how to
// use tooltips in your main window.
//
// Compiles with VC++ 4.1 or later.
#include "stdafx.h"
#include "Virgil.h"
#include "mainfrm.h"
#include "TraceWin.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
END_MESSAGE_MAP()
CMyApp::CMyApp()
{
}
CMyApp theApp;
//////////////////
// Standard initialization. No doc/view.
//
BOOL CMyApp::InitInstance()
{
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
m_pMainWnd = pMainFrame;
return TRUE;
}
//////////////////
// Generic Dib Control is like static,
// but paints DIB with true colors.
//
class CDibCtl : public CStatic {
protected:
DECLARE_DYNAMIC(CDibCtl);
CDib m_dib;
public:
CDibCtl(UINT nID);
CPalette* GetPalette() { return m_dib.GetPalette(); }
DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
};
IMPLEMENT_DYNAMIC(CDibCtl, CStatic);
BEGIN_MESSAGE_MAP(CDibCtl, CStatic)
ON_WM_PAINT()
END_MESSAGE_MAP()
//////////////////
// Constructor: Load DIB from resource
//
CDibCtl::CDibCtl(UINT nID)
{
m_dib.Load(AfxGetResourceHandle(), nID);
}
//////////////////
// Paint the DIB. Should really take into account
// all the stytes here (sunken, raised, etc.)
//
void CDibCtl::OnPaint()
{
CPaintDC dc(this);
m_dib.Draw(dc);
}
//////////////////
// About dialog with DIB in it.
//
class CAboutDlg : public CDialog {
CPalMsgHandler m_palMsgHandler; // handles palette messages
CDibCtl m_ctlDib; // DIB control
public:
CAboutDlg() : CDialog(IDD_ABOUTBOX), m_ctlDib(IDB_BITMAP1) { }
virtual BOOL OnInitDialog();
};
//////////////////
// Dialog created:
// - subclass DIB control
// - install palette handler
//
// The palette handler will realize the DIB's palette in the foreground
// whenever the dialog gains focus, and in the background whenever the
// parent window gets WM_PALETTECHANGED. Only because the CMainFrame also
// has a CPalMsgHandler in it--which it already does to draw the image.
// If your mainframe doesn't have a CPalMsgHandler to draw a bitmap, you must
// add one to propagate palette messages to any child dialogs it may create.
//
BOOL CAboutDlg::OnInitDialog()
{
BOOL bRet = CDialog::OnInitDialog();
m_ctlDib.SubclassDlgItem(IDB_BITMAP1, this);
m_palMsgHandler.Install(this, m_ctlDib.GetPalette());
return bRet;
}
//////////////////
// About command: run the About dialog
//
void CMyApp::OnAppAbout()
{
CAboutDlg().DoModal();
}