Click here to Skip to main content
15,885,998 members
Articles / Desktop Programming / MFC

A Visual Framework (Views, Tabs and Splitters)

Rate me:
Please Sign up or sign in to vote.
4.96/5 (71 votes)
6 Dec 2002 772.3K   17.9K   301  
Creating SDI/MDI applications with splitter and tab windows
/*#############################################################################
# VISUALFX.CPP
#
# SCA Software International S.A.
# http://www.scasoftware.com
# scaadmin@scasoftware.com
#
# Copyright (c) 1999 SCA Software International S.A.
#
# Date: 03.01.2000
# Author: Zoran M.Todorovic
#
# This software is provided "AS IS", without a warranty of any kind.
# You are free to use/modify this code but leave this header intact.
#
# Updates:
#
# 14.01.2000 -  Courtesy of Frederic Guillet (www.codeproject.com)
#               Added optional 16x16 icons to tabs
#
#############################################################################*/

#include "stdafx.h"
#include <afxpriv.h>        // Needed for WM_SIZEPARENT

#include "VisualFx.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//=============================================================================
// class TTabItem
//
//=============================================================================

// Create a tab item 
TTabItem::TTabItem(CWnd *pParent, LPCTSTR szLabel, HICON hIcon)
{
  m_pWnd = NULL;
  m_nMinX = m_nMaxX = 0;
  m_bVisible = TRUE;
  m_bEnabled = TRUE;
  m_bWndEnabled = TRUE;
  m_hIcon = hIcon;
  RECT rect;
  ::ZeroMemory(&rect,sizeof(RECT));
  m_pCaption = new CStatic;
  ASSERT(m_pCaption);
  m_pCaption->Create(szLabel, WS_CHILD|SS_CENTER|WS_VISIBLE,rect,pParent);
}

TTabItem::TTabItem(const TTabItem& obj)
{
  *this = obj;
}

TTabItem& TTabItem::operator=(const TTabItem& obj)
{
  m_pWnd = obj.m_pWnd;
  m_pCaption = obj.m_pCaption;
  m_bWndEnabled = obj.m_bWndEnabled;
  m_bEnabled = obj.m_bEnabled;
  m_bVisible = obj.m_bVisible;
  m_nMinX = obj.m_nMinX;
  m_nMaxX = obj.m_nMaxX;
  return *this;
}

TTabItem::~TTabItem()
{
  // This is done in TVisualFramework::Destroy()
  //if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  //  delete m_pWnd;
  ASSERT(m_pCaption);
  delete m_pCaption;
}

// Set rectangle for tab caption
void TTabItem::SetRect(CRect& rect)
{
  ASSERT(m_pCaption);
  m_pCaption->MoveWindow(&rect);
}

// Set font for tab caption
void TTabItem::SetFont(CFont *pFont)
{
  ASSERT(m_pCaption);
  ASSERT(pFont);
  m_pCaption->SetFont(pFont,FALSE);
}

// Set icon handle for this tab
void TTabItem::SetIcon(HICON hIcon)
{
  m_hIcon = hIcon;
}

// Get tab caption text
CString TTabItem::GetText(void)
{
  ASSERT(m_pCaption);
  CString str;
  m_pCaption->GetWindowText(str);
  return str;
}

int TTabItem::GetLength(void)
{
  return m_nMaxX - m_nMinX;
}

// Set tab caption text
void TTabItem::SetText(LPCTSTR szLabel)
{
  ASSERT(m_pCaption);
  ASSERT(szLabel);
  m_pCaption->SetWindowText(szLabel);
}

// Enable/disable a window
void TTabItem::Enable(BOOL bEnable)
{
  m_bWndEnabled = bEnable;
}

// Enable/disable tab caption
void TTabItem::EnableTab(BOOL bEnable)
{
  ASSERT(m_pCaption);
  m_bEnabled = bEnable;
  m_pCaption->EnableWindow(m_bEnabled);
}

// Show/hide tab caption
void TTabItem::ShowTab(BOOL bShow)
{
  ASSERT(m_pCaption);
  m_bVisible = bShow;
  m_pCaption->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
}

CWnd *TTabItem::GetSafeWnd(void)
{
  return (m_pWnd && ::IsWindow(m_pWnd->m_hWnd)) ? m_pWnd : NULL;
}

//=============================================================================
// class TTabWnd
//
//=============================================================================

#define TABWND_DEFAULT_ID 0x2578
#define TABWND_HEIGHT     30    // Height of the gray border between the toolbar 
                                // and the client area
#define TAB_HEIGHT        20    // Height on the normal tab
#define TABSEL_HEIGHT     20    // Height of the selected tab
#define TAB_SPACE         6     // Add to tab caption text width
#define TAB_DEPL          4     // Distance between the tabs and the client area
#define TAB_MAXLEN        200
#define TAB_ICON          16
#define TAB_ICON_X        2
#define TAB_ICON_Y        2

IMPLEMENT_DYNCREATE(TTabWnd,CWnd)

BEGIN_MESSAGE_MAP(TTabWnd, CWnd)
  //{{AFX_MSG_MAP(TTabWnd)
  ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
  ON_WM_ERASEBKGND()
  ON_WM_PAINT()
  ON_WM_LBUTTONUP()
  ON_WM_DESTROY()
  ON_WM_SIZE()
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TTabWnd::TTabWnd()
{
  m_nSelectedTab = 0;
  m_bLockFlag = FALSE;
  m_nTabPos = TP_BOTTOM;
  // cache most used resources
  m_BrushBlack.CreateSolidBrush(RGB(0,0,0));
  m_BrushLGray.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
  m_PenBlack.CreatePen(PS_SOLID, 1, (COLORREF)0);
  m_PenLGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNFACE));
  m_PenWhite.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  m_PenWhite2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  m_PenDGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNSHADOW));
  m_PenDGray2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNSHADOW));
}

TTabWnd::~TTabWnd()
{
}

// Find a tab within this tab window
TTabItem *TTabWnd::findTabItem(int nIndex)
{
  int nNdx = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    if (nNdx == nIndex)
      return (*iterator);
    nNdx ++;
  }
  return NULL;
}

// Create a tab window
BOOL TTabWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, 
                     DWORD dwStyle, const RECT& prect, CWnd* pParentWnd, 
                     UINT nID, CCreateContext *pContext)
{
  ASSERT(pParentWnd);

  dwStyle &= ~WS_BORDER;
  CRect rect(prect);
  if (!CWnd::Create(NULL, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext))
    return FALSE;
  if (pParentWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    ((CFrameWnd*)pParentWnd)->ModifyStyleEx(WS_EX_CLIENTEDGE,0,SWP_FRAMECHANGED);
    ((CFrameWnd*)pParentWnd)->RecalcLayout();
  }
  ResizeTab();
  return TRUE;
}

int TTabWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
  createFont();
	return 0;
}

void TTabWnd::OnDestroy() 
{
  CWnd::OnDestroy();
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator ++) {
    delete (*iterator);
  }
  m_TabList.clear();
  // This is done in TVisualFramework
  //if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  //  delete this;
}

// Virtual function to check whether switch to new tab can be done
BOOL TTabWnd::CanSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
{
  return TRUE;
}

// Virtual function (after the switch is done)
void TTabWnd::OnSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
{
}

// Create fonts for tab labels
void TTabWnd::createFont()
{
	NONCLIENTMETRICS metrics;
	metrics.cbSize = sizeof(metrics);
  ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0);

  CWindowDC wdc(NULL);
  int nLPixY = GetDeviceCaps(wdc.m_hDC, LOGPIXELSY);

	m_Font.CreateFontIndirect(&metrics.lfStatusFont);
}

