There’s no leaving here; ask, I’m an ear

There?s a great deal of existing rendering code built on old versions of Microsoft’s Foundation Class (MFC) model. In the old object model, you had a single CFrameWnd, a single CDocument, and one or more CView classes that were owned by the CFrameWnd class. These objects share message routing responsibilities with one another, and a great deal of code has been written with these assumptions.

Fast forward twenty or so years, and these old assumptions, upon which most of MFC was originally architected, are incompatible with the complexity of modern user interface design, and even a good deal of modern-day MFC itself. There can be multiple documents, controls floating and tabbed which may or may not be children of the CFrameWnd.

The funky new display and interface features like CDockablePane and CMFCToolBar don’t know anything about the CView class, and they don’t inherit from CView. It’s hard to upgrade an older application written based on the old MFC document-view model to dockable panes.

It?s a bit of work, but here’s one way to accomplish this without relying on proprietary libraries. The basic strategy we use is to change the old CView-based classes to be a subclass of CDockablePane, and then make the new class support enough of the major CView features in order to handle update rendering, commands, OLE drag and drop, and user-interface updates via the old interfaces in your old class.

In order to convert your own subclass of CView into a CDockablePane subclass, here’s what you need to do:

1. Create a new subclass of CDockablePane (we’ll call it CYourDockablePane) from which all your old CViews will inherit.
2. Implement update message handling and OLE support from your CYourDockablePane subclass.
3. Subclass COleDropTarget into a new class which we’ll call CYourOleDropTarget in order to handle OLE drag-and-drop in the manner that CView does.
4. Add features to your C*FrameWnd* class to support window updating and command message routing to the new panes.

 


 

YourDockablePane.h:

//------------------------------------------------------------------
#pragma once

#include "YourOleDropTarget.h"

class CYourDockablePane : public CDockablePane
{
	DECLARE_DYNCREATE(CYourDockablePane)

public:
	CYourDockablePane();
	virtual ~CYourDockablePane();

