Hook.h
#include "resource.h"
class CApp : public CWinApp {
public:
CApp();
virtual BOOL InitInstance();
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
Hook.cpp
//
#include "StdAfx.h"
#include "Hook.h"
#include "MainFrm.h"
#include "TraceWin.h"
.
.
.
BOOL CApp::InitInstance()
{
// Create main frame window (don't use doc/view stuff)
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
m_pMainWnd = pMainFrame;
return TRUE;
}
MainFrm.h
#include "MsgHook.h"
//////////////////
// Msg hook to spy on mouse messages
//
class CKbdMsgHook : public CMsgHook {
DECLARE_DYNAMIC(CKbdMsgHook);
virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
};
//////////////////
// Standard main frame
//
class CMainFrame : public CFrameWnd {
public:
CMouseMsgHook m_mouseMsgHook; // mouse message hook
CKbdMsgHook m_kbdMsgHook; // keyboard message hook
CAllMsgHook m_allMsgHook; // all message hook
CMainFrame();
virtual ~CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnHookKbd();
afx_msg void OnUpdateHookKbd(CCmdUI* pCmdUI);
...
};
MainFrm.cpp
// Note: edited to show only KBD hook
#include "StdAfx.h"
#include "Hook.h"
#include "MainFrm.h"
#include "Debug.h"
IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
•
•
•
ON_COMMAND(ID_HOOK_KBD, OnHookKbd)
ON_UPDATE_COMMAND_UI(ID_HOOK_KBD, OnUpdateHookKbd)
END_MESSAGE_MAP()
•
•
•
////////////////////////////////////////////////////////////////
// Command and UI handlers for hook/unhook commands
//
void CMainFrame::OnHookKbd()
{
m_kbdMsgHook.HookWindow(m_kbdMsgHook.IsHooked() ? NULL : this);
}
void CMainFrame::OnUpdateHookKbd(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_kbdMsgHook.IsHooked());
}
//////////////////
// CKbdMsgHook spies on keyboard messages,
//
LRESULT CKbdMsgHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
if (WM_KEYFIRST <= msg && msg <= WM_KEYLAST) {
TRACE("CKbdMsgHook::%s\n", DbgName(msg));
}
return CMsgHook::WindowProc(msg, wp, lp); // Important!!
}
IMPLEMENT_DYNAMIC(CKbdMsgHook, CMsgHook);
MsgHook.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// CMsgHook is a generic class for hooking another window's messages.
#include "StdAfx.h"
#include "MsgHook.h"
#include "Debug.h"
//////////////////
// The message hook map is derived from CMapPtrToPtr, which associates
// a pointer with another pointer. It maps an HWND to a CMsgHook, like
// the way MFC's internal maps map HWND's to CWnd's. The first hook
// attached to a window is stored in the map; all other hooks for that
// window are then chained via CMsgHook::m_pNext.
//
class CMsgHookMap : private CMapPtrToPtr {
public:
CMsgHookMap();
~CMsgHookMap();
static CMsgHookMap& GetHookMap();
void Add(HWND hwnd, CMsgHook* pMsgHook);
void Remove(CMsgHook* pMsgHook);
void RemoveAll(HWND hwnd);
CMsgHook* Lookup(HWND hwnd);
};
// This trick is used so the hook map isn't
// instantiated until someone actually requests it.
//
#define theHookMap (CMsgHookMap::GetHookMap())
IMPLEMENT_DYNAMIC(CMsgHook, CWnd);
CMsgHook::CMsgHook()
{
m_pNext = NULL;
m_pOldWndProc = NULL;
m_pWndHooked = NULL;
}
CMsgHook::~CMsgHook()
{
ASSERT(m_pWndHooked==NULL); // can't destroy while still hooked!
ASSERT(m_pOldWndProc==NULL);
}
//////////////////
// Hook a window.
// This installs a new window proc that directs messages to the CMsgHook.
// pWnd=NULL to remove.
//
BOOL CMsgHook::HookWindow(CWnd* pWnd)
{
if (pWnd) {
// Hook the window
ASSERT(m_pWndHooked==NULL);
TRACE("%s::HookWindow(%s)\n",
GetRuntimeClass()->m_lpszClassName, DbgName(pWnd));
HWND hwnd = pWnd->m_hWnd;
ASSERT(hwnd && ::IsWindow(hwnd));
theHookMap.Add(hwnd, this); // Add to map of hooks
} else {
// Unhook the window
ASSERT(m_pWndHooked!=NULL);
TRACE("%s::HookWindow(NULL) [unhook 0x%04x]\n",
GetRuntimeClass()->m_lpszClassName, m_pWndHooked->GetSafeHwnd());
theHookMap.Remove(this); // Remove from map
m_pOldWndProc = NULL;
}
m_pWndHooked = pWnd;
return TRUE;
}
//////////////////
// Window proc-like virtual function which specific CMsgHooks will
// override to do stuff. Default passes the message to the next hook;
// the last hook passes the message to the original window.
// You MUST call this at the end of your WindowProc if you want the real
// window to get the message. This is just like CWnd::WindowProc, except that
// a CMsgHook is not a window.
//
LRESULT CMsgHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
ASSERT(m_pOldWndProc);
return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :
::CallWindowProc(m_pOldWndProc, m_pWndHooked->m_hWnd, msg, wp, lp);
}
//////////////////
// Like calling base class WindowProc, but with no args, so individual
// message handlers can do the default thing. Like CWnd::Default
//
LRESULT CMsgHook::Default()
{
// MFC stores current MSG in thread state
MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
// Note: must explicitly call CMsgHook::WindowProc to avoid infinte
// recursion on virtual function
return CMsgHook::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);
}
//////////////////
// Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
// else was there before.)
//
LRESULT CALLBACK
HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
#ifdef _USRDLL
// If this is a DLL, need to set up MFC state
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
// Set up MFC message state just in case anyone wants it
// This is just like AfxCallWindowProc, but we can't use that because
// a CMsgHook is not a CWnd.
//
MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
MSG oldMsg = curMsg; // save for nesting
curMsg.hwnd = hwnd;
curMsg.message = msg;
curMsg.wParam = wp;
curMsg.lParam = lp;
// Get hook object for this window. Get from hook map
CMsgHook* pMsgHook = theHookMap.Lookup(hwnd);
ASSERT(pMsgHook);
LRESULT lr;
if (msg==WM_NCDESTROY) {
// Window is being destroyed: unhook all hooks (for this window)
// and pass msg to orginal window proc
//
WNDPROC wndproc = pMsgHook->m_pOldWndProc;
theHookMap.RemoveAll(hwnd);
lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);
} else {
// pass to msg hook
lr = pMsgHook->WindowProc(msg, wp, lp);
}
curMsg = oldMsg; // pop state
return lr;
}
CMsgHookMap::CMsgHookMap()
{
}
CMsgHookMap::~CMsgHookMap()
{
ASSERT(IsEmpty()); // all hooks should be removed!
}
//////////////////
// Get the one and only global hook map
//
CMsgHookMap& CMsgHookMap::GetHookMap()
{
// By creating theMap here, C++ doesn't instantiate it until/unless
// it's ever used! This is a good trick to use in C++, to
// instantiate/initialize a static object the first time it's used.
//
static CMsgHookMap theMap;
return theMap;
}
/////////////////
// Add hook to map; i.e., associate hook with window
//
void CMsgHookMap::Add(HWND hwnd, CMsgHook* pMsgHook)
{
ASSERT(hwnd && ::IsWindow(hwnd));
// Add to front of list
pMsgHook->m_pNext = Lookup(hwnd);
SetAt(hwnd, pMsgHook);
if (pMsgHook->m_pNext==NULL) {
// If this is the first hook added, subclass the window
pMsgHook->m_pOldWndProc =
(WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)HookWndProc);
} else {
// just copy wndproc from next hook
pMsgHook->m_pOldWndProc = pMsgHook->m_pNext->m_pOldWndProc;
}
ASSERT(pMsgHook->m_pOldWndProc);
}
//////////////////
// Remove hook from map
//
void CMsgHookMap::Remove(CMsgHook* pUnHook)
{
HWND hwnd = pUnHook->m_pWndHooked->GetSafeHwnd();
ASSERT(hwnd && ::IsWindow(hwnd));
CMsgHook* pHook = Lookup(hwnd);
ASSERT(pHook);
if (pHook==pUnHook) {
// hook to remove is the one in the hash table: replace w/next
if (pHook->m_pNext)
SetAt(hwnd, pHook->m_pNext);
else {
// This is the last hook for this window: restore wnd proc
RemoveKey(hwnd);
SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
}
} else {
// Hook to remove is in the middle: just remove from linked list
while (pHook->m_pNext!=pUnHook)
pHook = pHook->m_pNext;
ASSERT(pHook && pHook->m_pNext==pUnHook);
pHook->m_pNext = pUnHook->m_pNext;
}
}
//////////////////
// Remove all the hooks for a window
//
void CMsgHookMap::RemoveAll(HWND hwnd)
{
CMsgHook* pMsgHook;
while ((pMsgHook = Lookup(hwnd))!=NULL)
pMsgHook->HookWindow(NULL); // (unhook)
}
/////////////////
// Find first hook associate with window
//
CMsgHook* CMsgHookMap::Lookup(HWND hwnd)
{
CMsgHook* pFound = NULL;
if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
return NULL;
ASSERT_KINDOF(CMsgHook, pFound);
return pFound;
}
Debug.h
#ifdef _DEBUG
//////////////////
// Implements TRACEFN macro. Don't ever use directly, just use TRACEFN
//
class CTraceFn {
private:
static int nIndent; // current indent level
friend void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...);
public:
CTraceFn() { nIndent++; }
~CTraceFn() { nIndent--; }
};
// NOTE: YOU MUST NOT USE TRACEFN IN A ONE-LINE IF STATEMENT!
// This will fail:
//
// if (foo)
// TRACEFN(...)
//
// Instead, you must enclose the TRACE in squiggle-brackets
//
// if (foo) {
// TRACEFN(...)
// }
//
#define TRACEFN CTraceFn __fooble; TRACE
// Goodies to get names of things.
extern CString sDbgName(CWnd* pWnd); // get name of window
extern CString sDbgName(UINT uMsg); // get name of WM_ message
#ifdef REFIID
struct DBGINTERFACENAME {
const IID* piid; // ptr to GUID
LPCSTR name; // human-readable name of interface
};
// Change this to whatever interfaces you want to track. Default = none
extern DBGINTERFACENAME* _pDbgInterfaceNames;
extern CString sDbgName(REFIID iid); // get name of COM interface
#endif // REFIID
#else // Not _DEBUG
#define sDbgName(x) CString()
#define TRACEFN TRACE
#endif
// Macro casts to LPCTSTR for use with TRACE/printf/CString::Format
#define DbgName(x) (LPCTSTR)sDbgName(x)
Debug.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// General purpose debugging utilities
//
#include "StdAfx.h"
#include "Debug.h"
#include <afxpriv.h> // for MFC WM_ messages
#ifdef _DEBUG
int CTraceFn::nIndent=-1; // current indent level
#define _countof(array) (sizeof(array)/sizeof(array[0]))
////////////////
// These functions are copied from dumpout.cpp in the MFC source,
// with my modification to do indented TRACEing
//
void AFXAPI AfxDump(const CObject* pOb)
{
afxDump << pOb;
}
void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)
{
#ifdef _DEBUG // all AfxTrace output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return;
#endif
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512];
nBuf = _vstprintf(szBuffer, lpszFormat, args);
ASSERT(nBuf < _countof(szBuffer));
// PD: Here are my added lines to do the indenting. Search
// for newlines and insert prefix before each one. Yawn.
//
static BOOL bStartNewLine = TRUE;
char* nextline;
for (char* start = szBuffer; *start; start=nextline+1) {
if (bStartNewLine) {
if ((afxTraceFlags & traceMultiApp) && (AfxGetApp() != NULL))
afxDump << AfxGetApp()->m_pszExeName << ": ";
afxDump << CString(' ',CTraceFn::nIndent);
bStartNewLine = FALSE;
}
nextline = strchr(start, '\n');
if (nextline) {
*nextline = 0; // terminate string at newline
bStartNewLine = TRUE;
}
afxDump << start;
if (!nextline)
break;
afxDump << "\n"; // the one I terminated
}
va_end(args);
}
//////////////////
// Get window name in the form classname[HWND,title]
// Searches all the parents for a window with a title.
//
CString sDbgName(CWnd* pWnd)
{
CString sTitle;
HWND hwnd = pWnd->GetSafeHwnd();
if (hwnd==NULL)
sTitle = "NULL";
else if (!::IsWindow(hwnd))
sTitle = "[bad window]";
else {
sTitle = "[no title]";
for (CWnd* pw = pWnd; pw; pw = pw->GetParent()) {
if (pw->GetWindowTextLength() > 0) {
pw->GetWindowText(sTitle);
break;
}
}
}
CString s;
s.Format("%s[0x%04x,\"%s\"]",
pWnd ? pWnd->GetRuntimeClass()->m_lpszClassName : "NULL",
hwnd, (LPCTSTR)sTitle);
return s;
}
struct {
UINT msg;
LPCTSTR name;
} MsgData[] = {
{ WM_CREATE,_T("WM_CREATE") },
{ WM_DESTROY,_T("WM_DESTROY") },
.
. // (about 220 messages)
.
{ WM_QUEUE_SENTINEL,_T("*WM_QUEUE_SENTINEL") },
{ 0,NULL }
};
////////////////
// This class is basically just an array of 1024 strings,
// the names of each WM_ message. Constructor initializes it.
//
class CWndMsgMap {
static LPCTSTR Names[]; // array of WM_ message names
public:
CWndMsgMap(); // constructor initializes them
CString GetMsgName(UINT msg); // get name of message
};
LPCTSTR CWndMsgMap::Names[WM_USER]; // name of each WM_ message
//////////////////
// Initialize array from sparse data
//
CWndMsgMap::CWndMsgMap()
{
// copy sparse MsgData into table
memset(Names, 0, sizeof(Names));
for (int i=0; MsgData[i].msg; i++)
Names[MsgData[i].msg] = MsgData[i].name;
}
////////////////
// Get the name of a WM_ message
//
CString CWndMsgMap::GetMsgName(UINT msg)
{
CString name;
if (msg>=WM_USER)
name.Format("WM_USER+%d", msg-WM_USER);
else if (Names[msg])
name = Names[msg];
else
name.Format("0x%04x", msg);
return name;
}
//////////////////
// Get name of WM_ message.
//
CString sDbgName(UINT uMsg)
{
static CWndMsgMap wndMsgMap; // instantiate 1st time called
return wndMsgMap.GetMsgName(uMsg);
}
#endif // DEBUG
Figure 5 DIBVIEW
DibView.cpp
#include "StdAfx.h"
#include "DibView.h"
#include "MainFrm.h"
#include "Doc.h"
#include "View.h"
#include "TraceWin.h"
.
.
.
BOOL CApp::InitInstance()
{
#ifdef _MDI
AddDocTemplate(new CMultiDocTemplate(IDR_MYDOCTYPE,
RUNTIME_CLASS(CDIBDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CDIBView)));
#else
AddDocTemplate(new CSingleDocTemplate(IDR_MAINFRAME,
RUNTIME_CLASS(CDIBDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CDIBView)));
#endif
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
#ifdef _MDI
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// Parse command line. Since this is a read-only viewer,
// don't allow FileNew
if (cmdInfo.m_nShellCommand!=CCommandLineInfo::FileNew &&
!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
#else // SDI app
if (!ProcessShellCommand(cmdInfo))
return FALSE;
#endif
return TRUE;
}
MainFrm.h
#include "PalHook.h"
#ifdef _MDI
#define CBaseFrameWnd CMDIFrameWnd
#else
#define CBaseFrameWnd CFrameWnd
#endif
////////////////
// Palette-handling main frame window
//
class CMainFrame : public CBaseFrameWnd {
public:
CMainFrame();
virtual ~CMainFrame();
protected:
DECLARE_DYNCREATE(CMainFrame)
CPalMsgHandler m_palMsgHandler; // handles palette messages
CStatusBar m_wndStatusBar; // status bar
CToolBar m_wndToolBar; // tool (button) bar
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};
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.
//
#include "StdAfx.h"
#include "DibView.h"
#include "MainFrm.h"
.
.
.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
•
•
•
DragAcceptFiles(TRUE);
// Install palette handler.
// Mainframe doesn't draw, only views--so palette is NULL.
//
m_palMsgHandler.Install(this, NULL);
return 0;
}
Doc.h
#include "dib.h"
//////////////////
// Document class just holds a DIB
//
class CDIBDoc : public CDocument {
protected:
DECLARE_DYNCREATE(CDIBDoc)
CDIBDoc();
CDib m_dib; // the DIB
DECLARE_MESSAGE_MAP()
public:
virtual ~CDIBDoc();
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual void DeleteContents();
CDib* GetDIB() { return &m_dib; }
};
Doc.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "StdAfx.h"
#include "DibView.h"
#include "Doc.h"
.
.
.
BOOL CDIBDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
DeleteContents();
return m_dib.Load(lpszPathName);
}
void CDIBDoc::DeleteContents()
{
m_dib.DeleteObject();
}
View.h
#include "PalHook.h"
//////////////////
// DIB view class. A scroll view that draws DIBs and realizes palettes.
//
class CDIBView : public CScrollView {
public:
DECLARE_DYNCREATE(CDIBView)
CDIBView();
virtual ~CDIBView();
CDIBDoc* GetDocument() { return (CDIBDoc*)m_pDocument; }
CDib* GetDIB();
void SetZoom(int iZoom);
protected:
static CFont g_font; // current display font, global for all views
CPalMsgHandler m_palMsgHandler; // handles palette messages
CRect m_rcDIB; // bitmap rectangle (zoomed)
CRect m_rcText; // text rectangle to format BITMAPINFO
int m_iZoom; // current zoom factor
BOOL m_bSized; // first-time sized or not?
BOOL m_bUseDrawDib; // whether to use DrawDib for drawing
void DrawBITMAPHEADER(CDC& dc, CDib* pDIB, CRect& rc, UINT nFormat=0);
void UpdateScrollSizes();
virtual void OnDraw(CDC* pDC);
virtual void OnInitialUpdate();
virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo);
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnZoom(UINT nID);
afx_msg void OnUpdateZoom(CCmdUI* pCmdUI);
afx_msg void OnUpdatePrint(CCmdUI* pCmdUI);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnFontChange(UINT nID);
afx_msg void OnDither();
afx_msg void OnUpdateDither(CCmdUI* pCmdUI);
afx_msg void OnSizeToFit();
};
View.cpp
#include "StdAfx.h"
#include "DibView.h"
#include "Doc.h"
#include "View.h"
#include "Debug.h"
#include "FontUI.h"
#include <afxpriv.h>
const TCHAR SETTINGS[] = _T("Settings");
IMPLEMENT_DYNCREATE(CDIBView, CScrollView)
BEGIN_MESSAGE_MAP(CDIBView, CScrollView)
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDBLCLK()
ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, OnFilePrint)
ON_COMMAND(ID_VIEW_DITHER, OnDither)
ON_COMMAND(ID_VIEW_SIZE_TO_FIT, OnSizeToFit)
ON_COMMAND_RANGE(ID_VIEW_FONT, ID_VIEW_FONT_BIGGER, OnFontChange)
ON_COMMAND_RANGE(ID_ZOOM_4TH, ID_ZOOM_4X, OnZoom)
ON_UPDATE_COMMAND_UI(ID_VIEW_DITHER, OnUpdateDither)
ON_UPDATE_COMMAND_UI_RANGE(ID_ZOOM_4TH, ID_ZOOM_4X, OnUpdateZoom)
ON_UPDATE_COMMAND_UI(ID_FILE_PRINT, OnUpdatePrint)
ON_UPDATE_COMMAND_UI(ID_FILE_PRINT_DIRECT, OnUpdatePrint)
END_MESSAGE_MAP()
CFont CDIBView::g_font;
CDIBView::CDIBView()
{
if (!g_font.m_hObject) {
// Restore font from profile
if (!CFontUI().GetProfileFont(SETTINGS, "Font", g_font)) {
// Use 8pt Courier (monospace) default
g_font.CreatePointFont(100,"Courier");
}
}
m_iZoom=0;
m_bSized=FALSE;
m_bUseDrawDib = AfxGetApp()->GetProfileInt(SETTINGS, "Dither", 1);
}
CDIBView::~CDIBView()
{
CFontUI().WriteProfileFont(SETTINGS, "Font", g_font);
AfxGetApp()->WriteProfileInt(SETTINGS, "Dither", m_bUseDrawDib);
}
CDib* CDIBView::GetDIB()
{
CDIBDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CDib* pDIB = pDoc->GetDIB();
ASSERT_VALID(pDIB);
return pDIB->m_hObject ? pDIB : NULL;
}
//////////////////
// Initial update: set scroll sizes.
//
void CDIBView::OnInitialUpdate()
{
TRACEFN("CDIBView::OnInitialUpdate\n");
CScrollView::OnInitialUpdate();
// Compute size/position of text rectangle
UpdateScrollSizes();
CDib* pDIB = GetDIB();
if (pDIB) {
if (!m_palMsgHandler.IsHooked())
m_palMsgHandler.Install(this, pDIB->GetPalette());
// The following line is required because MFC does not send
// WM_INITIALUPDATE through normal channels. Only realize in
// foreground if I have the focus (could be updating all views
// from OnFontChange)
//
m_palMsgHandler.DoRealizePalette(m_hWnd==::GetFocus());
if (!m_bSized) {
// size window perfectly around frame
GetParentFrame()->RecalcLayout();
OnSizeToFit();
m_bSized=TRUE;
}
}
}
//////////////////
// Compute scroll sizes based on new font/image
//
void CDIBView::UpdateScrollSizes()
{
m_rcDIB.SetRectEmpty();
m_rcText.SetRectEmpty();
CDib* pDIB = GetDIB();
if (!pDIB) {
SetScrollSizes(MM_TEXT, CSize(100,100));
return;
}
// Adjust DIB rectangle by current zoom factor
m_rcDIB = CRect(CPoint(0,0), pDIB->GetSize());
if (m_iZoom>0) {
m_rcDIB.right <<= m_iZoom;
m_rcDIB.bottom <<= m_iZoom;
} else if (m_iZoom<0) {
m_rcDIB.right >>= -m_iZoom;
m_rcDIB.bottom >>= -m_iZoom;
}
// Compute text rectangle
CClientDC dc(this);
int w = m_rcDIB.Width();
int h = m_rcDIB.Height();
m_rcText.SetRect(0, h, w, h);
DrawBITMAPHEADER(dc, pDIB, m_rcText, DT_CALCRECT);
// Total width is max of bitmap, text
if (w > m_rcText.right)
m_rcText.right = w;
SetScrollSizes(MM_TEXT, CSize(m_rcText.right, m_rcText.bottom));
}
//////////////////
// Draw the bitmap. Be careful to specify foreground/background.
// Following bitmap, display fields in BITMAPINFOHEADER
//
void CDIBView::OnDraw(CDC* pDC)
{
CDib* pDIB = GetDIB();
if (pDIB) {
pDIB->Draw(*pDC, &m_rcDIB, NULL, m_bUseDrawDib);
DrawBITMAPHEADER(*pDC, pDIB, m_rcText);
}
}
//////////////////
// Helper fn to draw the formatted BITMAPINFOHEADER text.
// Because Windows is so brain damaged, this requires manually
// outputting each line with TabbedTextOut. DrawText is what I would ideally
// use here, but Windows doesn't let you use DT_CALCRECT and DT_TABSTOP at
// the same time. I need both, because I'm setting a custom tab stop to make
// my text align, but also need to calculate the size of the text in advance
// so I can set my scroll ranges.
//
void CDIBView::DrawBITMAPHEADER(CDC& dc, CDib* pDIB,
CRect& rc, UINT nFormat)
{
DIBSECTION ds;
VERIFY(pDIB->GetObject(sizeof(ds), &ds)==sizeof(ds));
const BITMAPINFOHEADER& bmi = ds.dsBmih;
// Format BITMAPINFOHEADER into a string
CString sCompression="none";
if (bmi.biCompression)
sCompression.Format("0x%04x",bmi.biCompression);
CString buf, text = "BITMAPINFOHEADER:\n";
buf.Format(" biSize\t= %ld\n", bmi.biSize); text += buf;
buf.Format(" biWidth\t= %ld\n", bmi.biWidth); text += buf;
buf.Format(" biHeight\t= %ld\n", bmi.biHeight); text += buf;
buf.Format(" biPlanes\t= %d\n", bmi.biPlanes); text += buf;
buf.Format(" biBitCount\t= %d\n", bmi.biBitCount); text += buf;
buf.Format(" biCompression\t= %s\n", sCompression); text += buf;
buf.Format(" biSizeImage\t= %ld\n", bmi.biSizeImage); text += buf;
buf.Format(" biXPelsPerMeter\t= %ld\n",bmi.biXPelsPerMeter);text += buf;
buf.Format(" biYPelsPerMeter\t= %ld\n",bmi.biYPelsPerMeter);text += buf;
buf.Format(" biClrUsed\t= %ld\n", bmi.biClrUsed); text += buf;
buf.Format(" biClrImportant\t= %ld", bmi.biClrImportant); text += buf;
// Now draw it using current font
CFont *pOldFont = dc.SelectObject(&g_font); // select my font
DRAWTEXTPARAMS dtp;
memset(&dtp,0,sizeof(dtp));
dtp.cbSize = sizeof(dtp); // size of struct
dtp.iTabLength = 20; // avg 20 chars per tab
DrawTextEx(dc, (TCHAR*)(LPCTSTR)text, -1, &rc,
nFormat|DT_EXPANDTABS|DT_TABSTOP, &dtp);
dc.SelectObject(pOldFont);
}
/////////////////////////////////////////////////////////////////////////////
// Standard printing stuff
//
BOOL CDIBView::OnPreparePrinting(CPrintInfo* pInfo)
{
PRINTDLG& pd = pInfo->m_pPD->m_pd;
pd.Flags |= PD_NOPAGENUMS;
if (!DoPreparePrinting(pInfo))
return FALSE;
if (!GetDeviceCaps(pd.hDC, RASTERCAPS) & RC_BITBLT) {
MessageBox("Sorry, this printer does not support bitmaps.",
"DIBVIEW",MB_OK);
return FALSE;
}
return TRUE;
}
//////////////////
// Print the bitmap. This would be the same as drawing it, but must scale
// the size to be true WYSIWYG
//
void CDIBView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
CDib* pDIB = GetDIB();
if (!pDIB)
return;
// save stuff
CRect rcDIB = m_rcDIB;
CRect rcText = m_rcText;
// Convert display resolution to printer resolution
CClientDC dcScreen(this);
CDC& dcPrinter = *pDC;
CSize sz = pDIB->GetSize();
int w = MulDiv(sz.cx, dcPrinter.GetDeviceCaps(LOGPIXELSX),
dcScreen.GetDeviceCaps(LOGPIXELSX));
int h = MulDiv(sz.cy, dcPrinter.GetDeviceCaps(LOGPIXELSY),
dcScreen.GetDeviceCaps(LOGPIXELSY));
// Compute new image, text rectangles
m_rcDIB.SetRect(0,0,w,h); // use new rectangle
m_rcText.SetRect(0, h, w, h); // approx
// Convert font to printer device units
CFontUI fui;
int pts = fui.GetFontPointSize(g_font, dcScreen);
fui.SetFontPointSize(g_font, dcPrinter, pts);
DrawBITMAPHEADER(dcPrinter, pDIB, m_rcText, DT_CALCRECT);
// Draw with new rects and font
OnDraw(pDC);
// Restore everthing to original
fui.SetFontPointSize(g_font, dcScreen, pts);
m_rcDIB = rcDIB;
m_rcText = rcText;
}
void CDIBView::OnUpdatePrint(CCmdUI* pCmdUI)
{
// Only enable printing if I have a DIB (for SDI app)
pCmdUI->Enable(GetDIB()!=NULL);
}
//////////////////
// Dithering command
//
void CDIBView::OnDither()
{
m_bUseDrawDib = ! m_bUseDrawDib;
Invalidate();
}
void CDIBView::OnUpdateDither(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bUseDrawDib);
}
//////////////////
// Double-clicking zooms the image: left button=bigger; right=smaller.
// Double-clicking in the text area invokes Font dialog.
//
void CDIBView::OnLButtonDblClk(UINT nFlags, CPoint pt)
{
pt += GetScrollPosition();
if (m_rcDIB.PtInRect(pt))
SetZoom(m_iZoom + 1);
else if (m_rcText.PtInRect(pt))
OnFontChange(ID_VIEW_FONT);
}
void CDIBView::OnRButtonDblClk(UINT nFlags, CPoint pt)
{
pt += GetScrollPosition();
if (m_rcDIB.PtInRect(pt))
SetZoom(m_iZoom - 1);
else if (m_rcText.PtInRect(pt))
OnFontChange(ID_VIEW_FONT);
}
//////////////////
// Zoom (magnify) command
//
void CDIBView::OnZoom(UINT nID)
{
SetZoom(m_iZoom = nID-ID_ZOOM_NORMAL);
}
void CDIBView::OnUpdateZoom(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(((int)pCmdUI->m_nID)==ID_ZOOM_NORMAL+m_iZoom);
}
void CDIBView::SetZoom(int iZoom)
{
if (-2<=iZoom && iZoom<=2) {
m_iZoom = iZoom;
UpdateScrollSizes();
Invalidate();
}
}
//////////////////
// Szie To Fit command
//
void CDIBView::OnSizeToFit()
{
ResizeParentToFit(FALSE);
// MFC might have grown the window off the screen--I'll fix it
CRect rc, rcMax;
CFrameWnd* pFrame = GetParentFrame();
ASSERT_VALID(pFrame);
pFrame->GetWindowRect(&rc);
CWnd* pGrandParent = pFrame->GetParent();
if (pGrandParent) {
// use top level window as maximum rectangle
pGrandParent->ScreenToClient(&rc);
pGrandParent->GetClientRect(&rcMax);
} else {
// use whole screen as maximum rectangle
rcMax.SetRect(0,0,GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
}
BOOL bTooBig = FALSE;
if (rc.bottom > rcMax.bottom) {
rc.bottom = rcMax.bottom;
rc.right += GetSystemMetrics(SM_CXVSCROLL) +
GetSystemMetrics(SM_CXBORDER);
bTooBig = TRUE;
}
if (rc.right > rcMax.right) {
rc.right = rcMax.right;
bTooBig = TRUE;
}
if (bTooBig) {
pFrame->SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(),
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
}
}
////////////////////////////////////////////////////////////////
// Handle font change (bigger/smaller/dialog)
//
void CDIBView::OnFontChange(UINT nID)
{
CFontUI fui;
if (fui.OnChangeFont(g_font,
nID==ID_VIEW_FONT_BIGGER ? 1 : nID==ID_VIEW_FONT_SMALLER ? -1 : 0,
this,
CF_SCREENFONTS|CF_FORCEFONTEXIST)) {
// For all views to recompute scroll sizes and repaint
GetTopLevelFrame()->SendMessageToDescendants(WM_INITIALUPDATE);
}
}
Dib.h
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// See dib.cpp
// global functions for ordinary CBitmap too
//
extern CSize GetBitmapSize(CBitmap* pBitmap);
extern BOOL DrawBitmap(CDC& dc, CBitmap* pBitmap,
const CRect* rcDst=NULL, const CRect* rcSrc=NULL);
////////////////
// CDib implements Device Independent Bitmaps as a form of CBitmap.
//
class CDib : public CBitmap {
protected:
DECLARE_DYNAMIC(CDib)
BITMAP m_bm; // stored for speed
CPalette m_pal; // palette
HDRAWDIB m_hdd; // for DrawDib
public:
CDib();
~CDib();
CSize GetSize() { return CSize(m_bm.bmWidth, m_bm.bmHeight); }
BOOL Attach(HGDIOBJ hbm);
BOOL Load(LPCTSTR szPathName);
BOOL Load(HINSTANCE hInst, LPCTSTR lpResourceName);
BOOL Load(HINSTANCE hInst, UINT uID)
{ return Load(hInst, MAKEINTRESOURCE(uID)); }
// Universal Draw function can use DrawDib or not.
BOOL Draw(CDC& dc, const CRect* rcDst=NULL, const CRect* rcSrc=NULL,
BOOL bUseDrawDib=TRUE, CPalette* pPal=NULL, BOOL bForeground=FALSE);
BOOL DeleteObject();
BOOL CreatePalette(CPalette& pal);
CPalette* GetPalette() { return &m_pal; }
UINT GetColorTable(RGBQUAD* colorTab, UINT nColors);
};
Dib.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
//
// CDib - Device Independent Bitmap.
// This implementation draws bitmaps using normal Win32 API functions,
// not DrawDib. CDib is derived from CBitmap, so you can use it with
// any other MFC functions that use bitmaps.
//
#include "StdAfx.h"
#include "Dib.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const int MAXPALCOLORS = 256;
IMPLEMENT_DYNAMIC(CDib, CObject)
CDib::CDib()
{
memset(&m_bm, 0, sizeof(m_bm));
m_hdd = NULL;
}
CDib::~CDib()
{
DeleteObject();
}
//////////////////
// Delete Object. Delete DIB and palette.
//
BOOL CDib::DeleteObject()
{
m_pal.DeleteObject();
if (m_hdd) {
DrawDibClose(m_hdd);
m_hdd = NULL;
}
memset(&m_bm, 0, sizeof(m_bm));
return CBitmap::DeleteObject();
}
//////////////////
// Read DIB from file.
//
BOOL CDib::Load(LPCTSTR lpszPathName)
{
return Attach(::LoadImage(NULL, lpszPathName, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE));
}
//////////////////
// Load bitmap resource. Never tested.
//
BOOL CDib::Load(HINSTANCE hInst, LPCTSTR lpResourceName)
{
return Attach(::LoadImage(hInst, lpResourceName, IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION | LR_DEFAULTSIZE));
}
//////////////////
// Attach is just like the CGdiObject version,
// except it also creates the palette
//
BOOL CDib::Attach(HGDIOBJ hbm)
{
if (CBitmap::Attach(hbm)) {
if (!GetBitmap(&m_bm)) // load BITMAP for speed
return FALSE;
m_pal.DeleteObject(); // in case one is already there
return CreatePalette(m_pal); // create palette
}
return FALSE;
}
//////////////////
// Get size (width, height) of bitmap.
// extern fn works for ordinary CBitmap objects.
//
CSize GetBitmapSize(CBitmap* pBitmap)
{
BITMAP bm;
return pBitmap->GetBitmap(&bm) ?
CSize(bm.bmWidth, bm.bmHeight) : CSize(0,0);
}
//////////////////
// You can use this static function to draw ordinary
// CBitmaps as well as CDibs
//
BOOL DrawBitmap(CDC& dc, CBitmap* pBitmap,
const CRect* rcDst, const CRect* rcSrc)
{
// Compute rectangles where NULL specified
CRect rc;
if (!rcSrc) {
// if no source rect, use whole bitmap
rc = CRect(CPoint(0,0), GetBitmapSize(pBitmap));
rcSrc = &rc;
}
if (!rcDst) {
// if no destination rect, use source
rcDst=rcSrc;
}
// Create memory DC
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap* pOldBm = memdc.SelectObject(pBitmap);
// Blast bits from memory DC to target DC.
// Use StretchBlt if size is different.
//
BOOL bRet = FALSE;
if (rcDst->Size()==rcSrc->Size()) {
bRet = dc.BitBlt(rcDst->left, rcDst->top,
rcDst->Width(), rcDst->Height(),
&memdc, rcSrc->left, rcSrc->top, SRCCOPY);
} else {
dc.SetStretchBltMode(COLORONCOLOR);
bRet = dc.StretchBlt(rcDst->left, rcDst->top, rcDst->Width(),
rcDst->Height(), &memdc, rcSrc->left, rcSrc->top, rcSrc->Width(),
rcSrc->Height(), SRCCOPY);
}
memdc.SelectObject(pOldBm);
return bRet;
}
////////////////////////////////////////////////////////////////
// Draw DIB on caller's DC. Does stretching from source to destination
// rectangles. Generally, you can let the following default to zero/NULL:
//
// bUseDrawDib = whether to use use DrawDib, default TRUE
// pPal = palette, default=NULL, (use DIB's palette)
// bForeground = realize in foreground (default FALSE)
//
// If you are handling palette messages, you should use bForeground=FALSE,
// since you will realize the foreground palette in WM_QUERYNEWPALETTE.
//
BOOL CDib::Draw(CDC& dc, const CRect* rcDst, const CRect* rcSrc,
BOOL bUseDrawDib, CPalette* pPal, BOOL bForeground)
{
if (!m_hObject)
return FALSE;
// Select, realize palette
if (pPal==NULL) // no palette specified:
pPal = GetPalette(); // use default
CPalette* pOldPal = dc.SelectPalette(pPal, !bForeground);
dc.RealizePalette();
BOOL bRet = FALSE;
if (bUseDrawDib) {
// Compute rectangles where NULL specified
//
CRect rc(0,0,-1,-1); // default for DrawDibDraw
if (!rcSrc)
rcSrc = &rc;
if (!rcDst)
rcDst=rcSrc;
if (!m_hdd)
VERIFY(m_hdd = DrawDibOpen());
// Get BITMAPINFOHEADER/color table. I copy into stack object each time.
// This doesn't seem to slow things down visibly.
//
DIBSECTION ds;
VERIFY(GetObject(sizeof(ds), &ds)==sizeof(ds));
char buf[sizeof(BITMAPINFOHEADER) + MAXPALCOLORS*sizeof(RGBQUAD)];
BITMAPINFOHEADER& bmih = *(BITMAPINFOHEADER*)buf;
RGBQUAD* colors = (RGBQUAD*)(&bmih+1);
memcpy(&bmih, &ds.dsBmih, sizeof(bmih));
GetColorTable(colors, MAXPALCOLORS);
// Let DrawDib do the work!
bRet = DrawDibDraw(m_hdd, dc,
rcDst->left, rcDst->top, rcDst->Width(), rcDst->Height(),
&bmih, // ptr to BITMAPINFOHEADER + colors
m_bm.bmBits, // bits in memory
rcSrc->left, rcSrc->top, rcSrc->Width(), rcSrc->Height(),
bForeground ? 0 : DDF_BACKGROUNDPAL);
} else {
// use normal draw function
bRet = DrawBitmap(dc, this, rcDst, rcSrc);
}
if (pOldPal)
dc.SelectPalette(pOldPal, TRUE);
return bRet;
}
#define PALVERSION 0x300 // magic number for LOGPALETTE
//////////////////
// Create the palette. Use halftone palette for hi-color bitmaps.
//
BOOL CDib::CreatePalette(CPalette& pal)
{
// should not already have palette
ASSERT(pal.m_hObject==NULL);
BOOL bRet = FALSE;
RGBQUAD* colors = new RGBQUAD[MAXPALCOLORS];
UINT nColors = GetColorTable(colors, MAXPALCOLORS);
if (nColors > 0) {
// Allocate memory for logical palette
int len = sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * nColors;
LOGPALETTE* pLogPal = (LOGPALETTE*)new char[len];
if (!pLogPal)
return NULL;
// set version and number of palette entries
pLogPal->palVersion = PALVERSION;
pLogPal->palNumEntries = nColors;
// copy color entries
for (UINT i = 0; i < nColors; i++) {
pLogPal->palPalEntry[i].peRed = colors[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = colors[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = colors[i].rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
}
// create the palette and destroy LOGPAL
bRet = pal.CreatePalette(pLogPal);
delete [] (char*)pLogPal;
} else {
CWindowDC dcScreen(NULL);
bRet = pal.CreateHalftonePalette(&dcScreen);
}
delete colors;
return bRet;
}
//////////////////
// Helper to get color table. Does all the mem DC voodoo.
//
UINT CDib::GetColorTable(RGBQUAD* colorTab, UINT nColors)
{
CWindowDC dcScreen(NULL);
CDC memdc;
memdc.CreateCompatibleDC(&dcScreen);
CBitmap* pOldBm = memdc.SelectObject(this);
nColors = GetDIBColorTable(memdc, 0, nColors, colorTab);
memdc.SelectObject(pOldBm);
return nColors;
}
FontUI.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
//
// CFontUI handles the user interface for changing font sizes, as well
// as saving/restoring font info in the application profile.
//
#include "StdAfx.h"
#include "FontUI.h"
CFontUI::CFontUI()
{
m_nFontPtSizeMin = 4;
m_nFontPtSizeMax = 120;
}
CFontUI::~CFontUI()
{
}
//////////////////
// Get font point size. Convert device units to points.
// There are 72 points per inch.
//
int CFontUI::GetFontPointSize(CFont& font, CDC& dc)
{
LOGFONT lf;
font.GetLogFont(&lf);
return MulDiv(-lf.lfHeight, 72, dc.GetDeviceCaps(LOGPIXELSY));
}
//////////////////
// Set font point size. Convert points to device units
// There are 72 points per inch.
//
BOOL CFontUI::SetFontPointSize(CFont& font, CDC& dc, int pts)
{
LOGFONT lf;
font.GetLogFont(&lf);
lf.lfHeight = MulDiv(-pts, dc.GetDeviceCaps(LOGPIXELSY), 72);
font.DeleteObject();
return font.CreateFontIndirect(&lf);
}
//////////////////
// Main UI function.
// op = 0 ==> run common font dialog
// op < 0 ==> font size smaller
// op < 0 ==> font size bigger
//
// Returns BOOL, whether changed or not, and CFont has new font.
//
BOOL CFontUI::OnChangeFont(CFont& font, int op, CWnd* pWnd, DWORD dwFlags)
{
ASSERT(font.m_hObject);
if (op==0) {
// Run common font dialog
LOGFONT logfont;
font.GetLogFont(&logfont);
CFontDialog dlg(&logfont, dwFlags, NULL, pWnd);
dlg.m_cf.nSizeMin = m_nFontPtSizeMin;
dlg.m_cf.nSizeMax = m_nFontPtSizeMax;
if (dlg.DoModal() != IDOK)
return FALSE;
// Change the font
font.DeleteObject();
return font.CreateFontIndirect(&logfont);
}
// Grow or shrink
CWindowDC dc(NULL); // use screen DC
int pts = GetFontPointSize(font, dc); // get point size
pts = GrowFontSize(pts, op); // grow (or shrink)
if (pts < m_nFontPtSizeMin || pts > m_nFontPtSizeMax)
return FALSE;
return SetFontPointSize(font, dc, pts);
}
//////////////////
// Increment or decrement font point size based on current size.
// Algorithm:
// If point size is
// <= 12 incr = 1 pts
// 10..32 incr = 2 pts
// 32..48 incr = 4 pts
// >= 48 incr = 8 pts
// Derived classes can override this virtual function to change this.
// Return zero to disallow changing size.
//
int CFontUI::GrowFontSize(int ptSize, int dir)
{
int incr = ptSize <= 12 ? 1 : ptSize < 32 ? 2 : ptSize < 48 ? 4 : 8;
ptSize += dir>0 ? incr : -incr;;
return ptSize;
}
//////////////////
// Create font from info in application profile. Reads info in the form
// facename,ptsize,weight,italic
//
BOOL CFontUI::GetProfileFont(LPCTSTR lpszKey, LPCTSTR lpszVal, CFont& font,
CDC* pDC)
{
CWinApp *pApp = AfxGetApp();
ASSERT_VALID(pApp);
CString s = pApp->GetProfileString(lpszKey, lpszVal);
if (s.IsEmpty())
return FALSE;
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfCharSet = DEFAULT_CHARSET;
int bItalic;
int iPtSize;
// scanf is overkill, but I'm lazy
if (sscanf((LPCTSTR)s, "%[a-zA-Z ],%d,%d,%d",
lf.lfFaceName, &iPtSize, &lf.lfWeight, &bItalic) != 4)
return FALSE;
lf.lfHeight = MulDiv(-iPtSize, // convert ptsize to logical units
::GetDeviceCaps(pDC ? pDC->m_hDC : ::GetDC(NULL), LOGPIXELSY), 72);
lf.lfItalic = bItalic; // because lf.lfItalic is a BYTE
font.DeleteObject(); // bye
return font.CreateFontIndirect(&lf);
}
//////////////////
// Write font to app profile in the form "facename,ptsize,weight,italic"
//
BOOL CFontUI::WriteProfileFont(LPCTSTR lpszKey, LPCTSTR lpszVal, CFont& font,
CDC* pDC)
{
CWinApp *pApp = AfxGetApp();
ASSERT_VALID(pApp);
LOGFONT lf;
font.GetLogFont(&lf);
int iPtSize = MulDiv(-lf.lfHeight, 72,
::GetDeviceCaps(pDC ? pDC->m_hDC : ::GetDC(NULL), LOGPIXELSY));
CString s;
s.Format("%s,%d,%d,%d", lf.lfFaceName, iPtSize, lf.lfWeight, lf.lfItalic);
return pApp->WriteProfileString(lpszKey, lpszVal, s);
}
MsgHook.h
Same as for HOOKMsgHook.cpp
Same as for HOOKPalHook.h
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#ifndef _PALMSGHOOK_H
#define _PALMSGHOOK_H
#include "MsgHook.h"
//////////////////
// Generic palette message handler makes handling palette messages easy.
// To use:
//
// * Instaniate a CPalMsgHandler in your main frame and
// every CWnd class that needs to realize palettes (e.g., your view).
// * Call Install to install.
// * Call DoRealizePalette(TRUE) from your view's OnInitialUpdate fn.
//
class CPalMsgHandler : public CMsgHook {
protected:
CPalette* m_pPalette; // ptr to palette
DECLARE_DYNAMIC(CPalMsgHandler);
// These are similar to, but NOT the same as the equivalent CWnd fns.
// Rarely, if ever need to override.
//
virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
virtual void OnPaletteChanged(CWnd* pFocusWnd);
virtual BOOL OnQueryNewPalette();
virtual void OnSetFocus(CWnd* pOldWnd);
// Override this if you realize your palette some other way
// (not by having a ptr to a CPalette).
//
virtual int DoRealizePalette(BOOL bForeground);
public:
CPalMsgHandler();
~CPalMsgHandler();
// Get/Set palette obj
CPalette* GetPalette() { return m_pPalette; }
void SetPalette(CPalette* pPal) { m_pPalette = pPal; }
// Call this to install the palette handler
BOOL Install(CWnd* pWnd, CPalette* pPal) {
m_pPalette = pPal;
return HookWindow(pWnd);
}
};
#endif
PalHook.cpp
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
//
#include "StdAfx.h"
#include "PalHook.h"
#include "Debug.h"
IMPLEMENT_DYNAMIC(CPalMsgHandler, CMsgHook);
CPalMsgHandler::CPalMsgHandler()
{
m_pPalette = NULL;
}
CPalMsgHandler::~CPalMsgHandler()
{
}
//////////////////
// Message handler handles palette-related messages
//
LRESULT CPalMsgHandler::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
ASSERT_VALID(m_pWndHooked);
switch (msg) {
case WM_PALETTECHANGED:
OnPaletteChanged(CWnd::FromHandle((HWND)wp));
return 0;
case WM_QUERYNEWPALETTE:
return OnQueryNewPalette();
case WM_SETFOCUS:
OnSetFocus(CWnd::FromHandle((HWND)wp));
return 0;
}
return CMsgHook::WindowProc(msg, wp, lp);
}
//////////////////
// Handle WM_PALETTECHANGED
//
void CPalMsgHandler::OnPaletteChanged(CWnd* pFocusWnd)
{
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
TRACEFN("CPalMsgHandler::OnPaletteChanged for %s [from %s]\n",
DbgName(&wnd), DbgName(pFocusWnd));
if (pFocusWnd->GetSafeHwnd() != wnd.m_hWnd) {
if (DoRealizePalette(FALSE)==0) {
if (wnd.GetParent()==NULL) {
// I'm the top-level frame: Broadcast to children
// (only MFC permanent CWnd's!)
//
const MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
wnd.SendMessageToDescendants(WM_PALETTECHANGED,
curMsg.wParam, curMsg.lParam);
}
}
} else {
// I'm the window that triggered the WM_PALETTECHANGED
// in the first place: ignore it
//
TRACE("[It's me, don't realize palette.]\n");
}
}
//////////////////
// Handle WM_QUERYNEWPALETTE
//
BOOL CPalMsgHandler::OnQueryNewPalette()
{
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
TRACEFN("CPalMsgHandler::OnQueryNewPalette for %s\n", DbgName(&wnd));
if (DoRealizePalette(TRUE)==0) { // realize in foreground
// No colors changed: if this is the top-level frame,
// give active view a chance to realize itself
//
if (wnd.GetParent()==NULL) {
ASSERT_KINDOF(CFrameWnd, &wnd);
CWnd* pView = ((CFrameWnd&)wnd).GetActiveFrame()->GetActiveView();
if (pView)
pView->SendMessage(WM_QUERYNEWPALETTE);
}
}
return TRUE;
}
//////////////////
// Handle WM_SETFOCUS
//
void CPalMsgHandler::OnSetFocus(CWnd* pOldWnd)
{
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
TRACEFN("CPalMsgHandler::OnSetFocus for %s\n", DbgName(&wnd));
wnd.SetForegroundWindow(); // Windows likes this
DoRealizePalette(TRUE); // realize in foreground
Default(); // let app handle focus message too
}
/////////////////
// Function to actually realize the palette.
// Override this to do different kind of palette realization; e.g.,
// DrawDib instead of setting the CPalette.
//
int CPalMsgHandler::DoRealizePalette(BOOL bForeground)
{
if (!m_pPalette || !m_pPalette->m_hObject)
return 0;
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
TRACEFN("CPalMsgHandler::DoRealizePalette(%s) for %s\n",
bForeground ? "foreground" : "background", DbgName(&wnd));
CClientDC dc(&wnd);
CPalette* pOldPal = dc.SelectPalette(m_pPalette, !bForeground);
int nColorsChanged = dc.RealizePalette();
if (pOldPal)
dc.SelectPalette(pOldPal, TRUE);
if (nColorsChanged > 0)
wnd.Invalidate(FALSE); // repaint
TRACE("[%d colors changed]\n", nColorsChanged);
return nColorsChanged;
}
Debug.h
Same as for DebugDebug.cpp
Same as for Debug
Figure 12 MFC Goodies Described in the Article
|
DrawBitmap A function to draw a CBitmap on any device context, possibly with stretching. [Dib.h, Dib.cpp] CDib A class to display device-independent bitmaps (DIBs). Uses Win32 API and Video for Windows DrawDib for dithering. [Dib.h, Dib.cpp] TRACEFN A macro for doing intending TRACE diagnostics. [Debug.h, Debug.cpp] DbgName, LpDbgName Debugging functions to get the name of a window, WM_ message, or COM interface. [Debug.h, Debug.cpp] CMsgHook A class to do Windows-style subclassing in MFC. Lets you subclass the same window any number of times. [MsgHook.h, MsgHook.cpp] CPalMsgHandler A CMsgHook that handles palette messages automatically. Can be used in any app. [PalHook.h, PalHook.cpp] CFontUI A class for managing the user interface to a font. Has easy functions to increment/decrement the point size, run the common font dialog, and read/write a font spec to the application profile. [FontUI.h, FontUI.cpp] HOOK A program that demonstrates the use of CMsgHook. DIBVIEW A program for viewing DIBs. Comes in SDI or MDI flavors and uses all the above goodies. |