// Add a tab to this window
TTabItem *TTabWnd::addTab(CWnd *pWnd, LPCTSTR szLabel)
{
  ASSERT(pWnd);
  ASSERT(szLabel);

  TTabItem *pItem = new TTabItem(this,szLabel);
  pItem->m_pWnd = pWnd;
  m_TabList.insert(m_TabList.end(), pItem);
  return pItem;
}

int TTabWnd::GetTabLength()
{
  int nLength = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    nLength += (*iterator)->GetLength();
  }
  return nLength;
}

// Get index of currently selected tab
int TTabWnd::GetTabIndex(void)
{
  return m_nSelectedTab;
}

// Get number of tabs
int TTabWnd::GetTabCount(void)
{
  return m_TabList.size();
}

// Get index of the tab associated with specified window
int TTabWnd::GetTabIndex(CWnd *pWnd)
{
  ASSERT(pWnd);

  int nIndex = 0;
  TTabItem *pItem;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    pItem = *iterator;
    if (pItem->m_pWnd == pWnd)
      return nIndex;
    nIndex ++;
  }
  return -1;
}

// Get pointer to window associated with the specified tab index
CWnd *TTabWnd::GetTabWnd(int index)
{
  TTabItem *pItem = findTabItem(index);
  ASSERT(pItem);
  return ::IsWindow(pItem->m_pWnd->m_hWnd) ? pItem->m_pWnd : NULL;
}

// Get tab caption text of the specified tab
CString TTabWnd::GetTabLabel(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->GetText();
}

// Set text of tab caption
void TTabWnd::SetTabLabel(int nIndex, LPCTSTR szLabel)
{
  ASSERT(szLabel);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->SetText(szLabel);
  invalidateTabArea();
}

// Enable/disable a view
void TTabWnd::Enable(int nIndex, BOOL bEnable)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->Enable(bEnable);
  //pItem->m_pWnd->EnableWindow(bEnable);
}

// Cannot disable currently selected tab
void TTabWnd::EnableTab(int nIndex, BOOL bEnable)
{
  ASSERT(nIndex != m_nSelectedTab);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->EnableTab(bEnable);
  invalidateTabArea();
}

// Cannot make invisible currently selected tab
void TTabWnd::ShowTab(int nIndex, BOOL bShow)
{
  ASSERT(nIndex != m_nSelectedTab);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->ShowTab(bShow);
  invalidateTabArea();
}

// Is tab enabled
BOOL TTabWnd::IsTabEnabled(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->m_bEnabled;
}

// Is tab visible
BOOL TTabWnd::IsTabVisible(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->m_bVisible;
}

// Set font
void TTabWnd::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  CWnd::SetFont(pFont);
  m_Font.DeleteObject();
  LOGFONT lf;
  pFont->GetLogFont(&lf);
  m_Font.CreateFontIndirect(&lf);
  invalidateTabArea();
}

// Set position of tabs (top or bottom)
void TTabWnd::SetTabPos(TTabPos nTabPos)
{
  m_nTabPos = nTabPos;
}

// Invalidate rectangle to redraw tabs
void TTabWnd::invalidateTabArea(void)
{
  CRect rect;
  switch (m_nTabPos) {
  case TP_TOP: 
    InvalidateRect(&CRect(0, 0, 32000, TABWND_HEIGHT)); 
    break;
  case TP_BOTTOM:
    GetClientRect(&rect);
    InvalidateRect(&CRect(CPoint(0,rect.Height()-TABWND_HEIGHT), 
                          CSize(32000,TABWND_HEIGHT)));
    break;
  };
}

// Draws a selected tab and returns its height
int TTabWnd::drawSelTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 4;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;
  int nDeltaWidth = 0;

  if (pItem->m_hIcon) {
    nDeltaWidth = TAB_ICON + TAB_ICON_X;
    textSize.cx += nDeltaWidth;
  }

  // black border, no bottom line
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(x,y+TABSEL_HEIGHT-1);
  pDC->LineTo(x,y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);

  // left and upper border in white, double line
  pDC->SelectObject(&m_PenWhite2);
  pDC->MoveTo(x+2,y+TABSEL_HEIGHT-1);
  pDC->LineTo(x+2,y+2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+2);

  // right border, dark gray, double line
  pDC->SelectObject(&m_PenDGray2);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);

  // clean up
  pDC->SelectObject(&m_PenLGray);
  pDC->MoveTo(x-1, y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT);
  pDC->MoveTo(x-1, y+TABSEL_HEIGHT+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);

  // a black line to far left and right
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(0, y+TABSEL_HEIGHT-1);
  pDC->LineTo(x, y+TABSEL_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE+1, y+TABSEL_HEIGHT-1);
  pDC->LineTo(client.right, y+TABSEL_HEIGHT-1);

  // and a white double line
  pDC->SelectObject(&m_PenWhite2);
  if (x!=0) {
    pDC->MoveTo(0, y+TABSEL_HEIGHT+1);
    pDC->LineTo(x, y+TABSEL_HEIGHT+1);
  }
  pDC->MoveTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);
  pDC->LineTo(client.right, y+TABSEL_HEIGHT+1);

  // gray inside
  pDC->FillSolidRect(x+3, y+3,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT, 
                  ::GetSysColor(COLOR_BTNFACE));

  if (pItem->m_hIcon) {
    ::DrawIconEx(pDC->m_hDC,x+4,y+TAB_ICON_Y,pItem->m_hIcon,16,16,0,NULL,DI_NORMAL);
  }

  CRect rect(CPoint(x+nDeltaWidth+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), 
             CSize(textSize.cx-nDeltaWidth, textSize.cy));
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw a selected tab at the bottom
int TTabWnd::drawSelTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 4;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;
  int nDeltaWidth = 0;

  if (pItem->m_hIcon) {
    nDeltaWidth = TAB_ICON + TAB_ICON_X;
    textSize.cx += nDeltaWidth;
  }

  // black border, no bottom line
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(x,y);
  pDC->LineTo(x,y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);

  // left border in white, double line
  pDC->SelectObject(&m_PenWhite2);
  pDC->MoveTo(x+2,y);
  pDC->LineTo(x+2,y+TABSEL_HEIGHT-1);

  // right and bottom border, dark gray, double line
  pDC->SelectObject(&m_PenDGray2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+TABSEL_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);

  // a black line to far left and right
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(0, y);
  pDC->LineTo(x, y);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE, y);
  pDC->LineTo(client.right, y);

  // and a gray line to far left and right
  pDC->SelectObject(&m_PenDGray);
  if (x != 0) {
    pDC->MoveTo(0, y-1);
    pDC->LineTo(x, y-1);
  }
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y-1);
  pDC->LineTo(client.right, y-1);

  // gray inside
  pDC->FillSolidRect(x+3,y,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT-2, 
                    ::GetSysColor(COLOR_BTNFACE));

  if (pItem->m_hIcon) {
    ::DrawIconEx(pDC->m_hDC,x+4,y+TAB_ICON_Y,pItem->m_hIcon,16,16,0,NULL,DI_NORMAL);
  }

  CRect rect(CPoint(x+nDeltaWidth+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), 
             CSize(textSize.cx-nDeltaWidth, textSize.cy));
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draws an unselected tab and returs its height
int TTabWnd::drawTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 4;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = TABWND_HEIGHT-TAB_HEIGHT-TAB_DEPL;
  int nDeltaWidth = 0;

  if (pItem->m_hIcon) {
    nDeltaWidth = TAB_ICON + TAB_ICON_X;
    textSize.cx += nDeltaWidth;
  }
  
  // black border
  pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT)), 
                  &m_BrushBlack);

  pDC->SelectObject(&m_PenWhite);
  pDC->MoveTo(x+1, y+1);
  pDC->LineTo(x+1, y+TAB_HEIGHT-1);
  pDC->MoveTo(x+1, y+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+1);

  pDC->SelectObject(&m_PenDGray);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);

  pDC->FillRect(&CRect(CPoint(x+2,y+2), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
                &m_BrushLGray);

  // clean up
  int dy = TABSEL_HEIGHT-TAB_HEIGHT;
  pDC->FillSolidRect(x, y-dy, textSize.cx+TAB_SPACE, dy, GetSysColor(COLOR_BTNFACE));

  if (pItem->m_hIcon) {
    ::DrawIconEx(pDC->m_hDC,x+4,y+TAB_ICON_Y,pItem->m_hIcon,16,16,0,NULL,DI_NORMAL);
  }

  CRect rect(CPoint(x+nDeltaWidth+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), 
             CSize(textSize.cx-nDeltaWidth, textSize.cy));
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw an unselected tab at the bottom
int TTabWnd::drawTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 4;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;
  int nDeltaWidth = 0;

  if (pItem->m_hIcon) {
    nDeltaWidth = TAB_ICON + TAB_ICON_X;
    textSize.cx += nDeltaWidth;
  }

  // black border
  pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT+1)), 
                  &m_BrushBlack);

  // Gray border bottom and right side
  pDC->SelectObject(&m_PenDGray);
  pDC->MoveTo(x+1, y+TAB_HEIGHT-1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);

  pDC->FillRect(&CRect(CPoint(x+1,y+1), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
                &m_BrushLGray);

  if (pItem->m_hIcon) {
    ::DrawIconEx(pDC->m_hDC,x+4,y+TAB_ICON_Y,pItem->m_hIcon,16,16,0,NULL,DI_NORMAL);
  }

  CRect rect(CPoint(x+nDeltaWidth+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), 
             CSize(textSize.cx-nDeltaWidth, textSize.cy));
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw edge arround client area
void TTabWnd::drawClient(CDC *pDc, CRect& rect)
{
  ASSERT(pDc);

  CWnd *pParent = GetParent();
  ASSERT(pParent);
  switch (m_nTabPos) {
  case TP_TOP:
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      pDc->DrawEdge(&rect, EDGE_ETCHED, BF_TOP);
    }
    pDc->Draw3dRect(0,TABWND_HEIGHT, rect.right, rect.bottom-TABWND_HEIGHT,
                   ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
    pDc->Draw3dRect(1,TABWND_HEIGHT+1, rect.right-2, rect.bottom-TABWND_HEIGHT-2,
                    0, ::GetSysColor(COLOR_3DLIGHT));
    break;
  case TP_BOTTOM:
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      pDc->DrawEdge(&rect, EDGE_ETCHED, BF_BOTTOM);
    }
    pDc->Draw3dRect(0,0, rect.right, rect.bottom-TABWND_HEIGHT+1,
                   ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
    pDc->Draw3dRect(1,1, rect.right-2, rect.bottom-TABWND_HEIGHT-1,
                    0, ::GetSysColor(COLOR_3DLIGHT));
    break;
  }
}

void TTabWnd::OnPaint()
{
  CPaintDC dc(this); // device context for painting
  CRect client;

  GetClientRect(&client);
  drawClient(&dc, client);

  int x = 0, nIndex = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    TTabItem *pItem = *iterator;
    ASSERT(pItem != NULL);
    if (pItem->m_bVisible) {
      pItem->m_nMinX = x;
      if (nIndex != m_nSelectedTab) {
        switch (m_nTabPos) {
        case TP_TOP: x += drawTabTop(&dc, x, client, pItem); break;
        case TP_BOTTOM: x += drawTabBottom(&dc, x, client, pItem); break;
        }
      } else {
        switch (m_nTabPos) {
        case TP_TOP: x += drawSelTabTop(&dc, x, client, pItem); break;
        case TP_BOTTOM: x += drawSelTabBottom(&dc, x, client, pItem); break;
        }
      }
    }
    pItem->m_nMaxX = x;
    nIndex ++;
  }
}