	// These are needed to get around OnUpdate()?s protected status
	virtual BOOL ReceiveNotification(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
	virtual void OnUpdate( CView *pSender, LPARAM lHint, CObject *pHint );

	// Simulate CView OLE drag and drop effects
	virtual DROPEFFECT	OnDragEnter( COleDataObject *pDataObject, DWORD dwKeyState, CPoint point );
	virtual void		OnDragLeave( void );
	virtual DROPEFFECT	OnDragOver( COleDataObject *pDataObject, DWORD dwKeyState, CPoint point );
	virtual DROPEFFECT	OnDropEx( COleDataObject *pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point );
	virtual DROPEFFECT	OnDragScroll( DWORD dwKeyState, CPoint point);
	virtual BOOL        OnDrop( COleDataObject* pDataObject,	DROPEFFECT dropEffect, CPoint point);

protected:
	DECLARE_MESSAGE_MAP()

	// View classes know how to do this
	virtual CYourDocument *GetDocument(void) const;
	CYourOleDropTarget	m_OleDropTarget;

public:

};



YourDockablePane.cpp:
/---------------------------------------------------------------------
// YourDockablePane.cpp : implementation file

#include "stdafx.h"
#include "YourDockablePane.h"

IMPLEMENT_DYNCREATE(CYourDockablePane, CDockablePane)

CYourDockablePane::CYourDockablePane()
{

}

CYourDockablePane::~CYourDockablePane()
{
}


BEGIN_MESSAGE_MAP(CYourDockablePane, CDockablePane)
END_MESSAGE_MAP()

CYourDocument * CYourDockablePane::GetDocument( void ) const
{
	CDocTemplate* pDocTemplate;
	POSITION pos;
	pos = AfxGetApp()->GetFirstDocTemplatePosition();
	pDocTemplate = AfxGetApp()->GetNextDocTemplate(pos);
	pos = pDocTemplate->GetFirstDocPosition();
	return (CYourDocument*) pDocTemplate->GetNextDoc(pos);
}

BOOL CYourDockablePane::ReceiveNotification( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
	return OnNotify( wParam, lParam, pResult );
}

DROPEFFECT CYourDockablePane::OnDragEnter( COleDataObject *pDataObject, DWORD dwKeyState, CPoint point )
{
	return DROPEFFECT_NONE;
}

void CYourDockablePane::OnDragLeave( void )
{

}

DROPEFFECT CYourDockablePane::OnDragOver( COleDataObject *pDataObject, DWORD dwKeyState, CPoint point )
{
	return DROPEFFECT_NONE;
}

DROPEFFECT CYourDockablePane::OnDropEx( COleDataObject *pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point )
{
	return DROPEFFECT_NONE;
}

DROPEFFECT CYourDockablePane::OnDragScroll( DWORD dwKeyState, CPoint point )
{
	return DROPEFFECT_NONE;
}

BOOL CYourDockablePane::OnDrop( COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point )
{
	return false;
}

void CYourDockablePane::OnUpdate( CView *pSender, LPARAM lHint, CObject *pHint )
{

}



YourOleDropTarget.h:
/------------------------------------------------------------------------
#pragma once

class CYourDockablePane;

// CYourOleDropTarget command target

class CYourOleDropTarget : public COleDropTarget
{
	DECLARE_DYNAMIC(CYourOleDropTarget)

public:
	CYourOleDropTarget();
	virtual ~CYourOleDropTarget();
	BOOL Register(CWnd* pWnd);

protected:
	CYourDockablePane *m_pPane;
	DECLARE_MESSAGE_MAP()
public:
	virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject,
		DWORD dwKeyState, CPoint point);
	virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject,
		DWORD dwKeyState, CPoint point);
	virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
		DROPEFFECT dropEffect, CPoint point);
	virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject,
		DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
	virtual void OnDragLeave(CWnd* pWnd);
	virtual DROPEFFECT OnDragScroll(CWnd* pWnd, DWORD dwKeyState,
		CPoint point);

};



YourOleDropTarget.cpp:
/--------------------------------------------------------------
// YourOleDropTarget.cpp : implementation file
//

#include "stdafx.h"
#include "YourDockablePane.h"
#include "YourOleDropTarget.h"

// CYourOleDropTarget

IMPLEMENT_DYNAMIC(CYourOleDropTarget, COleDropTarget)


CYourOleDropTarget::CYourOleDropTarget() :
	m_pPane( NULL )
{
}

CYourOleDropTarget::~CYourOleDropTarget()
{
}

BEGIN_MESSAGE_MAP(CYourOleDropTarget, COleDropTarget)
END_MESSAGE_MAP()

BOOL CYourOleDropTarget::Register( CWnd* pWnd )
{
	m_pPane = dynamic_cast< CYourDockablePane * >( pWnd );
	return COleDropTarget::Register( pWnd );
}


// CYourOleDropTarget message handlers

DROPEFFECT CYourOleDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	return m_pPane->OnDragEnter( pDataObject, dwKeyState, point);
}

void CYourOleDropTarget::OnDragLeave(CWnd* pWnd)
{
	m_pPane->OnDragLeave();
}

DROPEFFECT CYourOleDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	return m_pPane->OnDragOver( pDataObject, dwKeyState, point);
}

DROPEFFECT CYourOleDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point)
{
	return m_pPane->OnDragScroll( dwKeyState, point);
}

BOOL CYourOleDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
	return m_pPane->OnDrop( pDataObject, dropEffect, point);
}

DROPEFFECT CYourOleDropTarget::OnDropEx( CWnd *pWnd, 
							COleDataObject* pDataObject,
							DROPEFFECT dropDefault,
							DROPEFFECT dropList,
							CPoint point 
							)
{
	DROPEFFECT effect;
	
	effect = m_pPane->OnDropEx( pDataObject, dropDefault, dropList, point );

	if ( effect == DROPEFFECT_NONE )
		return m_pPane->OnDrop( pDataObject, dropDefault, point );

	return effect;
}