// Returns tab index that holds specified point
int TTabWnd::HitTest(CPoint& point)
{
  return HitTest(point.x,point.y);
}

// Returns tab index that holds specified point
int TTabWnd::HitTest(int x, int y)
{
  int notsel_y_min, sel_y_min, y_max;
  CRect rect;

  GetClientRect(&rect);
  switch (m_nTabPos) {
  case TP_TOP:
    notsel_y_min = TABWND_HEIGHT - TAB_HEIGHT - TAB_DEPL;
    sel_y_min = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;
    y_max = TABWND_HEIGHT - TAB_DEPL;
    break;
  case TP_BOTTOM:
    notsel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
    sel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
    y_max = rect.Height() - TABWND_HEIGHT + TAB_DEPL + TAB_HEIGHT;
    break;
  };

  int nIndex = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    TTabItem *pItem = (*iterator);
    if (pItem->m_bEnabled && pItem->m_bVisible) {
      if (nIndex != m_nSelectedTab && (y < notsel_y_min || y > y_max)) 
        continue;
      if (nIndex == m_nSelectedTab && (y < sel_y_min || y > y_max)) 
        continue;
      if (x >= pItem->m_nMinX && x <= pItem->m_nMaxX)
        return nIndex;
    }
    nIndex++;
  }
  return -1;
}

// Switch focus to specified tab index
BOOL TTabWnd::SetActivePane(int nIndex, BOOL bActivate)
{
  if (nIndex == -1)
    return FALSE;
  if (nIndex == m_nSelectedTab)
    return TRUE;

  TTabItem *pNewPane = findTabItem(nIndex);
  if (!pNewPane->m_bEnabled || !pNewPane->m_bVisible)
    return FALSE;
  TTabItem *pOldPane = NULL;
  if (m_nSelectedTab != -1)
    pOldPane = findTabItem(m_nSelectedTab);
  if (CanSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd)) {
    // Deactivate old pane
    if (m_nSelectedTab != -1) {
      pOldPane->m_pWnd->EnableWindow(FALSE);
      pOldPane->m_pWnd->ShowWindow(SW_HIDE);
    }
    // Activate new pane
    pNewPane->m_pWnd->EnableWindow(pNewPane->m_bWndEnabled ? TRUE : FALSE);
    pNewPane->m_pWnd->ShowWindow(SW_SHOW);
    pNewPane->m_pWnd->SetFocus();
    // Save index of new pane
    m_nSelectedTab = nIndex;
    // Invalidate tab
    invalidateTabArea();
    // Inform derived class
    OnSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd);
    // Update frame window
    if (bActivate) {
      CWnd *pParent = GetParent();
      while (pParent && !pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
        pParent = pParent->GetParent();
      ASSERT(pParent != NULL);
      updateFrame((CFrameWnd*)pParent, pNewPane->m_pWnd);
    }
    return TRUE;
  }
  return FALSE;
}

// Update frame window active view based on newly selected tab item
BOOL TTabWnd::updateFrame(CFrameWnd *pFrame, CWnd *pWnd)
{
  ASSERT(pFrame);
  ASSERT(pWnd);

  if (pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    // New tab item is a view
    pFrame->SetActiveView((CView*)pWnd);
    return TRUE;
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
    CWnd *pView = pSplitter->GetActivePane();
    if (pView == NULL) {
      CWnd *pTmpView;
      for (int x = 0; x < pSplitter->GetRowCount(); x ++) {
        for (int y = 0; y < pSplitter->GetColumnCount(); y ++) {
          pTmpView = pSplitter->GetPane(x,y);
          if (pTmpView->IsWindowEnabled()) {
            if (updateFrame(pFrame, pTmpView))
              return TRUE;
          }
        }
      }
    }
    if (pView == NULL)
      pView = pSplitter->GetPane(0,0);
    pFrame->SetActiveView((CView*)pView);
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    int nIndex = pTab->GetTabIndex();
    CWnd *pTabWnd = pTab->GetTabWnd(nIndex);
    if (updateFrame(pFrame, pTabWnd))
      return TRUE;
  }
  return FALSE;
}

// Resize tab
void TTabWnd::ResizeTab(int cx, int cy)
{
  CRect rect;
  CWnd *pParent = (CWnd*)GetParent();
  ASSERT(pParent);
  pParent->GetClientRect(&rect); 

  if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    CSplitterWnd *splitter = (CSplitterWnd*)pParent;
    ASSERT(pParent);
    int row,col;
    splitter->IsChildPane(this,row,col);
    splitter->RecalcLayout();
  } else if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    m_bLockFlag = TRUE;
    pParent->RepositionBars(0, 0xFFFF, AFX_IDW_PANE_FIRST, CWnd::reposQuery, &rect);
    MoveWindow(rect.left,rect.top,rect.Width(),rect.Height());
    m_bLockFlag = FALSE;
  }

  m_bLockFlag = TRUE; // reentrancy check (might get called recursivly from OnSize)
  CWnd *pWnd;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    pWnd = (*iterator)->m_pWnd;
    if (cx == -1 && cy == -1) {
      switch (m_nTabPos) {
      case TP_TOP:
        pWnd->MoveWindow(1, TABWND_HEIGHT+1, 
                         rect.Width()-2, rect.Height()-TABWND_HEIGHT-2);
        break;
      case TP_BOTTOM:
        pWnd->MoveWindow(1, 0, rect.Width()-2, rect.Height()-TABWND_HEIGHT);
        break;
      }
    } else {
      switch (m_nTabPos) {
      case TP_TOP:
        pWnd->MoveWindow(1, TABWND_HEIGHT+1, cx, cy-TABWND_HEIGHT-2);
        break;
      case TP_BOTTOM:
        pWnd->MoveWindow(1, 0, cx, cy-TABWND_HEIGHT);
        break;
      }
    }
  }
  m_bLockFlag=FALSE;
}

// Erase area where the tabs are displayed
BOOL TTabWnd::OnEraseBkgnd(CDC* pDC)
{
  ASSERT(pDC);

  CRect rect;
  GetClientRect(&rect);
  switch (m_nTabPos) {
  case TP_TOP:
    pDC->FillSolidRect(&CRect(0, 0, rect.right, TABWND_HEIGHT), 
                      ::GetSysColor(COLOR_BTNFACE));
    break;
  case TP_BOTTOM:
    pDC->FillSolidRect(&CRect(0, rect.bottom-TABWND_HEIGHT-3, rect.right, rect.bottom), 
                      ::GetSysColor(COLOR_BTNFACE));
    break;
  }
  return TRUE;
}

// Handle couse click on tabs
void TTabWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
  int nNewTab = HitTest(point.x, point.y);
  SetActivePane(nNewTab);
  CWnd::OnLButtonUp(nFlags, point);
}

// Handle resize
LRESULT TTabWnd::OnSizeParent(WPARAM, LPARAM lParam)
{
  if (m_bLockFlag)
    return 0;
  ResizeTab();
  return 0;
}

// Handle resize
void TTabWnd::OnSize(UINT nType, int cx, int cy) 
{
  CWnd::OnSize(nType, cx, cy);
  ResizeTab(cx,cy);
}

// Create a CView derived class as a tab
TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, CRuntimeClass *pViewClass, 
                                 CCreateContext *pContext)
{
  CRect rect, client;
  ASSERT(pViewClass && pContext);

  CWnd *pWnd = (CWnd*)pViewClass->CreateObject();
  if (!pWnd) 
    return NULL;
  
  GetClientRect(&client);
  rect.left = 0;
  rect.top = TABWND_HEIGHT+2;
  rect.right = client.right;
  rect.bottom = client.bottom;

  int dwStyle = AFX_WS_DEFAULT_VIEW;
  if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    dwStyle &= ~WS_BORDER;
  }

  if (!pWnd->Create(NULL, NULL, dwStyle, rect, this, 13576+m_TabList.size(), pContext))
  {
    TRACE0("Warning: couldn't create client area for tab view\n");
    // pWnd will be cleaned up by PostNcDestroy
    return NULL;
  }

  // Insert new tab object into the list
  TTabItem *pTab = addTab(pWnd,lpszLabel);
  ASSERT(pTab);
  if (m_TabList.size() != 1) {
    pWnd->EnableWindow(FALSE);
    pWnd->ShowWindow(SW_HIDE);
  /*
  // Framework is responsible to set the active view
  } else {
    CWnd *pParent = GetParent();
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      ((CFrameWnd*)pParent)->SetActiveView((CView*)pWnd);
    } else if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
      ((CSplitterWnd*)pParent)->SetActivePane(0,0,pWnd);
    }
  */
  }
  return pTab;
}

// Create a splitter window as a tab
TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, int nRows, int nCols, 
                                 CWnd *pWnd, UINT nID)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));

  // Moved to TVisualFramework to handle creation of CSplitterWnd derived classes
  //CSplitterWnd *pWnd = new CSplitterWnd;
  //if (!pWnd) 
  //  return NULL;

  int dwStyle = AFX_WS_DEFAULT_VIEW;
  dwStyle &= ~WS_BORDER;

  CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  if (!pSplitter->CreateStatic(this, nRows, nCols, dwStyle, nID)) {
    TRACE0("Warning: couldn't create client area for tab view\n");
    // pWnd will be cleaned up by PostNcDestroy
    return NULL;
  }

  TTabItem *pTab = addTab(pWnd,lpszLabel);
  ASSERT(pTab);
  if (m_TabList.size() != 1) {
    pWnd->EnableWindow(FALSE);
    pWnd->ShowWindow(SW_HIDE);
  } 

  /*
  // Framework will set the active view
  CWnd *paneWnd = pWnd->GetActivePane();
  if (paneWnd) {
    ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  } else {
    paneWnd = pWnd->GetPane(0,0);
    pWnd->SetActivePane(0,0);
    ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  }
  */

  return pTab;
}

//=============================================================================
// class TVisualObject
//
//=============================================================================

// Private constructor
TVisualObject::TVisualObject()
{
}

// Create a plain view
TVisualObject::TVisualObject(DWORD dwId, CCreateContext *pContext, 
                       CRuntimeClass *pClass)
{
  ASSERT(pContext);
  ASSERT(pClass);
  ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)));

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_VIEW;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  checkStyle();
}

// Create a view within a tab window or a tab window
TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, CCreateContext *pContext, 
                       CRuntimeClass *pClass, DWORD dwStyle)
{
  ASSERT(szTitle);
  ASSERT(pContext);
  ASSERT(pClass);

  zeroAll();
  m_dwId = dwId;
  if (pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd))) {
    m_nObjectType = OT_TAB;
  } else if (pClass->IsDerivedFrom(RUNTIME_CLASS(CView))) {
    m_nObjectType = OT_TABVIEW;
  } else {
    ASSERT(FALSE);
  }
  m_strTitle = szTitle;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a splitter window
TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, int nRows, int nCols, 
                       CCreateContext *pContext, DWORD dwStyle)
{
  ASSERT(szTitle);
  ASSERT(pContext);
  ASSERT(nRows);
  ASSERT(nCols);

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTER;
  m_strTitle = szTitle;
  m_pContext = pContext;
  m_nRows = nRows;
  m_nCols = nCols;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a view within a splitter window
TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, CCreateContext *pContext, 
                       CRuntimeClass *pClass, CSize size, DWORD dwStyle)
{
  ASSERT(pContext);
  ASSERT(pClass);
  ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)) ||
         pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTERVIEW;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  m_nRowIndex = nRow;
  m_nColIndex = nCol;
  m_Size = size;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a splitter within a splitter window
TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, int nRows, int nCols, 
                       CCreateContext *pContext, DWORD dwStyle)
{
  ASSERT(pContext);
  ASSERT(nRows);
  ASSERT(nCols);

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTERSPLITTER;
  m_pContext = pContext;
  m_nRowIndex = nRow;
  m_nColIndex = nCol;
  m_nRows = nRows;
  m_nCols = nCols;
  m_dwStyle = dwStyle;
  checkStyle();
}

TVisualObject::TVisualObject(const TVisualObject& obj)
{
  zeroAll();
  *this = obj;
}

TVisualObject::~TVisualObject()
{
  if (m_hIcon) {
    ::DestroyIcon(m_hIcon);
    m_hIcon = NULL;
  }
}

TVisualObject& TVisualObject::operator=(const TVisualObject& obj)
{
  // No need to copy m_ObjectList since it is populated after
  // this code is executed in STL container
  m_nObjectType = obj.m_nObjectType;
  m_dwId = obj.m_dwId;
  m_pWnd = obj.m_pWnd;
  m_pParent = obj.m_pParent;
  m_strTitle = obj.m_strTitle;
  m_nRows = obj.m_nRows;
  m_nCols = obj.m_nCols;
  m_nRowIndex = obj.m_nRowIndex;
  m_nColIndex = obj.m_nColIndex;
  m_pContext = obj.m_pContext;
  m_pRuntimeClass = obj.m_pRuntimeClass;
  m_Size = obj.m_Size;
  m_bEnabled = obj.m_bEnabled;
  m_dwStyle = obj.m_dwStyle;
  m_cHotKey = obj.m_cHotKey;
  m_hIcon = obj.m_hIcon;
  m_pOwner = obj.m_pOwner;
  m_pFramework = obj.m_pFramework;
  return *this;
}

void TVisualObject::zeroAll(void)
{
  // No need to zero m_ObjectList since it is already empty
  m_nObjectType = OT_UNKNOWN;
  m_dwId = 0;
  m_pWnd = NULL;
  m_pParent = NULL;
  m_strTitle = _T("");
  m_nRows = 0;
  m_nCols = 0;
  m_nRowIndex = 0;
  m_nColIndex = 0;
  m_pContext = NULL;
  m_pRuntimeClass = NULL;
  m_Size = CSize(0,0);
  m_bEnabled = TRUE;
  m_dwStyle = 0;
  m_cHotKey = 0;
  m_pOwner = NULL;
  m_pFramework = NULL;
  m_hIcon = NULL;
}

// Check if style is valid
void TVisualObject::checkStyle(void)
{
  if ((m_dwStyle & TOS_TABTOP) || (m_dwStyle & TOS_TABBOTTOM)) {
    ASSERT(m_pRuntimeClass);
    // Tab position valid only for tab window derived classes
    ASSERT(m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
  }
  if (m_dwStyle & TOS_SELECTED) {
    // Selected valid only for tab panes that are not splitters and tabs
    // In this case, use TVisualFramework::SetActivePane() to set the active pane
    // once the framework is created
    if (m_pRuntimeClass == NULL) {
      // Splitters canot be dynamically create (m_pRuntimeClass is NULL)
      ASSERT((m_nObjectType != OT_SPLITTER) && (m_nObjectType != OT_SPLITTERVIEW));
    } else {
      ASSERT(!m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
    }
  }
}

// Delete the window pointer and optionally destroy the window
void TVisualObject::Destroy(BOOL bDestroyWindow)
{
  if (m_pWnd) {
    if (bDestroyWindow)
      m_pWnd->DestroyWindow();
    delete m_pWnd;
    m_pWnd = NULL;
  }
}

// If this object is a tab window or splitter window that it 
// cannot be focused
BOOL TVisualObject::CanFocus(void)
{
  ASSERT(m_pWnd);

  if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)) ||
      m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  {
    return FALSE;
  }
  return TRUE;
}

// Set hot key for this tab object
void TVisualObject::SetHotKey(CHAR cHotKey)
{
  m_cHotKey = cHotKey;
}

// Optional: Set description 
void TVisualObject::SetDescription(LPCTSTR szDesc)
{
  m_strDescription = szDesc;
}

// Optional: Set icon for tab and load it from the resource
BOOL TVisualObject::SetIcon(UINT nIconId)
{
  // Cannot specify icon if already specified
  if (m_hIcon)
    return FALSE;

  // Load icon if possible
  if (nIconId == 0) {
    m_hIcon = NULL;
  } else {
    m_hIcon = (HICON)::LoadImage(::AfxGetResourceHandle(), 
                        MAKEINTRESOURCE(nIconId), 
                        IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
    if (m_hIcon == NULL)
      return FALSE;
  }
  return TRUE;
}

// Optional: Return icon handle for tab
HICON TVisualObject::GetIcon(void)
{
  return m_hIcon;
}

// Set this object as active pane
BOOL TVisualObject::SetActivePane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->SetActivePane(this);
}

// Set this tab to be active tab (not the active pane)
BOOL TVisualObject::SetActiveTab(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->SetActiveTab(this);
}

// Enable/disable this object
BOOL TVisualObject::Enable(BOOL bEnable)
{
  ASSERT(m_pFramework);
  return m_pFramework->Enable(this,bEnable);
}

// Enable/disable tab
BOOL TVisualObject::EnableTab(BOOL bEnable)
{
  ASSERT(m_pFramework);
  return m_pFramework->EnableTab(this,bEnable);
}

// SHow/hide this object
BOOL TVisualObject::ShowTab(BOOL bShow)
{
  ASSERT(m_pFramework);
  return m_pFramework->ShowTab(this,bShow);
}

// Is this object enabled
BOOL TVisualObject::IsEnabled(BOOL& bEnabled)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsEnabled(this,bEnabled);
}

// Is this object enabled
BOOL TVisualObject::IsTabEnabled(BOOL& bEnabled)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabEnabled(this,bEnabled);
}

// Is this object visible
BOOL TVisualObject::IsTabVisible(BOOL& bVisible)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabVisible(this,bVisible);
}

// Returns TRUE if this object is a tab within a tab window
BOOL TVisualObject::IsTabPane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabPane(this);
}

// Returns TRUE if this object is a tab window
BOOL TVisualObject::IsTabWindow(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabWindow(this);
}

// Returns TRUE if this object is a pane within a splitter window
BOOL TVisualObject::IsSplitterPane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsSplitterPane(this);
}

// Returns TRUE if this object is a splitter window
BOOL TVisualObject::IsSplitterWindow(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsSplitterWindow(this);
}

// Returns TRUE if this object is derived from CView 
BOOL TVisualObject::IsView(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsView(this);
}

// Get object ID
#ifdef _DEBUG
DWORD TVisualObject::GetID(void)
{
  return m_dwId;
}
#endif

// Get object window
#ifdef _DEBUG
CWnd *TVisualObject::GetWnd(void)
{
  return m_pWnd;
}
#endif

// Get safe object window
#ifdef _DEBUG
CWnd *TVisualObject::GetSafeWnd(void)
{
  return ::IsWindow(m_pWnd->m_hWnd) ? m_pWnd : NULL;
}
#endif

#ifdef _DEBUG
CString TVisualObject::GetTitle(void)
{
  return m_strTitle;
}
#endif