Add this snippet to the definition file (the .h file) of your application?s CFrameWnd or CFrameWndEx subclass:
. . .
		typedef list< CPane *> CYourPanes;
		typedef CYourPanes::iterator  CYourPaneIter;

		typedef list< CYourDockablePane *> CYourDockablePanes;
		typedef CYourDockablePanes::iterator CYourDockablePanesIter;

		CYourPanes			m_Panes;
		CYourDockablePanes 	m_YourDockablePanes;

Add this to the implementation file (the .cpp file) of your application's CFrameWnd or CFrameWndEx subclass, changing the CYourFrameWnd class name to the name of your frame window subclass:
void CYourFrameWnd::OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint )
{
	for ( CYourDockablePanesIter it = m_YourDockablePanes.begin(); 
		it != m_YourDockablePanes.end(); it++ )
       {
		(*it)->OnUpdate( pSender, lHint, pHint );
	}
}
BOOL CYourFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	/* We intercept all the commands sent to the frame here, and give all 
	 * our child panes first crack at handling them.
	 */
	for ( CYourPaneIter it = m_Panes.begin(); it != m_Panes.end(); it++ )
	{
         	if ( (*it)->OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ))
			return true;
	}

	return CFrameWndEx::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
BOOL CYourFrameWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	for ( CYourDockablePanesIter it = m_YourDockablePanes.begin(); it != m_YourDockablePanes.end(); it++ )
	{
		if ( (*it)->ReceiveNotification( wParam, lParam, pResult ) )
			return true;
	}

	return CFrameWndEx::OnNotify(wParam, lParam, pResult);
}
At this point, you should search through your old CView class to find any references to a COleDropTarget. These references should be changed to refer to the m_OleDropTarget member that you put within the CYourDockablePane class.

A couple final things you need to do: inform your main frame class about the locations of your CYourDockablePane-derived classes, as well as your toolbars and other non-view features, which are generally children of the CPane class.

Wherever you instantiate and call Create() on your CYourDockablePane descendant, add it to your list of CYourDockablePanes:

  m_YourDockablePanes.push_back( &yourNewlyCreatedDockablePane );

And wherever you instantiate and Create() toolbars or other things that need to process commands or UI updates, including your dockable panes, add them to the list of CPanes as well:

  m_Panes.push_back( &yourNewlyCreatedControlOrPane );

This basic strategy can be used to convert old CView-based classes into any modern control-based user interface architecture, or merely to have your classes derive from CWnd directly.

There may be other side effects of this class conversion that I’ve not considered. Please feel free to modify the above code to your specific situation.

Information and code from this blog entry may be reused and redistributed under the following license.

Creative Commons License
There’s no leaving here; ask, I’m an ear by John Byrd is licensed under a Creative Commons Attribution 3.0 United States License.
Based on a work at www.johnbyrd.org.

One night in Bangkok makes a hard man humble

My thoughts this morning go out to my brother, who has been sent to Afghanistan for a month. Part of the troop surge there. He’s going in a support role, in order to provide some equipment and weapons training, so he’s not expected to see combat; however, I’m still thinking of him. He’s a large fellow and at least as smart as me.

A couple weeks ago I joined the Screen Actors’ Guild. I’m hoping I can leverage some of the Ambassador’s Day work into a new agent.

I just wrote the world’s youngest chess playing program, Superpawn. Superpawn plays like a smart young child. It plays a mostly legal game of chess, though it doesn’t understand concepts like castling and the fifty move rule. Superpawn loves to get the queen out early and parade her around the board. It is trivially Winboard compatible. Once Superpawn survives the epd2wb test suite, I’ll probably release it with a CC0 license.

When my wife was a little girl, she used to play chess with her older and somewhat more serious sister. At a certain point in the game, Amanda would grab a pawn and scream “SUPERPAWN!” and fly it around the board, knocking all the pieces over.

Me used to be a angry young man