#ifdef _DEBUG
CString TVisualObject::GetDescription(void)
{
  return m_strDescription;
}
#endif

#ifdef _DEBUG
CWnd *TVisualObject::GetParentWnd(void)
{
  return m_pParent;
}
#endif

#ifdef _DEBUG
TVisualFramework *TVisualObject::GetFramework(void)
{
  return m_pFramework;
}
#endif

#ifdef _DEBUG
TVisualObject *TVisualObject::GetOwner(void)
{
  return m_pOwner;
}
#endif

//=============================================================================
// class TVisualFramework
//
//=============================================================================

IMPLEMENT_DYNCREATE(TVisualFramework, CCmdTarget)

BEGIN_MESSAGE_MAP(TVisualFramework, CCmdTarget)
	//{{AFX_MSG_MAP(TVisualFramework)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TVisualFramework::TVisualFramework()
{
  m_pOwner = NULL;
  m_bEnableCtrlTab = TRUE;
}

TVisualFramework::~TVisualFramework()
{
  if (m_ObjectMap.size() && m_ObjectList.size()) {
    TRACE0(_T(">>> TVisualFramework::Destroy() called in TVisualFramework destructor\n"));
    TRACE0(_T(">>>   It must be called in CFrameWnd derived class OnDestroy() message handler\n"));
    Destroy();
  }
}

// Find an object in the map with the specified unique id
TVisualObject *TVisualFramework::findObject(DWORD dwId)
{
  TVisualObjectMap::iterator iterator;
  for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
    if (dwId == iterator->first)
      return iterator->second;
  }
  return NULL;
}

// Find an object in the map with the specified window
TVisualObject *TVisualFramework::findObject(CWnd *pWnd)
{
  TVisualObjectMap::iterator iterator;
  for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
    if (pWnd == iterator->second->m_pWnd)
      return iterator->second;
  }
  return NULL;
}

// Add object to the container (this is a root level object)
// There is only one root level object (either splitter or tab)
BOOL TVisualFramework::Add(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(m_ObjectList.size() == 0);   // Only one root level object allowed

  // Root level object is either a view, splitter or a tab
  ASSERT((pObject->m_nObjectType == TVisualObject::OT_TAB) || 
         (pObject->m_nObjectType == TVisualObject::OT_VIEW) || 
         (pObject->m_nObjectType == TVisualObject::OT_SPLITTER));

  if (findObject(pObject->m_dwId) == NULL) {
    m_ObjectList.insert(m_ObjectList.end(), pObject);
    pObject->m_pFramework = this;
    m_ObjectMap[pObject->m_dwId] = pObject;
    return TRUE;
  }
  ASSERT(FALSE);    // Duplicate object Id
  return FALSE;
}

// Add child object to the specified object
BOOL TVisualFramework::Add(TVisualObject *pOwner, TVisualObject *pObject)
{
  ASSERT(pObject);

  #ifdef _DEBUG
  // Validate definition
  if (pOwner->m_nObjectType == TVisualObject::OT_TAB) {
    if ((pObject->m_nObjectType != TVisualObject::OT_TABVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTER) &&
        (pObject->m_nObjectType != TVisualObject::OT_TAB))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTER) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTERSPLITTER) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_TABVIEW) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTER))
    {
      ASSERT(FALSE);
    }
  }

  #endif

  if (findObject(pObject->m_dwId) == NULL) {
    pOwner->m_ObjectList.insert(pOwner->m_ObjectList.end(), pObject);
    pObject->m_pOwner = pOwner;
    pObject->m_pFramework = this;
    m_ObjectMap[pObject->m_dwId] = pObject;
    return TRUE;
  }
  ASSERT(FALSE);    // Duplicate object Id
  return FALSE;
}

// Create all objects within the framework
BOOL TVisualFramework::Create(CWnd *pWnd)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));

  // Save owner for later
  m_pOwner = pWnd;

  // Disable Ctrl+Tab for MDI applications
  if (pWnd->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
    m_bEnableCtrlTab = FALSE;

  // Walk thru visual object hierarchy and create windows
  BOOL rc;
  TVisualObject *pObject;
  TVisualObjectList::iterator it;
  for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
    pObject = *it;
    rc = execCreate(pWnd, pObject);
    if (rc == FALSE) {
      TRACE0(_T("Create visual object failed!\n"));
      return FALSE;
    }
  }
  
  // Walk thru the map and find first object that can be focused
  // Then set focus
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
    pObject = mapit->second;
    if (pObject->CanFocus()) {
      SetActivePane(pObject);
      break;
    }
  }

  return TRUE;
}

// Destroy all objects in the framework
void TVisualFramework::Destroy(void)
{
  TVisualObject *pObject;

  // Recursive delete of all objects
  TVisualObjectList::iterator it;
  for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
    pObject = *it;
    execDestroy(pObject);
  }

  // Delete pointers in object map
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit++) {
    pObject = mapit->second;
    delete pObject;
  }

  // Empty all containers (for check in destructor)
  m_ObjectMap.clear();
  m_ObjectList.clear();
}

// Recursive function to delete all object windows. Does not delete views since
// they are destroyed by frame.
void TVisualFramework::execDestroy(TVisualObject *pObject)
{
  if (pObject->m_pWnd && ::IsWindow(pObject->m_pWnd->m_hWnd)) {
    if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TVisualObject *pObj;
      TVisualObjectList::iterator it;
      for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
        pObj = *it;
        execDestroy(pObj);
      }
      pObject->Destroy(TRUE);
    } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
      TVisualObject *pObj;
      TVisualObjectList::iterator it;
      for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
        pObj = *it;
        execDestroy(pObj);
      }
      pObject->Destroy(TRUE);
    } 
  }
}

// Create specified object and all its childs
BOOL TVisualFramework::execCreate(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);
  
  BOOL rc = FALSE;
  switch (pObject->m_nObjectType) {
  case (TVisualObject::OT_SPLITTER): 
    rc = execCreateSplitter(pWnd,pObject);
    break;
  case (TVisualObject::OT_SPLITTERVIEW): 
    rc = execCreateSplitterView(pWnd, pObject);
    break;
  case (TVisualObject::OT_SPLITTERSPLITTER):
    rc = execCreateSplitterSplitter(pWnd, pObject);
    break;
  case (TVisualObject::OT_TAB):
    rc = execCreateTabWnd(pWnd, pObject);
    break;
  case (TVisualObject::OT_TABVIEW):
    rc = execCreateTabView(pWnd, pObject);
    break;
  case (TVisualObject::OT_VIEW):
    rc = execCreateView(pWnd, pObject);
    break;
  }
  return rc;
}

// Create a simple view 
BOOL TVisualFramework::execCreateView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
  ASSERT(pObject);
  ASSERT(pObject->m_pContext);
  ASSERT(pObject->m_pRuntimeClass);

  CFrameWnd *pFrame = (CFrameWnd*)pWnd;
  pObject->m_pContext->m_pNewViewClass = pObject->m_pRuntimeClass;
  pObject->m_pWnd = pFrame->CreateView(pObject->m_pContext);
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  
  pObject->m_pParent = pFrame;
  setTabWndProperties(pObject);
  
  return TRUE;
}

// Create a view within a tab window
BOOL TVisualFramework::execCreateTabView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)));
  ASSERT(pObject);
  ASSERT(pObject->m_pContext);
  ASSERT(pObject->m_pRuntimeClass);
  ASSERT(!pObject->m_strTitle.IsEmpty());

  TTabWnd *pTab = (TTabWnd*)pWnd;
  TTabItem *pItem;
  pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass, 
                            pObject->m_pContext);
  pItem->SetIcon(pObject->GetIcon());
  ASSERT(pItem);
  pObject->m_pWnd = pItem->GetSafeWnd();
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  pObject->m_pParent = pTab;
  setTabWndProperties(pObject);
  return TRUE;
}

// Create a splitter window
BOOL TVisualFramework::execCreateSplitter(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);

  // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  // do not support dynamic creation
  pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));

  if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_nRows, 
                                       pObject->m_nCols, pObject->m_pWnd);
    pItem->SetIcon(pObject->GetIcon());
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    ((CSplitterWnd*)pObject->m_pWnd)->CreateStatic(pWnd,pObject->m_nRows,pObject->m_nCols);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
  }
  setTabWndProperties(pObject);
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a view within a splitter. Then create all childs of this view.
BOOL TVisualFramework::execCreateSplitterView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  ASSERT(pObject);
  
  CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  pSplitter->CreateView(pObject->m_nRowIndex, pObject->m_nColIndex, 
                        pObject->m_pRuntimeClass, pObject->m_Size, 
                        pObject->m_pContext);
  pObject->m_pWnd = pSplitter->GetPane(pObject->m_nRowIndex, pObject->m_nColIndex);
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  pObject->m_pParent = pSplitter;
  setTabWndProperties(pObject);
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a nested splitter window
BOOL TVisualFramework::execCreateSplitterSplitter(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  
  CSplitterWnd *pParent = (CSplitterWnd*)pWnd;
  // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  // do not support dynamic creation
  pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  CSplitterWnd *pSplitter = (CSplitterWnd*)pObject->m_pWnd;
  pSplitter->CreateStatic(pParent, pObject->m_nRows, pObject->m_nCols, 
                          WS_CHILD|WS_VISIBLE|WS_BORDER, 
                          pParent->IdFromRowCol(pObject->m_nRowIndex,pObject->m_nColIndex));
  ASSERT(::IsWindow(pSplitter->m_hWnd));
  pObject->m_pParent = pParent;
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a tab window and all its childs (tabs)
BOOL TVisualFramework::execCreateTabWnd(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);
  
  if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass,
                                       pObject->m_pContext);
    pItem->SetIcon(pObject->GetIcon());
    ASSERT(pItem);
    pObject->m_pWnd = pItem->GetSafeWnd();
    ASSERT(pObject->m_pWnd);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
    setTabWndProperties(pObject);
  } else {
    CRect rect;
    pObject->m_pWnd = (CWnd*)pObject->m_pRuntimeClass->CreateObject();
    ASSERT(pObject->m_pWnd);
    pObject->m_pParent = pWnd;
    pObject->m_pWnd->Create(NULL,_T(""),WS_VISIBLE|WS_CHILD,
                                rect,pWnd,TABWND_DEFAULT_ID);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    setTabWndProperties(pObject);
  }
  TVisualObject *pObj;
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    pObj = *it;
    execCreate(pObject->m_pWnd, pObj);
  }
  return TRUE;
}

// Set properties of tab window
void TVisualFramework::setTabWndProperties(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd != NULL);

  // If this is a tab window then set the position of tabs
  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
    if (pObject->m_dwStyle & TVisualObject::TOS_TABTOP)
      pTab->SetTabPos(TTabWnd::TP_TOP);
    else if (pObject->m_dwStyle & TVisualObject::TOS_TABBOTTOM)
      pTab->SetTabPos(TTabWnd::TP_BOTTOM);
  }
  
  // If this is a pane within a tab then check if this pane
  // should be a selected pane
  ASSERT(pObject->m_pParent);
  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    if (pObject->m_dwStyle & TVisualObject::TOS_SELECTED) {
      int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
      pTab->SetActivePane(nIndex);
    }
  }
}

// Get owner pointer (CFrameWnd derived class)
CWnd *TVisualFramework::GetWnd(void)
{
  return m_pOwner;
}

// Get safe owner pointer (CFrameWnd derived class)
CWnd *TVisualFramework::GetSafeWnd(void)
{
  if (m_pOwner && ::IsWindow(m_pOwner->m_hWnd))
    return m_pOwner;
  return NULL;
}

// Get window associated with the visual object specified with its id
// Can be any object (view, splitter or tab window)
CWnd *TVisualFramework::GetObject(DWORD dwId)
{
  TVisualObject *pObject = findObject(dwId);
  if (pObject == NULL)
    return NULL;
  return pObject->m_pWnd;
}

// Get ID associated with the visual object specified with its window pointer
// Can be any object (view, splitter or tab window)
DWORD TVisualFramework::GetObject(CWnd *pWnd)
{
  ASSERT(pWnd);
  TVisualObject *pObject = findObject(pWnd);
  if (pObject == NULL)
    return NULL;
  return pObject->m_dwId;
}

// Return a visual object with the specified id
TVisualObject *TVisualFramework::Get(DWORD dwId)
{
  return findObject(dwId);
}

// Return a visual object with the specified window
TVisualObject *TVisualFramework::Get(CWnd *pWnd)
{
  return findObject(pWnd);
}

// Returns an object that represents the currently active tab within the
// supplied tab window object. This may not be the active pane
TVisualObject *TVisualFramework::GetActiveTab(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (!pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return NULL;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
  int nIndex = pTab->GetTabIndex();
  CWnd *pWnd = pTab->GetTabWnd(nIndex);
  ASSERT(pWnd);
  
  return Get(pWnd);
}

// Set the active tab of the parent tab window. This will not activate the
// pane associated with the active tab.
BOOL TVisualFramework::SetActiveTab(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return FALSE;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  return pTab->SetActivePane(nIndex,FALSE);
}

// Returns TRUE if object is a tab within a tab window
BOOL TVisualFramework::IsTabPane(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a tab window
BOOL TVisualFramework::IsTabWindow(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a pane within a splitter window
BOOL TVisualFramework::IsSplitterPane(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a pane within a splitter window
BOOL TVisualFramework::IsSplitterWindow(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is derived from CView
BOOL TVisualFramework::IsView(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView)))
    return TRUE;
  return FALSE;
}

// Get the count of visual objects
int TVisualFramework::GetCount(void)
{
  return m_ObjectMap.size();
}

// Set font for complete framework
void TVisualFramework::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  
  TVisualObject *pObject;
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
    pObject = mapit->second;
    if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      ((TTabWnd*)pObject->m_pWnd)->SetFont(pFont);
    } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TVisualFormView))) {
      ((TVisualFormView*)pObject->m_pWnd)->SetFont(pFont);
    } else {
      pObject->m_pWnd->SetFont(pFont);
    }
  }
}

// Enable/disable CtrlTab for tab window
void TVisualFramework::EnableCtrlTab(BOOL bEnable)
{
  // If framework is used in an MDI application, then Ctrl+Tab is used to switch
  // among open windows. If it is enabled, Ctrl+Tab will then switch among 
  // tab panes within the MDI child frame window (this disables default Ctrl+Tab
  // for MDI windows).
  // Ctrl+Tab works only if CWinApp derived class overloads PreTranslateMessage
  // and calles ProcessMessage() of the active TVisualFramework object
  m_bEnableCtrlTab = bEnable;
}

// Since CSplitterWnd does not support dynamic creation, this is a chance for
// derived class to supply CSplitterWnd derived class instead of CSplitterWnd
CSplitterWnd *TVisualFramework::CreateSplitter(DWORD dwId)
{
  return new CSplitterWnd;
}