With thanks and respect to my father, my company now has a small office at 2102 Business Center Drive in Irvine. I’ve spent the last few weeks launching the company. I spent Christmas break moving in the computers and the printer and the Ikea furniture. I even hired a part-time employee with a flurry of W-2s and I-9s to manage some accounts.

I was worried that I’d turn into a porn-surfing late-sleeping slug when self-employed, but it’s been just the opposite. I am happy to report that I work just as hard for myself as for The Man — twelve-hour days in many cases, and I don’t feel that I’ve worked that hard at the end of the day.

It’s an exciting time, and I’m glad to be alive so I can live it. I am physically tired, but I wake up every morning now excited to go into work.


View Larger Map

<%image(20100106-Photo_010110_005.jpg|1280|1024|Me hiding me head in the sand)%>

The sky is blue, and all the leaves are green

For those of you who are not familiar: for the past three years I’ve been working as a contractor at a large video game development house. The company has been laying off a bunch of people, and yesterday they decided that I should be on that list as well. I feel ambivalent about this. The timing was awkward, but I feel like my product is at the point where I can begin licensing it on a larger scale, so this has the potential to be a blessing in disguise. At least, I’m going to try to keep a positive attitude, and try to focus on rounding up new business. Those of you who know me know that I’m the most sardonic human being in this hemisphere, so positivity does not come naturally to me; but middleware is one of those few industries that does well in an economic downturn, and the product we’ve developed is actually better than the competition, so I do have a good chance of actually turning a profit soon.

Obligatory joke: Titles of musicals that will write themselves:

“Bleeding In The Rain”
“Jesus Christ Porno Star”
“Annie Get Your Machete”
“An American In Paris Hilton”
“Blow Me Kate”
“Oklahomo”

Jesus don’t want me for a sunbeam

She had a particular set of music that she wanted at the funeral. “Memories” from Cats, and “Don’t Cry For Me Argentina” from Evita. Big, loud, brassy numbers. Laptop was down, but I figured out how to burn the CDs on Dad’s laptop. My Harvard computer science degree came in handy for my stepmother’s funeral.

I could handle the service, no problem. Standard Catholic mass. Lots of cookie-cutter sisters pressed like Whitman white chocolates into the front row, all sobbing delicately into Kleenex. Sit-stand-kneel, sing number 865 from the hymnal. I even took communion with the bunch of them, and did not burst into flames. I ate the body of Christ and thought of zombies.

There was a gullet-stuffing potluck after the funeral. Even after everyone had eaten all the Sam’s Club Value Cashews and cole slaw and four-cheese sandwiches and Vienna sausages on plastic toothpicks, even then, there were two party trays of fatty ham and roast-beef sandwiches left over. Somehow the trays came to my dad’s house. I tried to throw them away, but my brother and my father reacted violently. “You so much as make a move to throw out that party tray, and I’ll break your neck,” snarled my brother. He kept it for a day, ate one sandwich from it, and threw it away himself. Something about West Virginia and wasted food. I’ll explain it to you someday. If you lived here, you would already understand.

Flew to West Virginia a week ago, again. Death tours back east running into tens of thousands of dollars.

Dad is sad. I was ready for much worse; I was expecting full-on drinking binges and self-loathing and suicidality, like when we were kids. He’s focusing on helping others instead. He just got elected into an officer position down at the local AA branch. He laughs now and then.

E-mail says more layoffs at work. They fired fifteen people; do you think I could maybe re-do the schedule while I have down time? [Redacted.] Dad is worried about what he will eat. I taught him how to cook a steak, how to dice an onion, how to bake fish, and how to roast croutons.

House looks the same. Lots of leaves. You people in California don’t know leaves, and you don’t know sky. You should come visit, and you will see sky.

The sisters left, in a gossiping, nattering bunch and I was left with my brother, my father, and my Appalachian stoicism. I was actually doing fine until tonight, when I had to disassemble the pictures from the collage back into a photo album. That’s when it hit me. Death in the family. Sixth in three years.

Goddammit!

How straightforward the game when one has trust in one’s player

There exist many variants on the basic rules of chess. Many of these variants introduce a larger board or new pieces or movement rules in order to make the game more complex. Personally, I have a hard time keeping straight the additional rules of the variants, and so they seem less fun to me. In the words of Trip Hawkins, great games are simple, hot and deep.

Cathouse is my contribution to the world of chess variants. In Cathouse, many queens scratch at one another to gain the attention of the king.

The only valid pieces in Cathouse are pawns, queens, and kings. Initial setup for the Cathouse board is as follows:

20091017-Cathouse

In the initial setup, your pieces are placed on the opponent’s side of the board. You have your king on the eighth rank, with pawns on the fifth, sixth and seventh rank.

Move and capture rules are the same as for FIDE chess: pawns may move forward exactly one square into an empty space, or they may capture on adjacent front diagonals. Kings may move and capture to any open adjacent square.

When a pawn reaches the eighth rank, it is immediately exchanged for a queen. This means that each player may theoretically promote up to three queens on the board. (Pawns may only promote to queens.) If you don’t have so many queens laying around with your chess board, just turns your rooks onto their heads and pretend they’re queens.

Queens move as in FIDE chess: they slide along rows, columns or diagonals in rays until they are stopped by a piece of their own color, or until they capture an opposing piece.

The game ends according to FIDE chess rules. Checkmate, stalemate, draw, and three-move repetition endings are all possible.

For a piece layout this simple, the game is surprisingly well-balanced. Computer analysis suggests that white has an opening advantage of only about half a pawn, the same as in traditional FIDE chess. Each player must try to generate their own queens while simultaneously preventing the opponent from doing so. Space control and tempo seem to be key in Cathouse.

It’s spring time for Hitler in Germany

Dad called a couple weeks ago. Rachel, my stepmother, has had cancer for some years now. They just decided to transition her into hospice care. We rejiggered our vacation plans and traveled to Charleston, West Virginia three days ago instead. As with all cancers, no one really knows anything.

People come and go in Dad’s house, some of whom I don’t know. Rachel’s Catholic; a priest came in.

We’ve been on the road a lot. Charleston, West Virginia to Fishersville, Virginia where we will spend the night tonight. My mother made food. Lots of food. It’s what she does. We’re watching The Producers (new school) on TV while I write this.

I snapped at my wife the other day. I regret having done that.

My family looks old to me. I assume I look old to them.

I’m quiet, mostly because I’m tired. I feel physically drained, and I find it difficult to hold a coherent train of thought. I suppose I’m growing a little too set in my ways.

Spam spam spam spam spam spam spam spam

So the Yahoo porn spam bot keeps contacting me. Rather than ignore it, I thought I’d try to get it to tell me a little more about its internal structure, so that someone with more time than me can r00t it. Here’s what I found out.

The bot has a canned script. If a human responds to its IMs, and it doesn’t see a keyword, then it continues with its canned script. Once the script ends, the conversation terminates.

Known interesting keywords follow. The bot searches this keyword list in this order, and the earliest match is returned as a response. The bot is case-insensitive. The bot will continue to chat you indefinitely if you include one or more of the following words in your response.

web / http://tinyurl.com/SEXYMISSES [or whatever the porn URL du jour is]
site / http://tinyurl.com/SEXYMISSES [or whatever the porn URL du jour is]
shit / what’s wrong?
fake / the only thing fake here is your toupet!
spam / nah, I am a vegetarian… no spam for me! haha
bot / whats a bot? im 100% all real and natural, from tits to ass!

Them chickens jacking my style

So this Black Eyed Peas song Boom Boom Pow is getting overplayed to death on TV and radio right now. The lyrics are all about how original and futuristic this song is. The song is an unmodified Bo Diddley beat, which is the oldest and most copied syncopation in all of rock and roll. The beat is also called a “son clave” or “3-2 clave” and it is also common to Cuban, Argentinian and African music.