// Set focus to visual object 
BOOL TVisualFramework::SetActivePane(TVisualObject *pObject)
{
  ASSERT(pObject);

  // Cannot set focus to splitter or tab window
  if (!pObject->CanFocus())
    return FALSE;

  // Cannot set focus to disabled window
  BOOL bEnabled;
  if (pObject->IsEnabled(bEnabled) && !bEnabled)
    return FALSE;

  // Build a list that walks thru the object hierarchy from specified 
  // object to the root
  TVisualObjectList list;
  TVisualObject *pObj = pObject;
  while (pObj) {
    list.insert(list.end(),pObj);
    pObj = pObj->m_pOwner;
  }
  
  // Reverse the list so that we can walk from root to the desired object
  list.reverse();
  
  // Now, walk thru the list and set focus as desired
  TVisualObjectList::iterator it;
  for (it = list.begin(); it != list.end(); it ++) {
    pObj = *it;
    if (pObj->m_pOwner && pObj->m_pOwner->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TTabWnd *pTab = (TTabWnd*)pObj->m_pOwner->m_pWnd;
      int nIndex = pTab->GetTabIndex(pObj->m_pWnd);
      if (!pTab->SetActivePane(nIndex)) 
        return FALSE;
    }
  }

  // Update framework owner
  CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  pFrame->SetActiveView((CView*)pObject->m_pWnd);

  return TRUE;
}

// Return a pointer to visual object that represents the currently active pane
TVisualObject *TVisualFramework::GetActivePane(void)
{
  CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  ASSERT(pFrame);
  CView *pView = pFrame->GetActiveView();
  ASSERT(pView);
  return findObject(pView);
}

// Enable/disable a view. Returns TRUE if sucessful
BOOL TVisualFramework::Enable(TVisualObject *pObject, BOOL bEnable)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    pObject->m_bEnabled = bEnable;
    pObject->m_pWnd->EnableWindow(bEnable);
    if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
      int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
      pTab->Enable(nIndex, bEnable);
    }
    return TRUE;
  }
  return FALSE;
}

// Enable/disable a tab
BOOL TVisualFramework::EnableTab(TVisualObject *pObject, BOOL bEnable)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  // Check if parent is a tab window
  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    if (nIndex == pTab->GetTabIndex())
      return FALSE;
    pTab->EnableTab(nIndex, bEnable);
    pObject->m_bEnabled = bEnable;
    return TRUE;
  }
  return FALSE;
}

// Show/hide a tab
BOOL TVisualFramework::ShowTab(TVisualObject *pObject, BOOL bShow)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return FALSE;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  if (nIndex == pTab->GetTabIndex())
    return FALSE;

  pTab->ShowTab(nIndex, bShow);
  return TRUE;
}

// Is object enabled. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE, check bEnabled
BOOL TVisualFramework::IsEnabled(TVisualObject *pObject, BOOL& bEnabled)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  bEnabled = pObject->m_bEnabled;
  return TRUE;
  /*
  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    bEnabled = pObject->m_pWnd->IsWindowEnabled();
    return TRUE;
  }
  return FALSE;
  */
}

// Is tab enabled. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE, check bEnabled
BOOL TVisualFramework::IsTabEnabled(TVisualObject *pObject, BOOL& bEnabled)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    bEnabled = pTab->IsTabEnabled(nIndex);
    return TRUE;
  }
  return FALSE;
}

// Is tab visible. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE then check bVisible.
BOOL TVisualFramework::IsTabVisible(TVisualObject *pObject, BOOL& bVisible)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    bVisible = pTab->IsTabVisible(nIndex);
    return TRUE;
  }
  return FALSE;
}

// This should be called from CWinApp derived PreTranslateMessage to handle
// any framework related messages
BOOL TVisualFramework::ProcessMessage(MSG *pMsg)
{
  ASSERT(pMsg);
  if (pMsg->message == WM_KEYDOWN) {
    // Handle Ctrl+Tab for tab windows
    if (m_bEnableCtrlTab) {
      if ((pMsg->wParam == VK_TAB) && (::GetAsyncKeyState(VK_CONTROL) != 0)) {
        CWnd *pWnd = CWnd::FromHandle(pMsg->hwnd);
        ASSERT(pWnd);
        if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
          return FALSE;
        // If we are in form view then pWnd is a control
        while (pWnd && !pWnd->IsKindOf(RUNTIME_CLASS(CView)))
          pWnd = pWnd->GetParent();
        // Find object for this window
        ASSERT(pWnd);
        TVisualObject *pObject = findObject(pWnd);
        ASSERT(pObject != NULL);
        while (pObject && !pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
          pObject = pObject->m_pOwner;
        if (pObject) {
          TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
          BOOL bShift = (::GetAsyncKeyState(VK_SHIFT) != 0);
          int nIndex = pTab->GetTabIndex();
          int nNdx = nIndex;
          // Switch to new pane (skip invisible and disabled)
          do {
            if (bShift) {
              // Does not work
              nNdx--;
              if (nNdx < 0)
                nNdx = pTab->GetTabCount()-1;
            } else {
              nNdx ++;
              if (nNdx == pTab->GetTabCount())
                nNdx = 0;
            }
          } while (!pTab->SetActivePane(nNdx) && (nNdx != nIndex));
          return TRUE;
        }
      }
    }
  } else if (pMsg->message == WM_SYSKEYDOWN) {
    // Handle hot keys for views (if defined)
    TVisualObject *pObject;
    TVisualObjectMap::iterator mapit;
    for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
      pObject = mapit->second;
      if (pObject->m_cHotKey == pMsg->wParam) {
        if (SetActivePane(pObject))
          return TRUE;
      }
    }
  }

  return FALSE;
}

BOOL TVisualFramework::OnCmdMsg(UINT nID, int nCode, void* pExtra, 
                             AFX_CMDHANDLERINFO* pHandlerInfo) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	return CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

//=============================================================================
// class TVisualFormView
//
// This class implements the code to set the font for all child controls.
// If it belongs to the visual framework (as a pane) then setting the font
// for the framework's panes will automatically set the font for all child
// controls in the form view.
//=============================================================================

static BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam);
static BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam);

IMPLEMENT_DYNAMIC(TVisualFormView, CFormView)

BEGIN_MESSAGE_MAP(TVisualFormView, CFormView)
	//{{AFX_MSG_MAP(TVisualFormView)
	ON_WM_ENABLE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TVisualFormView::TVisualFormView(LPCTSTR lpszTemplateName)
	: CFormView(lpszTemplateName)
{
}

TVisualFormView::TVisualFormView(UINT nIDTemplate)
	: CFormView(nIDTemplate)
{
}

void TVisualFormView::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  ::EnumChildWindows(m_hWnd, ::setChildFont, (LPARAM)pFont);
}

void TVisualFormView::OnEnable(BOOL bEnable) 
{
	CFormView::OnEnable(bEnable);
  ::EnumChildWindows(m_hWnd, ::setChildEnabled, (LPARAM)bEnable);
}

// lParam is a pointer to CFont object
BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam)
{
  CFont *pFont = (CFont*)lparam;
  ASSERT(pFont);
  CWnd *pWnd = CWnd::FromHandle(hwnd);
  ASSERT(pWnd);
  pWnd->SetFont(pFont);
  return TRUE;
}

// lParam is a BOOL
BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam)
{
  BOOL bEnabled = (BOOL)lparam;
  CWnd *pWnd = CWnd::FromHandle(hwnd);
  ASSERT(pWnd);
  pWnd->EnableWindow(bEnabled);
  return TRUE;
}

/*#############################################################################
# End of file VISUALFX.CPP
#############################################################################*/

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer SCA d.o.o.
Serbia Serbia
I am a cofounder of SCA Software, company that specializes in software for process control, visualization and communication. Programming for the last 10 years in C++, Delphi. Visual C++ for the last 6 years. Degree in Electronics Engineering and Telecommunications.

Comments and Discussions