VisualFx.cpp
上传用户:wpp2016
上传日期:2010-02-01
资源大小:1250k
文件大小:63k
源码类别:

Telnet服务器

开发平台:

Visual C++

  1. /*#############################################################################
  2. # VISUALFX.CPP
  3. #
  4. # SCA Software International S.A.
  5. # http://www.scasoftware.com
  6. # scaadmin@scasoftware.com
  7. #
  8. # Copyright (c) 1999 SCA Software International S.A.
  9. #
  10. # Date: 03.01.2000
  11. # Author: Zoran M.Todorovic
  12. #
  13. # This software is provided "AS IS", without a warranty of any kind.
  14. # You are free to use/modify this code but leave this header intact.
  15. #
  16. #############################################################################*/
  17. #include "stdafx.h"
  18. #include <afxpriv.h>        // Needed for WM_SIZEPARENT
  19. #include "VisualFx.h"
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. #ifdef _DEBUG
  25. #define new DEBUG_NEW
  26. #endif
  27. //=============================================================================
  28. // class TTabItem
  29. //
  30. //=============================================================================
  31. // Create a tab item 
  32. TTabItem::TTabItem(CWnd *pParent, LPCTSTR szLabel)
  33. {
  34.   m_pWnd = NULL;
  35.   m_nMinX = m_nMaxX = 0;
  36.   m_bVisible = TRUE;
  37.   m_bEnabled = TRUE;
  38.   m_bWndEnabled = TRUE;
  39.   RECT rect;
  40.   ::ZeroMemory(&rect,sizeof(RECT));
  41.   m_pCaption = new CStatic;
  42.   ASSERT(m_pCaption);
  43.   m_pCaption->Create(szLabel, WS_CHILD|SS_CENTER|WS_VISIBLE,rect,pParent);
  44. }
  45. TTabItem::TTabItem(const TTabItem& obj)
  46. {
  47.   *this = obj;
  48. }
  49. TTabItem& TTabItem::operator=(const TTabItem& obj)
  50. {
  51.   m_pWnd = obj.m_pWnd;
  52.   m_pCaption = obj.m_pCaption;
  53.   m_bWndEnabled = obj.m_bWndEnabled;
  54.   m_bEnabled = obj.m_bEnabled;
  55.   m_bVisible = obj.m_bVisible;
  56.   m_nMinX = obj.m_nMinX;
  57.   m_nMaxX = obj.m_nMaxX;
  58.   return *this;
  59. }
  60. TTabItem::~TTabItem()
  61. {
  62.   // This is done in TVisualFramework::Destroy()
  63.   //if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  64.   //  delete m_pWnd;
  65.   ASSERT(m_pCaption);
  66.   delete m_pCaption;
  67. }
  68. // Set rectangle for tab caption
  69. void TTabItem::SetRect(CRect& rect)
  70. {
  71.   ASSERT(m_pCaption);
  72.   m_pCaption->MoveWindow(&rect);
  73. }
  74. // Set font for tab caption
  75. void TTabItem::SetFont(CFont *pFont)
  76. {
  77.   ASSERT(m_pCaption);
  78.   ASSERT(pFont);
  79.   m_pCaption->SetFont(pFont,FALSE);
  80. }
  81. // Get tab caption text
  82. CString TTabItem::GetText(void)
  83. {
  84.   ASSERT(m_pCaption);
  85.   CString str;
  86.   m_pCaption->GetWindowText(str);
  87.   return str;
  88. }
  89. int TTabItem::GetLength(void)
  90. {
  91.   return m_nMaxX - m_nMinX;
  92. }
  93. // Set tab caption text
  94. void TTabItem::SetText(LPCTSTR szLabel)
  95. {
  96.   ASSERT(m_pCaption);
  97.   ASSERT(szLabel);
  98.   m_pCaption->SetWindowText(szLabel);
  99. }
  100. // Enable/disable a window
  101. void TTabItem::Enable(BOOL bEnable)
  102. {
  103.   m_bWndEnabled = bEnable;
  104. }
  105. // Enable/disable tab caption
  106. void TTabItem::EnableTab(BOOL bEnable)
  107. {
  108.   ASSERT(m_pCaption);
  109.   m_bEnabled = bEnable;
  110.   m_pCaption->EnableWindow(m_bEnabled);
  111. }
  112. // Show/hide tab caption
  113. void TTabItem::ShowTab(BOOL bShow)
  114. {
  115.   ASSERT(m_pCaption);
  116.   m_bVisible = bShow;
  117.   m_pCaption->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
  118. }
  119. CWnd *TTabItem::GetSafeWnd(void)
  120. {
  121.   return (m_pWnd && ::IsWindow(m_pWnd->m_hWnd)) ? m_pWnd : NULL;
  122. }
  123. //=============================================================================
  124. // class TTabWnd
  125. //
  126. //=============================================================================
  127. #define TABWND_DEFAULT_ID 0x2578
  128. #define TABWND_HEIGHT     30    // Height of the gray border between the toolbar 
  129.                                 // and the client area
  130. #define TAB_HEIGHT        20    // Height on the normal tab
  131. #define TABSEL_HEIGHT     20    // Height of the selected tab
  132. #define TAB_SPACE         6     // Add to tab caption text width
  133. #define TAB_DEPL          4     // Distance between the tabs and the client area
  134. #define TAB_MAXLEN        200
  135. IMPLEMENT_DYNCREATE(TTabWnd,CWnd)
  136. BEGIN_MESSAGE_MAP(TTabWnd, CWnd)
  137.   //{{AFX_MSG_MAP(TTabWnd)
  138.   ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
  139.   ON_WM_ERASEBKGND()
  140.   ON_WM_PAINT()
  141.   ON_WM_LBUTTONUP()
  142.   ON_WM_DESTROY()
  143.   ON_WM_SIZE()
  144. ON_WM_CREATE()
  145. //}}AFX_MSG_MAP
  146. END_MESSAGE_MAP()
  147. TTabWnd::TTabWnd()
  148. {
  149.   m_nSelectedTab = 0;
  150.   m_bLockFlag = FALSE;
  151.   m_nTabPos = TP_BOTTOM;
  152.   // cache most used resources
  153.   m_BrushBlack.CreateSolidBrush(RGB(0,0,0));
  154.   m_BrushLGray.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
  155.   m_PenBlack.CreatePen(PS_SOLID, 1, (COLORREF)0);
  156.   m_PenLGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNFACE));
  157.   m_PenWhite.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  158.   m_PenWhite2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  159.   m_PenDGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNSHADOW));
  160.   m_PenDGray2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNSHADOW));
  161. }
  162. TTabWnd::~TTabWnd()
  163. {
  164. }
  165. // Find a tab within this tab window
  166. TTabItem *TTabWnd::findTabItem(int nIndex)
  167. {
  168.   int nNdx = 0;
  169.   TTabItemList::iterator iterator;
  170.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  171.     if (nNdx == nIndex)
  172.       return (*iterator);
  173.     nNdx ++;
  174.   }
  175.   return NULL;
  176. }
  177. // Create a tab window
  178. BOOL TTabWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, 
  179.                      DWORD dwStyle, const RECT& prect, CWnd* pParentWnd, 
  180.                      UINT nID, CCreateContext *pContext)
  181. {
  182.   ASSERT(pParentWnd);
  183.   dwStyle &= ~WS_BORDER;
  184.   CRect rect(prect);
  185.   if (!CWnd::Create(NULL, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext))
  186.     return FALSE;
  187.   if (pParentWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  188.     ((CFrameWnd*)pParentWnd)->ModifyStyleEx(WS_EX_CLIENTEDGE,0,SWP_FRAMECHANGED);
  189.     ((CFrameWnd*)pParentWnd)->RecalcLayout();
  190.   }
  191.   ResizeTab();
  192.   return TRUE;
  193. }
  194. int TTabWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  195. {
  196. if (CWnd::OnCreate(lpCreateStruct) == -1)
  197. return -1;
  198.   createFont();
  199. return 0;
  200. }
  201. void TTabWnd::OnDestroy() 
  202. {
  203.   CWnd::OnDestroy();
  204.   TTabItemList::iterator iterator;
  205.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator ++) {
  206.     delete (*iterator);
  207.   }
  208.   m_TabList.clear();
  209.   // This is done in TVisualFramework
  210.   //if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  211.   //  delete this;
  212. }
  213. // Virtual function to check whether switch to new tab can be done
  214. BOOL TTabWnd::CanSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
  215. {
  216.   return TRUE;
  217. }
  218. // Virtual function (after the switch is done)
  219. void TTabWnd::OnSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
  220. {
  221. }
  222. // Create fonts for tab labels
  223. void TTabWnd::createFont()
  224. {
  225. NONCLIENTMETRICS metrics;
  226. metrics.cbSize = sizeof(metrics);
  227.   ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0);
  228.   CWindowDC wdc(NULL);
  229.   int nLPixY = GetDeviceCaps(wdc.m_hDC, LOGPIXELSY);
  230. m_Font.CreateFontIndirect(&metrics.lfStatusFont);
  231. }
  232. // Add a tab to this window
  233. TTabItem *TTabWnd::addTab(CWnd *pWnd, LPCTSTR szLabel)
  234. {
  235.   ASSERT(pWnd);
  236.   ASSERT(szLabel);
  237.   TTabItem *pItem = new TTabItem(this,szLabel);
  238.   pItem->m_pWnd = pWnd;
  239.   m_TabList.insert(m_TabList.end(), pItem);
  240.   return pItem;
  241. }
  242. int TTabWnd::GetTabLength()
  243. {
  244.   int nLength = 0;
  245.   TTabItemList::iterator iterator;
  246.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  247.     nLength += (*iterator)->GetLength();
  248.   }
  249.   return nLength;
  250. }
  251. // Get index of currently selected tab
  252. int TTabWnd::GetTabIndex(void)
  253. {
  254.   return m_nSelectedTab;
  255. }
  256. // Get number of tabs
  257. int TTabWnd::GetTabCount(void)
  258. {
  259.   return m_TabList.size();
  260. }
  261. // Get index of the tab associated with specified window
  262. int TTabWnd::GetTabIndex(CWnd *pWnd)
  263. {
  264.   ASSERT(pWnd);
  265.   int nIndex = 0;
  266.   TTabItem *pItem;
  267.   TTabItemList::iterator iterator;
  268.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  269.     pItem = *iterator;
  270.     if (pItem->m_pWnd == pWnd)
  271.       return nIndex;
  272.     nIndex ++;
  273.   }
  274.   return -1;
  275. }
  276. // Get pointer to window associated with the specified tab index
  277. CWnd *TTabWnd::GetTabWnd(int index)
  278. {
  279.   TTabItem *pItem = findTabItem(index);
  280.   ASSERT(pItem);
  281.   return ::IsWindow(pItem->m_pWnd->m_hWnd) ? pItem->m_pWnd : NULL;
  282. }
  283. // Get tab caption text of the specified tab
  284. CString TTabWnd::GetTabLabel(int nIndex)
  285. {
  286.   TTabItem *pItem = findTabItem(nIndex);
  287.   ASSERT(pItem);
  288.   return pItem->GetText();
  289. }
  290. // Set text of tab caption
  291. void TTabWnd::SetTabLabel(int nIndex, LPCTSTR szLabel)
  292. {
  293.   ASSERT(szLabel);
  294.   TTabItem *pItem = findTabItem(nIndex);
  295.   ASSERT(pItem);
  296.   pItem->SetText(szLabel);
  297.   invalidateTabArea();
  298. }
  299. // Enable/disable a view
  300. void TTabWnd::Enable(int nIndex, BOOL bEnable)
  301. {
  302.   TTabItem *pItem = findTabItem(nIndex);
  303.   ASSERT(pItem);
  304.   pItem->Enable(bEnable);
  305.   //pItem->m_pWnd->EnableWindow(bEnable);
  306. }
  307. // Cannot disable currently selected tab
  308. void TTabWnd::EnableTab(int nIndex, BOOL bEnable)
  309. {
  310.   ASSERT(nIndex != m_nSelectedTab);
  311.   TTabItem *pItem = findTabItem(nIndex);
  312.   ASSERT(pItem);
  313.   pItem->EnableTab(bEnable);
  314.   invalidateTabArea();
  315. }
  316. // Cannot make invisible currently selected tab
  317. void TTabWnd::ShowTab(int nIndex, BOOL bShow)
  318. {
  319.   ASSERT(nIndex != m_nSelectedTab);
  320.   TTabItem *pItem = findTabItem(nIndex);
  321.   ASSERT(pItem);
  322.   pItem->ShowTab(bShow);
  323.   invalidateTabArea();
  324. }
  325. // Is tab enabled
  326. BOOL TTabWnd::IsTabEnabled(int nIndex)
  327. {
  328.   TTabItem *pItem = findTabItem(nIndex);
  329.   ASSERT(pItem);
  330.   return pItem->m_bEnabled;
  331. }
  332. // Is tab visible
  333. BOOL TTabWnd::IsTabVisible(int nIndex)
  334. {
  335.   TTabItem *pItem = findTabItem(nIndex);
  336.   ASSERT(pItem);
  337.   return pItem->m_bVisible;
  338. }
  339. // Set font
  340. void TTabWnd::SetFont(CFont *pFont)
  341. {
  342.   ASSERT(pFont);
  343.   CWnd::SetFont(pFont);
  344.   m_Font.DeleteObject();
  345.   LOGFONT lf;
  346.   pFont->GetLogFont(&lf);
  347.   m_Font.CreateFontIndirect(&lf);
  348.   invalidateTabArea();
  349. }
  350. // Set position of tabs (top or bottom)
  351. void TTabWnd::SetTabPos(TTabPos nTabPos)
  352. {
  353.   m_nTabPos = nTabPos;
  354. }
  355. // Invalidate rectangle to redraw tabs
  356. void TTabWnd::invalidateTabArea(void)
  357. {
  358.   CRect rect;
  359.   switch (m_nTabPos) {
  360.   case TP_TOP: 
  361.     InvalidateRect(&CRect(0, 0, 32000, TABWND_HEIGHT)); 
  362.     break;
  363.   case TP_BOTTOM:
  364.     GetClientRect(&rect);
  365.     InvalidateRect(&CRect(CPoint(0,rect.Height()-TABWND_HEIGHT), 
  366.                           CSize(32000,TABWND_HEIGHT)));
  367.     break;
  368.   };
  369. }
  370. // Draws a selected tab and returns its height
  371. int TTabWnd::drawSelTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
  372. {
  373.   ASSERT(pItem);
  374.   ASSERT(pDC);
  375.   CString str = pItem->GetText();
  376.   CSize textSize = pDC->GetTextExtent(str);
  377.   textSize.cx += 10;
  378.   if (textSize.cx > TAB_MAXLEN)
  379.     textSize.cx = TAB_MAXLEN;
  380.   int y = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;
  381.   // black border, no bottom line
  382.   pDC->SelectObject(&m_PenBlack);
  383.   pDC->MoveTo(x,y+TABSEL_HEIGHT-1);
  384.   pDC->LineTo(x,y);
  385.   pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);
  386.   pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);
  387.   // left and upper border in white, double line
  388.   pDC->SelectObject(&m_PenWhite2);
  389.   pDC->MoveTo(x+2,y+TABSEL_HEIGHT-1);
  390.   pDC->LineTo(x+2,y+2);
  391.   pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+2);
  392.   // right border, dark gray, double line
  393.   pDC->SelectObject(&m_PenDGray2);
  394.   pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+2);
  395.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);
  396.   // clean up
  397.   pDC->SelectObject(&m_PenLGray);
  398.   pDC->MoveTo(x-1, y+TABSEL_HEIGHT);
  399.   pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT);
  400.   pDC->MoveTo(x-1, y+TABSEL_HEIGHT+1);
  401.   pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);
  402.   // a black line to far left and right
  403.   pDC->SelectObject(&m_PenBlack);
  404.   pDC->MoveTo(0, y+TABSEL_HEIGHT-1);
  405.   pDC->LineTo(x, y+TABSEL_HEIGHT-1);
  406.   pDC->MoveTo(x+textSize.cx+TAB_SPACE+1, y+TABSEL_HEIGHT-1);
  407.   pDC->LineTo(client.right, y+TABSEL_HEIGHT-1);
  408.   // and a white double line
  409.   pDC->SelectObject(&m_PenWhite2);
  410.   if (x!=0) {
  411.     pDC->MoveTo(0, y+TABSEL_HEIGHT+1);
  412.     pDC->LineTo(x, y+TABSEL_HEIGHT+1);
  413.   }
  414.   pDC->MoveTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);
  415.   pDC->LineTo(client.right, y+TABSEL_HEIGHT+1);
  416.   // gray inside
  417.   pDC->FillSolidRect(x+3, y+3,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT, 
  418.                   ::GetSysColor(COLOR_BTNFACE));
  419.   CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), textSize);
  420.   pItem->SetFont(&m_Font);
  421.   pItem->SetRect(rect);
  422.   
  423.   return textSize.cx+TAB_SPACE;
  424. }
  425. // Draw a selected tab at the bottom
  426. int TTabWnd::drawSelTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
  427. {
  428.   ASSERT(pItem);
  429.   ASSERT(pDC);
  430.   CString str = pItem->GetText();
  431.   CSize textSize = pDC->GetTextExtent(str);
  432.   textSize.cx += 10;
  433.   if (textSize.cx > TAB_MAXLEN)
  434.     textSize.cx = TAB_MAXLEN;
  435.   int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;
  436.   // black border, no bottom line
  437.   pDC->SelectObject(&m_PenBlack);
  438.   pDC->MoveTo(x,y);
  439.   pDC->LineTo(x,y+TABSEL_HEIGHT);
  440.   pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);
  441.   pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);
  442.   // left border in white, double line
  443.   pDC->SelectObject(&m_PenWhite2);
  444.   pDC->MoveTo(x+2,y);
  445.   pDC->LineTo(x+2,y+TABSEL_HEIGHT-1);
  446.   // right and bottom border, dark gray, double line
  447.   pDC->SelectObject(&m_PenDGray2);
  448.   pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+TABSEL_HEIGHT-1);
  449.   pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  450.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);
  451.   // a black line to far left and right
  452.   pDC->SelectObject(&m_PenBlack);
  453.   pDC->MoveTo(0, y);
  454.   pDC->LineTo(x, y);
  455.   pDC->MoveTo(x+textSize.cx+TAB_SPACE, y);
  456.   pDC->LineTo(client.right, y);
  457.   // and a gray line to far left and right
  458.   pDC->SelectObject(&m_PenDGray);
  459.   if (x != 0) {
  460.     pDC->MoveTo(0, y-1);
  461.     pDC->LineTo(x, y-1);
  462.   }
  463.   pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y-1);
  464.   pDC->LineTo(client.right, y-1);
  465.   // gray inside
  466.   pDC->FillSolidRect(x+3,y,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT-2, 
  467.                     ::GetSysColor(COLOR_BTNFACE));
  468.   CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2), textSize);
  469.   pItem->SetFont(&m_Font);
  470.   pItem->SetRect(rect);
  471.   
  472.   return textSize.cx+TAB_SPACE;
  473. }
  474. // Draws an unselected tab and returs its height
  475. int TTabWnd::drawTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
  476. {
  477.   ASSERT(pItem);
  478.   ASSERT(pDC);
  479.   CString str = pItem->GetText();
  480.   CSize textSize = pDC->GetTextExtent(str);
  481.   textSize.cx += 10;
  482.   if (textSize.cx > TAB_MAXLEN)
  483.     textSize.cx = TAB_MAXLEN;
  484.   int y = TABWND_HEIGHT-TAB_HEIGHT-TAB_DEPL;
  485.   
  486.   // black border
  487.   pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT)), 
  488.                   &m_BrushBlack);
  489.   pDC->SelectObject(&m_PenWhite);
  490.   pDC->MoveTo(x+1, y+1);
  491.   pDC->LineTo(x+1, y+TAB_HEIGHT-1);
  492.   pDC->MoveTo(x+1, y+1);
  493.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+1);
  494.   pDC->SelectObject(&m_PenDGray);
  495.   pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+1);
  496.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);
  497.   pDC->FillRect(&CRect(CPoint(x+2,y+2), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
  498.                 &m_BrushLGray);
  499.   // clean up
  500.   int dy = TABSEL_HEIGHT-TAB_HEIGHT;
  501.   pDC->FillSolidRect(x, y-dy, textSize.cx+TAB_SPACE, dy, GetSysColor(COLOR_BTNFACE));
  502.   CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), textSize);
  503.   pItem->SetFont(&m_Font);
  504.   pItem->SetRect(rect);
  505.   
  506.   return textSize.cx+TAB_SPACE;
  507. }
  508. // Draw an unselected tab at the bottom
  509. int TTabWnd::drawTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
  510. {
  511.   ASSERT(pItem);
  512.   ASSERT(pDC);
  513.   CString str = pItem->GetText();
  514.   CSize textSize = pDC->GetTextExtent(str);
  515.   textSize.cx += 10;
  516.   if (textSize.cx > TAB_MAXLEN)
  517.     textSize.cx = TAB_MAXLEN;
  518.   int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;
  519.   // black border
  520.   pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT+1)), 
  521.                   &m_BrushBlack);
  522.   // Gray border bottom and right side
  523.   pDC->SelectObject(&m_PenDGray);
  524.   pDC->MoveTo(x+1, y+TAB_HEIGHT-1);
  525.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);
  526.   pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  527.   pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);
  528.   pDC->FillRect(&CRect(CPoint(x+1,y+1), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
  529.                 &m_BrushLGray);
  530.   CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2), textSize);
  531.   pItem->SetFont(&m_Font);
  532.   pItem->SetRect(rect);
  533.   
  534.   return textSize.cx+TAB_SPACE;
  535. }
  536. // Draw edge arround client area
  537. void TTabWnd::drawClient(CDC *pDc, CRect& rect)
  538. {
  539.   ASSERT(pDc);
  540.   CWnd *pParent = GetParent();
  541.   ASSERT(pParent);
  542.   switch (m_nTabPos) {
  543.   case TP_TOP:
  544.     if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  545.       pDc->DrawEdge(&rect, EDGE_ETCHED, BF_TOP);
  546.     }
  547.     pDc->Draw3dRect(0,TABWND_HEIGHT, rect.right, rect.bottom-TABWND_HEIGHT,
  548.                    ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
  549.     pDc->Draw3dRect(1,TABWND_HEIGHT+1, rect.right-2, rect.bottom-TABWND_HEIGHT-2,
  550.                     0, ::GetSysColor(COLOR_3DLIGHT));
  551.     break;
  552.   case TP_BOTTOM:
  553.     if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  554.       pDc->DrawEdge(&rect, EDGE_ETCHED, BF_BOTTOM);
  555.     }
  556.     pDc->Draw3dRect(0,0, rect.right, rect.bottom-TABWND_HEIGHT+1,
  557.                    ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
  558.     pDc->Draw3dRect(1,1, rect.right-2, rect.bottom-TABWND_HEIGHT-1,
  559.                     0, ::GetSysColor(COLOR_3DLIGHT));
  560.     break;
  561.   }
  562. }
  563. void TTabWnd::OnPaint()
  564. {
  565.   CPaintDC dc(this); // device context for painting
  566.   CRect client;
  567.   GetClientRect(&client);
  568.   drawClient(&dc, client);
  569.   int x = 0, nIndex = 0;
  570.   TTabItemList::iterator iterator;
  571.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  572.     TTabItem *pItem = *iterator;
  573.     ASSERT(pItem != NULL);
  574.     if (pItem->m_bVisible) {
  575.       pItem->m_nMinX = x;
  576.       if (nIndex != m_nSelectedTab) {
  577.         switch (m_nTabPos) {
  578.         case TP_TOP: x += drawTabTop(&dc, x, client, pItem); break;
  579.         case TP_BOTTOM: x += drawTabBottom(&dc, x, client, pItem); break;
  580.         }
  581.       } else {
  582.         switch (m_nTabPos) {
  583.         case TP_TOP: x += drawSelTabTop(&dc, x, client, pItem); break;
  584.         case TP_BOTTOM: x += drawSelTabBottom(&dc, x, client, pItem); break;
  585.         }
  586.       }
  587.     }
  588.     pItem->m_nMaxX = x;
  589.     nIndex ++;
  590.   }
  591. }
  592. // Returns tab index that holds specified point
  593. int TTabWnd::HitTest(CPoint& point)
  594. {
  595.   return HitTest(point.x,point.y);
  596. }
  597. // Returns tab index that holds specified point
  598. int TTabWnd::HitTest(int x, int y)
  599. {
  600.   int notsel_y_min, sel_y_min, y_max;
  601.   CRect rect;
  602.   GetClientRect(&rect);
  603.   switch (m_nTabPos) {
  604.   case TP_TOP:
  605.     notsel_y_min = TABWND_HEIGHT - TAB_HEIGHT - TAB_DEPL;
  606.     sel_y_min = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;
  607.     y_max = TABWND_HEIGHT - TAB_DEPL;
  608.     break;
  609.   case TP_BOTTOM:
  610.     notsel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
  611.     sel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
  612.     y_max = rect.Height() - TABWND_HEIGHT + TAB_DEPL + TAB_HEIGHT;
  613.     break;
  614.   };
  615.   int nIndex = 0;
  616.   TTabItemList::iterator iterator;
  617.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  618.     TTabItem *pItem = (*iterator);
  619.     if (pItem->m_bEnabled && pItem->m_bVisible) {
  620.       if (nIndex != m_nSelectedTab && (y < notsel_y_min || y > y_max)) 
  621.         continue;
  622.       if (nIndex == m_nSelectedTab && (y < sel_y_min || y > y_max)) 
  623.         continue;
  624.       if (x >= pItem->m_nMinX && x <= pItem->m_nMaxX)
  625.         return nIndex;
  626.     }
  627.     nIndex++;
  628.   }
  629.   return -1;
  630. }
  631. // Switch focus to specified tab index
  632. BOOL TTabWnd::SetActivePane(int nIndex, BOOL bActivate)
  633. {
  634.   if (nIndex == -1)
  635.     return FALSE;
  636.   if (nIndex == m_nSelectedTab)
  637.     return TRUE;
  638.   TTabItem *pNewPane = findTabItem(nIndex);
  639.   if (!pNewPane->m_bEnabled || !pNewPane->m_bVisible)
  640.     return FALSE;
  641.   TTabItem *pOldPane = NULL;
  642.   if (m_nSelectedTab != -1)
  643.     pOldPane = findTabItem(m_nSelectedTab);
  644.   if (CanSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd)) {
  645.     // Deactivate old pane
  646.     if (m_nSelectedTab != -1) {
  647.       pOldPane->m_pWnd->EnableWindow(FALSE);
  648.       pOldPane->m_pWnd->ShowWindow(SW_HIDE);
  649.     }
  650.     // Activate new pane
  651.     pNewPane->m_pWnd->EnableWindow(pNewPane->m_bWndEnabled ? TRUE : FALSE);
  652.     pNewPane->m_pWnd->ShowWindow(SW_SHOW);
  653.     pNewPane->m_pWnd->SetFocus();
  654.     // Save index of new pane
  655.     m_nSelectedTab = nIndex;
  656.     // Invalidate tab
  657.     invalidateTabArea();
  658.     // Inform derived class
  659.     OnSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd);
  660.     // Update frame window
  661.     if (bActivate) {
  662.       CWnd *pParent = GetParent();
  663.       while (pParent && !pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
  664.         pParent = pParent->GetParent();
  665.       ASSERT(pParent != NULL);
  666.       updateFrame((CFrameWnd*)pParent, pNewPane->m_pWnd);
  667.     }
  668.     return TRUE;
  669.   }
  670.   return FALSE;
  671. }
  672. // Update frame window active view based on newly selected tab item
  673. BOOL TTabWnd::updateFrame(CFrameWnd *pFrame, CWnd *pWnd)
  674. {
  675.   ASSERT(pFrame);
  676.   ASSERT(pWnd);
  677.   if (pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
  678.     // New tab item is a view
  679.     pFrame->SetActiveView((CView*)pWnd);
  680.     return TRUE;
  681.   } else if (pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
  682.     CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  683.     CWnd *pView = pSplitter->GetActivePane();
  684.     if (pView == NULL) {
  685.       CWnd *pTmpView;
  686.       for (int x = 0; x < pSplitter->GetRowCount(); x ++) {
  687.         for (int y = 0; y < pSplitter->GetColumnCount(); y ++) {
  688.           pTmpView = pSplitter->GetPane(x,y);
  689.           if (pTmpView->IsWindowEnabled()) {
  690.             if (updateFrame(pFrame, pTmpView))
  691.               return TRUE;
  692.           }
  693.         }
  694.       }
  695.     }
  696.     if (pView == NULL)
  697.       pView = pSplitter->GetPane(0,0);
  698.     pFrame->SetActiveView((CView*)pView);
  699.   } else if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  700.     TTabWnd *pTab = (TTabWnd*)pWnd;
  701.     int nIndex = pTab->GetTabIndex();
  702.     CWnd *pTabWnd = pTab->GetTabWnd(nIndex);
  703.     if (updateFrame(pFrame, pTabWnd))
  704.       return TRUE;
  705.   }
  706.   return FALSE;
  707. }
  708. // Resize tab
  709. void TTabWnd::ResizeTab(int cx, int cy)
  710. {
  711.   CRect rect;
  712.   CWnd *pParent = (CWnd*)GetParent();
  713.   ASSERT(pParent);
  714.   pParent->GetClientRect(&rect); 
  715.   if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
  716.     CSplitterWnd *splitter = (CSplitterWnd*)pParent;
  717.     ASSERT(pParent);
  718.     int row,col;
  719.     splitter->IsChildPane(this,row,col);
  720.     splitter->RecalcLayout();
  721.   } else if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  722.     m_bLockFlag = TRUE;
  723.     pParent->RepositionBars(0, 0xFFFF, AFX_IDW_PANE_FIRST, CWnd::reposQuery, &rect);
  724.     MoveWindow(rect.left,rect.top,rect.Width(),rect.Height());
  725.     m_bLockFlag = FALSE;
  726.   }
  727.   m_bLockFlag = TRUE; // reentrancy check (might get called recursivly from OnSize)
  728.   CWnd *pWnd;
  729.   TTabItemList::iterator iterator;
  730.   for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
  731.     pWnd = (*iterator)->m_pWnd;
  732.     if (cx == -1 && cy == -1) {
  733.       switch (m_nTabPos) {
  734.       case TP_TOP:
  735.         pWnd->MoveWindow(1, TABWND_HEIGHT+1, 
  736.                          rect.Width()-2, rect.Height()-TABWND_HEIGHT-2);
  737.         break;
  738.       case TP_BOTTOM:
  739.         pWnd->MoveWindow(1, 0, rect.Width()-2, rect.Height()-TABWND_HEIGHT);
  740.         break;
  741.       }
  742.     } else {
  743.       switch (m_nTabPos) {
  744.       case TP_TOP:
  745.         pWnd->MoveWindow(1, TABWND_HEIGHT+1, cx, cy-TABWND_HEIGHT-2);
  746.         break;
  747.       case TP_BOTTOM:
  748.         pWnd->MoveWindow(1, 0, cx, cy-TABWND_HEIGHT);
  749.         break;
  750.       }
  751.     }
  752.   }
  753.   m_bLockFlag=FALSE;
  754. }
  755. // Erase area where the tabs are displayed
  756. BOOL TTabWnd::OnEraseBkgnd(CDC* pDC)
  757. {
  758.   ASSERT(pDC);
  759.   CRect rect;
  760.   GetClientRect(&rect);
  761.   switch (m_nTabPos) {
  762.   case TP_TOP:
  763.     pDC->FillSolidRect(&CRect(0, 0, rect.right, TABWND_HEIGHT), 
  764.                       ::GetSysColor(COLOR_BTNFACE));
  765.     break;
  766.   case TP_BOTTOM:
  767.     pDC->FillSolidRect(&CRect(0, rect.bottom-TABWND_HEIGHT-3, rect.right, rect.bottom), 
  768.                       ::GetSysColor(COLOR_BTNFACE));
  769.     break;
  770.   }
  771.   return TRUE;
  772. }
  773. // Handle couse click on tabs
  774. void TTabWnd::OnLButtonUp(UINT nFlags, CPoint point)
  775. {
  776.   int nNewTab = HitTest(point.x, point.y);
  777.   SetActivePane(nNewTab);
  778.   CWnd::OnLButtonUp(nFlags, point);
  779. }
  780. // Handle resize
  781. LRESULT TTabWnd::OnSizeParent(WPARAM, LPARAM lParam)
  782. {
  783.   if (m_bLockFlag)
  784.     return 0;
  785.   ResizeTab();
  786.   return 0;
  787. }
  788. // Handle resize
  789. void TTabWnd::OnSize(UINT nType, int cx, int cy) 
  790. {
  791.   CWnd::OnSize(nType, cx, cy);
  792.   ResizeTab(cx,cy);
  793. }
  794. // Create a CView derived class as a tab
  795. TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, CRuntimeClass *pViewClass, 
  796.                                  CCreateContext *pContext)
  797. {
  798.   CRect rect, client;
  799.   ASSERT(pViewClass && pContext);
  800.   CWnd *pWnd = (CWnd*)pViewClass->CreateObject();
  801.   if (!pWnd) 
  802.     return NULL;
  803.   
  804.   GetClientRect(&client);
  805.   rect.left = 0;
  806.   rect.top = TABWND_HEIGHT+2;
  807.   rect.right = client.right;
  808.   rect.bottom = client.bottom;
  809.   int dwStyle = AFX_WS_DEFAULT_VIEW;
  810.   if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
  811.     dwStyle &= ~WS_BORDER;
  812.   }
  813.   if (!pWnd->Create(NULL, NULL, dwStyle, rect, this, 13576+m_TabList.size(), pContext))
  814.   {
  815.     TRACE0("Warning: couldn't create client area for tab viewn");
  816.     // pWnd will be cleaned up by PostNcDestroy
  817.     return NULL;
  818.   }
  819.   // Insert new tab object into the list
  820.   TTabItem *pTab = addTab(pWnd,lpszLabel);
  821.   ASSERT(pTab);
  822.   if (m_TabList.size() != 1) {
  823.     pWnd->EnableWindow(FALSE);
  824.     pWnd->ShowWindow(SW_HIDE);
  825.   /*
  826.   // Framework is responsible to set the active view
  827.   } else {
  828.     CWnd *pParent = GetParent();
  829.     if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  830.       ((CFrameWnd*)pParent)->SetActiveView((CView*)pWnd);
  831.     } else if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
  832.       ((CSplitterWnd*)pParent)->SetActivePane(0,0,pWnd);
  833.     }
  834.   */
  835.   }
  836.   return pTab;
  837. }
  838. // Create a splitter window as a tab
  839. TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, int nRows, int nCols, 
  840.                                  CWnd *pWnd, UINT nID)
  841. {
  842.   ASSERT(pWnd);
  843.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  844.   // Moved to TVisualFramework to handle creation of CSplitterWnd derived classes
  845.   //CSplitterWnd *pWnd = new CSplitterWnd;
  846.   //if (!pWnd) 
  847.   //  return NULL;
  848.   int dwStyle = AFX_WS_DEFAULT_VIEW;
  849.   dwStyle &= ~WS_BORDER;
  850.   CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  851.   if (!pSplitter->CreateStatic(this, nRows, nCols, dwStyle, nID)) {
  852.     TRACE0("Warning: couldn't create client area for tab viewn");
  853.     // pWnd will be cleaned up by PostNcDestroy
  854.     return NULL;
  855.   }
  856.   TTabItem *pTab = addTab(pWnd,lpszLabel);
  857.   ASSERT(pTab);
  858.   if (m_TabList.size() != 1) {
  859.     pWnd->EnableWindow(FALSE);
  860.     pWnd->ShowWindow(SW_HIDE);
  861.   } 
  862.   /*
  863.   // Framework will set the active view
  864.   CWnd *paneWnd = pWnd->GetActivePane();
  865.   if (paneWnd) {
  866.     ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  867.   } else {
  868.     paneWnd = pWnd->GetPane(0,0);
  869.     pWnd->SetActivePane(0,0);
  870.     ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  871.   }
  872.   */
  873.   return pTab;
  874. }
  875. //=============================================================================
  876. // class TVisualObject
  877. //
  878. //=============================================================================
  879. // Private constructor
  880. TVisualObject::TVisualObject()
  881. {
  882. }
  883. // Create a plain view
  884. TVisualObject::TVisualObject(DWORD dwId, CCreateContext *pContext, 
  885.                        CRuntimeClass *pClass)
  886. {
  887.   ASSERT(pContext);
  888.   ASSERT(pClass);
  889.   ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)));
  890.   zeroAll();
  891.   m_dwId = dwId;
  892.   m_nObjectType = OT_VIEW;
  893.   m_pContext = pContext;
  894.   m_pRuntimeClass = pClass;
  895.   checkStyle();
  896. }
  897. // Create a view within a tab window or a tab window
  898. TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, CCreateContext *pContext, 
  899.                        CRuntimeClass *pClass, DWORD dwStyle)
  900. {
  901.   ASSERT(szTitle);
  902.   ASSERT(pContext);
  903.   ASSERT(pClass);
  904.   zeroAll();
  905.   m_dwId = dwId;
  906.   if (pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd))) {
  907.     m_nObjectType = OT_TAB;
  908.   } else if (pClass->IsDerivedFrom(RUNTIME_CLASS(CView))) {
  909.     m_nObjectType = OT_TABVIEW;
  910.   } else {
  911.     ASSERT(FALSE);
  912.   }
  913.   m_strTitle = szTitle;
  914.   m_pContext = pContext;
  915.   m_pRuntimeClass = pClass;
  916.   m_dwStyle = dwStyle;
  917.   checkStyle();
  918. }
  919. // Create a splitter window
  920. TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, int nRows, int nCols, 
  921.                        CCreateContext *pContext, DWORD dwStyle)
  922. {
  923.   ASSERT(szTitle);
  924.   ASSERT(pContext);
  925.   ASSERT(nRows);
  926.   ASSERT(nCols);
  927.   zeroAll();
  928.   m_dwId = dwId;
  929.   m_nObjectType = OT_SPLITTER;
  930.   m_strTitle = szTitle;
  931.   m_pContext = pContext;
  932.   m_nRows = nRows;
  933.   m_nCols = nCols;
  934.   m_dwStyle = dwStyle;
  935.   checkStyle();
  936. }
  937. // Create a view within a splitter window
  938. TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, CCreateContext *pContext, 
  939.                        CRuntimeClass *pClass, CSize size, DWORD dwStyle)
  940. {
  941.   ASSERT(pContext);
  942.   ASSERT(pClass);
  943.   ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)) ||
  944.          pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
  945.   zeroAll();
  946.   m_dwId = dwId;
  947.   m_nObjectType = OT_SPLITTERVIEW;
  948.   m_pContext = pContext;
  949.   m_pRuntimeClass = pClass;
  950.   m_nRowIndex = nRow;
  951.   m_nColIndex = nCol;
  952.   m_Size = size;
  953.   m_dwStyle = dwStyle;
  954.   checkStyle();
  955. }
  956. // Create a splitter within a splitter window
  957. TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, int nRows, int nCols, 
  958.                        CCreateContext *pContext, DWORD dwStyle)
  959. {
  960.   ASSERT(pContext);
  961.   ASSERT(nRows);
  962.   ASSERT(nCols);
  963.   zeroAll();
  964.   m_dwId = dwId;
  965.   m_nObjectType = OT_SPLITTERSPLITTER;
  966.   m_pContext = pContext;
  967.   m_nRowIndex = nRow;
  968.   m_nColIndex = nCol;
  969.   m_nRows = nRows;
  970.   m_nCols = nCols;
  971.   m_dwStyle = dwStyle;
  972.   checkStyle();
  973. }
  974. TVisualObject::TVisualObject(const TVisualObject& obj)
  975. {
  976.   zeroAll();
  977.   *this = obj;
  978. }
  979. TVisualObject::~TVisualObject()
  980. {
  981. }
  982. TVisualObject& TVisualObject::operator=(const TVisualObject& obj)
  983. {
  984.   // No need to copy m_ObjectList since it is populated after
  985.   // this code is executed in STL container
  986.   m_nObjectType = obj.m_nObjectType;
  987.   m_dwId = obj.m_dwId;
  988.   m_pWnd = obj.m_pWnd;
  989.   m_pParent = obj.m_pParent;
  990.   m_strTitle = obj.m_strTitle;
  991.   m_nRows = obj.m_nRows;
  992.   m_nCols = obj.m_nCols;
  993.   m_nRowIndex = obj.m_nRowIndex;
  994.   m_nColIndex = obj.m_nColIndex;
  995.   m_pContext = obj.m_pContext;
  996.   m_pRuntimeClass = obj.m_pRuntimeClass;
  997.   m_Size = obj.m_Size;
  998.   m_bEnabled = obj.m_bEnabled;
  999.   m_dwStyle = obj.m_dwStyle;
  1000.   m_cHotKey = obj.m_cHotKey;
  1001.   m_pOwner = obj.m_pOwner;
  1002.   m_pFramework = obj.m_pFramework;
  1003.   return *this;
  1004. }
  1005. void TVisualObject::zeroAll(void)
  1006. {
  1007.   // No need to zero m_ObjectList since it is already empty
  1008.   m_nObjectType = OT_UNKNOWN;
  1009.   m_dwId = 0;
  1010.   m_pWnd = NULL;
  1011.   m_pParent = NULL;
  1012.   m_strTitle = _T("");
  1013.   m_nRows = 0;
  1014.   m_nCols = 0;
  1015.   m_nRowIndex = 0;
  1016.   m_nColIndex = 0;
  1017.   m_pContext = NULL;
  1018.   m_pRuntimeClass = NULL;
  1019.   m_Size = CSize(0,0);
  1020.   m_bEnabled = TRUE;
  1021.   m_dwStyle = 0;
  1022.   m_cHotKey = 0;
  1023.   m_pOwner = NULL;
  1024.   m_pFramework = NULL;
  1025. }
  1026. // Check if style is valid
  1027. void TVisualObject::checkStyle(void)
  1028. {
  1029.   if ((m_dwStyle & TOS_TABTOP) || (m_dwStyle & TOS_TABBOTTOM)) {
  1030.     ASSERT(m_pRuntimeClass);
  1031.     // Tab position valid only for tab window derived classes
  1032.     ASSERT(m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
  1033.   }
  1034.   if (m_dwStyle & TOS_SELECTED) {
  1035.     // Selected valid only for tab panes that are not splitters and tabs
  1036.     // In this case, use TVisualFramework::SetActivePane() to set the active pane
  1037.     // once the framework is created
  1038.     if (m_pRuntimeClass == NULL) {
  1039.       // Splitters canot be dynamically create (m_pRuntimeClass is NULL)
  1040.       ASSERT((m_nObjectType != OT_SPLITTER) && (m_nObjectType != OT_SPLITTERVIEW));
  1041.     } else {
  1042.       ASSERT(!m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
  1043.     }
  1044.   }
  1045. }
  1046. // Delete the window pointer and optionally destroy the window
  1047. void TVisualObject::Destroy(BOOL bDestroyWindow)
  1048. {
  1049.   if (m_pWnd) {
  1050.     if (bDestroyWindow)
  1051.       m_pWnd->DestroyWindow();
  1052.     delete m_pWnd;
  1053.     m_pWnd = NULL;
  1054.   }
  1055. }
  1056. // If this object is a tab window or splitter window that it 
  1057. // cannot be focused
  1058. BOOL TVisualObject::CanFocus(void)
  1059. {
  1060.   ASSERT(m_pWnd);
  1061.   if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)) ||
  1062.       m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1063.   {
  1064.     return FALSE;
  1065.   }
  1066.   return TRUE;
  1067. }
  1068. // Set hot key for this tab object
  1069. void TVisualObject::SetHotKey(CHAR cHotKey)
  1070. {
  1071.   m_cHotKey = cHotKey;
  1072. }
  1073. // Optional: Set description 
  1074. void TVisualObject::SetDescription(LPCTSTR szDesc)
  1075. {
  1076.   m_strDescription = szDesc;
  1077. }
  1078. // Set this object as active pane
  1079. BOOL TVisualObject::SetActivePane(void)
  1080. {
  1081.   ASSERT(m_pFramework);
  1082.   return m_pFramework->SetActivePane(this);
  1083. }
  1084. // Set this tab to be active tab (not the active pane)
  1085. BOOL TVisualObject::SetActiveTab(void)
  1086. {
  1087.   ASSERT(m_pFramework);
  1088.   return m_pFramework->SetActiveTab(this);
  1089. }
  1090. // Enable/disable this object
  1091. BOOL TVisualObject::Enable(BOOL bEnable)
  1092. {
  1093.   ASSERT(m_pFramework);
  1094.   return m_pFramework->Enable(this,bEnable);
  1095. }
  1096. // Enable/disable tab
  1097. BOOL TVisualObject::EnableTab(BOOL bEnable)
  1098. {
  1099.   ASSERT(m_pFramework);
  1100.   return m_pFramework->EnableTab(this,bEnable);
  1101. }
  1102. // SHow/hide this object
  1103. BOOL TVisualObject::ShowTab(BOOL bShow)
  1104. {
  1105.   ASSERT(m_pFramework);
  1106.   return m_pFramework->ShowTab(this,bShow);
  1107. }
  1108. // Is this object enabled
  1109. BOOL TVisualObject::IsEnabled(BOOL& bEnabled)
  1110. {
  1111.   ASSERT(m_pFramework);
  1112.   return m_pFramework->IsEnabled(this,bEnabled);
  1113. }
  1114. // Is this object enabled
  1115. BOOL TVisualObject::IsTabEnabled(BOOL& bEnabled)
  1116. {
  1117.   ASSERT(m_pFramework);
  1118.   return m_pFramework->IsTabEnabled(this,bEnabled);
  1119. }
  1120. // Is this object visible
  1121. BOOL TVisualObject::IsTabVisible(BOOL& bVisible)
  1122. {
  1123.   ASSERT(m_pFramework);
  1124.   return m_pFramework->IsTabVisible(this,bVisible);
  1125. }
  1126. // Returns TRUE if this object is a tab within a tab window
  1127. BOOL TVisualObject::IsTabPane(void)
  1128. {
  1129.   ASSERT(m_pFramework);
  1130.   return m_pFramework->IsTabPane(this);
  1131. }
  1132. // Returns TRUE if this object is a tab window
  1133. BOOL TVisualObject::IsTabWindow(void)
  1134. {
  1135.   ASSERT(m_pFramework);
  1136.   return m_pFramework->IsTabWindow(this);
  1137. }
  1138. // Returns TRUE if this object is a pane within a splitter window
  1139. BOOL TVisualObject::IsSplitterPane(void)
  1140. {
  1141.   ASSERT(m_pFramework);
  1142.   return m_pFramework->IsSplitterPane(this);
  1143. }
  1144. // Returns TRUE if this object is a splitter window
  1145. BOOL TVisualObject::IsSplitterWindow(void)
  1146. {
  1147.   ASSERT(m_pFramework);
  1148.   return m_pFramework->IsSplitterWindow(this);
  1149. }
  1150. // Returns TRUE if this object is derived from CView 
  1151. BOOL TVisualObject::IsView(void)
  1152. {
  1153.   ASSERT(m_pFramework);
  1154.   return m_pFramework->IsView(this);
  1155. }
  1156. // Get object ID
  1157. #ifdef _DEBUG
  1158. DWORD TVisualObject::GetID(void)
  1159. {
  1160.   return m_dwId;
  1161. }
  1162. #endif
  1163. // Get object window
  1164. #ifdef _DEBUG
  1165. CWnd *TVisualObject::GetWnd(void)
  1166. {
  1167.   return m_pWnd;
  1168. }
  1169. #endif
  1170. // Get safe object window
  1171. #ifdef _DEBUG
  1172. CWnd *TVisualObject::GetSafeWnd(void)
  1173. {
  1174.   return ::IsWindow(m_pWnd->m_hWnd) ? m_pWnd : NULL;
  1175. }
  1176. #endif
  1177. #ifdef _DEBUG
  1178. CString TVisualObject::GetTitle(void)
  1179. {
  1180.   return m_strTitle;
  1181. }
  1182. #endif
  1183. #ifdef _DEBUG
  1184. CString TVisualObject::GetDescription(void)
  1185. {
  1186.   return m_strDescription;
  1187. }
  1188. #endif
  1189. #ifdef _DEBUG
  1190. CWnd *TVisualObject::GetParentWnd(void)
  1191. {
  1192.   return m_pParent;
  1193. }
  1194. #endif
  1195. #ifdef _DEBUG
  1196. TVisualFramework *TVisualObject::GetFramework(void)
  1197. {
  1198.   return m_pFramework;
  1199. }
  1200. #endif
  1201. #ifdef _DEBUG
  1202. TVisualObject *TVisualObject::GetOwner(void)
  1203. {
  1204.   return m_pOwner;
  1205. }
  1206. #endif
  1207. //=============================================================================
  1208. // class TVisualFramework
  1209. //
  1210. //=============================================================================
  1211. IMPLEMENT_DYNCREATE(TVisualFramework, CCmdTarget)
  1212. BEGIN_MESSAGE_MAP(TVisualFramework, CCmdTarget)
  1213. //{{AFX_MSG_MAP(TVisualFramework)
  1214. //}}AFX_MSG_MAP
  1215. END_MESSAGE_MAP()
  1216. TVisualFramework::TVisualFramework()
  1217. {
  1218.   m_pOwner = NULL;
  1219.   m_bEnableCtrlTab = TRUE;
  1220. }
  1221. TVisualFramework::~TVisualFramework()
  1222. {
  1223.   if (m_ObjectMap.size() && m_ObjectList.size()) {
  1224.     TRACE0(_T(">>> TVisualFramework::Destroy() called in TVisualFramework destructorn"));
  1225.     TRACE0(_T(">>>   It must be called in CFrameWnd derived class OnDestroy() message handlern"));
  1226.     Destroy();
  1227.   }
  1228. }
  1229. // Find an object in the map with the specified unique id
  1230. TVisualObject *TVisualFramework::findObject(DWORD dwId)
  1231. {
  1232.   TVisualObjectMap::iterator iterator;
  1233.   for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
  1234.     if (dwId == iterator->first)
  1235.       return iterator->second;
  1236.   }
  1237.   return NULL;
  1238. }
  1239. // Find an object in the map with the specified window
  1240. TVisualObject *TVisualFramework::findObject(CWnd *pWnd)
  1241. {
  1242.   TVisualObjectMap::iterator iterator;
  1243.   for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
  1244.     if (pWnd == iterator->second->m_pWnd)
  1245.       return iterator->second;
  1246.   }
  1247.   return NULL;
  1248. }
  1249. // Add object to the container (this is a root level object)
  1250. // There is only one root level object (either splitter or tab)
  1251. BOOL TVisualFramework::Add(TVisualObject *pObject)
  1252. {
  1253.   ASSERT(pObject);
  1254.   ASSERT(m_ObjectList.size() == 0);   // Only one root level object allowed
  1255.   // Root level object is either a view, splitter or a tab
  1256.   ASSERT((pObject->m_nObjectType == TVisualObject::OT_TAB) || 
  1257.          (pObject->m_nObjectType == TVisualObject::OT_VIEW) || 
  1258.          (pObject->m_nObjectType == TVisualObject::OT_SPLITTER));
  1259.   if (findObject(pObject->m_dwId) == NULL) {
  1260.     m_ObjectList.insert(m_ObjectList.end(), pObject);
  1261.     pObject->m_pFramework = this;
  1262.     m_ObjectMap[pObject->m_dwId] = pObject;
  1263.     return TRUE;
  1264.   }
  1265.   ASSERT(FALSE);    // Duplicate object Id
  1266.   return FALSE;
  1267. }
  1268. // Add child object to the specified object
  1269. BOOL TVisualFramework::Add(TVisualObject *pOwner, TVisualObject *pObject)
  1270. {
  1271.   ASSERT(pObject);
  1272.   #ifdef _DEBUG
  1273.   // Validate definition
  1274.   if (pOwner->m_nObjectType == TVisualObject::OT_TAB) {
  1275.     if ((pObject->m_nObjectType != TVisualObject::OT_TABVIEW) && 
  1276.         (pObject->m_nObjectType != TVisualObject::OT_SPLITTER) &&
  1277.         (pObject->m_nObjectType != TVisualObject::OT_TAB))
  1278.     {
  1279.       ASSERT(FALSE);
  1280.     }
  1281.   } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTER) {
  1282.     if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
  1283.         (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
  1284.     {
  1285.       ASSERT(FALSE);
  1286.     }
  1287.   } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTERSPLITTER) {
  1288.     if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
  1289.         (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
  1290.     {
  1291.       ASSERT(FALSE);
  1292.     }
  1293.   } else if (pOwner->m_nObjectType == TVisualObject::OT_TABVIEW) {
  1294.     if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTER))
  1295.     {
  1296.       ASSERT(FALSE);
  1297.     }
  1298.   }
  1299.   #endif
  1300.   if (findObject(pObject->m_dwId) == NULL) {
  1301.     pOwner->m_ObjectList.insert(pOwner->m_ObjectList.end(), pObject);
  1302.     pObject->m_pOwner = pOwner;
  1303.     pObject->m_pFramework = this;
  1304.     m_ObjectMap[pObject->m_dwId] = pObject;
  1305.     return TRUE;
  1306.   }
  1307.   ASSERT(FALSE);    // Duplicate object Id
  1308.   return FALSE;
  1309. }
  1310. // Create all objects within the framework
  1311. BOOL TVisualFramework::Create(CWnd *pWnd)
  1312. {
  1313.   ASSERT(pWnd);
  1314.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
  1315.   // Save owner for later
  1316.   m_pOwner = pWnd;
  1317.   // Disable Ctrl+Tab for MDI applications
  1318.   if (pWnd->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
  1319.     m_bEnableCtrlTab = FALSE;
  1320.   // Walk thru visual object hierarchy and create windows
  1321.   BOOL rc;
  1322.   TVisualObject *pObject;
  1323.   TVisualObjectList::iterator it;
  1324.   for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
  1325.     pObject = *it;
  1326.     rc = execCreate(pWnd, pObject);
  1327.     if (rc == FALSE) {
  1328.       TRACE0(_T("Create visual object failed!n"));
  1329.       return FALSE;
  1330.     }
  1331.   }
  1332.   
  1333.   // Walk thru the map and find first object that can be focused
  1334.   // Then set focus
  1335.   TVisualObjectMap::iterator mapit;
  1336.   for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
  1337.     pObject = mapit->second;
  1338.     if (pObject->CanFocus()) {
  1339.       SetActivePane(pObject);
  1340.       break;
  1341.     }
  1342.   }
  1343.   return TRUE;
  1344. }
  1345. // Destroy all objects in the framework
  1346. void TVisualFramework::Destroy(void)
  1347. {
  1348.   TVisualObject *pObject;
  1349.   // Recursive delete of all objects
  1350.   TVisualObjectList::iterator it;
  1351.   for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
  1352.     pObject = *it;
  1353.     execDestroy(pObject);
  1354.   }
  1355.   // Delete pointers in object map
  1356.   TVisualObjectMap::iterator mapit;
  1357.   for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit++) {
  1358.     pObject = mapit->second;
  1359.     delete pObject;
  1360.   }
  1361.   // Empty all containers (for check in destructor)
  1362.   m_ObjectMap.clear();
  1363.   m_ObjectList.clear();
  1364. }
  1365. // Recursive function to delete all object windows. Does not delete views since
  1366. // they are destroyed by frame.
  1367. void TVisualFramework::execDestroy(TVisualObject *pObject)
  1368. {
  1369.   if (pObject->m_pWnd && ::IsWindow(pObject->m_pWnd->m_hWnd)) {
  1370.     if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1371.       TVisualObject *pObj;
  1372.       TVisualObjectList::iterator it;
  1373.       for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1374.         pObj = *it;
  1375.         execDestroy(pObj);
  1376.       }
  1377.       pObject->Destroy(TRUE);
  1378.     } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
  1379.       TVisualObject *pObj;
  1380.       TVisualObjectList::iterator it;
  1381.       for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1382.         pObj = *it;
  1383.         execDestroy(pObj);
  1384.       }
  1385.       pObject->Destroy(TRUE);
  1386.     } 
  1387.   }
  1388. }
  1389. // Create specified object and all its childs
  1390. BOOL TVisualFramework::execCreate(CWnd *pWnd, TVisualObject *pObject)
  1391. {
  1392.   ASSERT(pWnd);
  1393.   ASSERT(pObject);
  1394.   
  1395.   BOOL rc = FALSE;
  1396.   switch (pObject->m_nObjectType) {
  1397.   case (TVisualObject::OT_SPLITTER): 
  1398.     rc = execCreateSplitter(pWnd,pObject);
  1399.     break;
  1400.   case (TVisualObject::OT_SPLITTERVIEW): 
  1401.     rc = execCreateSplitterView(pWnd, pObject);
  1402.     break;
  1403.   case (TVisualObject::OT_SPLITTERSPLITTER):
  1404.     rc = execCreateSplitterSplitter(pWnd, pObject);
  1405.     break;
  1406.   case (TVisualObject::OT_TAB):
  1407.     rc = execCreateTabWnd(pWnd, pObject);
  1408.     break;
  1409.   case (TVisualObject::OT_TABVIEW):
  1410.     rc = execCreateTabView(pWnd, pObject);
  1411.     break;
  1412.   case (TVisualObject::OT_VIEW):
  1413.     rc = execCreateView(pWnd, pObject);
  1414.     break;
  1415.   }
  1416.   return rc;
  1417. }
  1418. // Create a simple view 
  1419. BOOL TVisualFramework::execCreateView(CWnd *pWnd, TVisualObject *pObject)
  1420. {
  1421.   ASSERT(pWnd);
  1422.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
  1423.   ASSERT(pObject);
  1424.   ASSERT(pObject->m_pContext);
  1425.   ASSERT(pObject->m_pRuntimeClass);
  1426.   CFrameWnd *pFrame = (CFrameWnd*)pWnd;
  1427.   pObject->m_pContext->m_pNewViewClass = pObject->m_pRuntimeClass;
  1428.   pObject->m_pWnd = pFrame->CreateView(pObject->m_pContext);
  1429.   ASSERT(pObject->m_pWnd);
  1430.   ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1431.   
  1432.   pObject->m_pParent = pFrame;
  1433.   setTabWndProperties(pObject);
  1434.   
  1435.   return TRUE;
  1436. }
  1437. // Create a view within a tab window
  1438. BOOL TVisualFramework::execCreateTabView(CWnd *pWnd, TVisualObject *pObject)
  1439. {
  1440.   ASSERT(pWnd);
  1441.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)));
  1442.   ASSERT(pObject);
  1443.   ASSERT(pObject->m_pContext);
  1444.   ASSERT(pObject->m_pRuntimeClass);
  1445.   ASSERT(!pObject->m_strTitle.IsEmpty());
  1446.   TTabWnd *pTab = (TTabWnd*)pWnd;
  1447.   TTabItem *pItem;
  1448.   pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass, 
  1449.                             pObject->m_pContext);
  1450.   ASSERT(pItem);
  1451.   pObject->m_pWnd = pItem->GetSafeWnd();
  1452.   ASSERT(pObject->m_pWnd);
  1453.   ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1454.   pObject->m_pParent = pTab;
  1455.   setTabWndProperties(pObject);
  1456.   return TRUE;
  1457. }
  1458. // Create a splitter window
  1459. BOOL TVisualFramework::execCreateSplitter(CWnd *pWnd, TVisualObject *pObject)
  1460. {
  1461.   ASSERT(pWnd);
  1462.   ASSERT(pObject);
  1463.   // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  1464.   // do not support dynamic creation
  1465.   pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  1466.   ASSERT(pObject->m_pWnd);
  1467.   ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  1468.   if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1469.     TTabWnd *pTab = (TTabWnd*)pWnd;
  1470.     TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_nRows, 
  1471.                                        pObject->m_nCols, pObject->m_pWnd);
  1472.     ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1473.     pObject->m_pParent = pWnd;
  1474.   } else if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
  1475.     ((CSplitterWnd*)pObject->m_pWnd)->CreateStatic(pWnd,pObject->m_nRows,pObject->m_nCols);
  1476.     ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1477.     pObject->m_pParent = pWnd;
  1478.   }
  1479.   setTabWndProperties(pObject);
  1480.   TVisualObjectList::iterator it;
  1481.   for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1482.     execCreate(pObject->m_pWnd, *it);
  1483.   }
  1484.   return TRUE;
  1485. }
  1486. // Create a view within a splitter. Then create all childs of this view.
  1487. BOOL TVisualFramework::execCreateSplitterView(CWnd *pWnd, TVisualObject *pObject)
  1488. {
  1489.   ASSERT(pWnd);
  1490.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  1491.   ASSERT(pObject);
  1492.   
  1493.   CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  1494.   pSplitter->CreateView(pObject->m_nRowIndex, pObject->m_nColIndex, 
  1495.                         pObject->m_pRuntimeClass, pObject->m_Size, 
  1496.                         pObject->m_pContext);
  1497.   pObject->m_pWnd = pSplitter->GetPane(pObject->m_nRowIndex, pObject->m_nColIndex);
  1498.   ASSERT(pObject->m_pWnd);
  1499.   ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1500.   pObject->m_pParent = pSplitter;
  1501.   setTabWndProperties(pObject);
  1502.   TVisualObjectList::iterator it;
  1503.   for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1504.     execCreate(pObject->m_pWnd, *it);
  1505.   }
  1506.   return TRUE;
  1507. }
  1508. // Create a nested splitter window
  1509. BOOL TVisualFramework::execCreateSplitterSplitter(CWnd *pWnd, TVisualObject *pObject)
  1510. {
  1511.   ASSERT(pWnd);
  1512.   ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  1513.   
  1514.   CSplitterWnd *pParent = (CSplitterWnd*)pWnd;
  1515.   // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  1516.   // do not support dynamic creation
  1517.   pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  1518.   ASSERT(pObject->m_pWnd);
  1519.   ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  1520.   CSplitterWnd *pSplitter = (CSplitterWnd*)pObject->m_pWnd;
  1521.   pSplitter->CreateStatic(pParent, pObject->m_nRows, pObject->m_nCols, 
  1522.                           WS_CHILD|WS_VISIBLE|WS_BORDER, 
  1523.                           pParent->IdFromRowCol(pObject->m_nRowIndex,pObject->m_nColIndex));
  1524.   ASSERT(::IsWindow(pSplitter->m_hWnd));
  1525.   pObject->m_pParent = pParent;
  1526.   TVisualObjectList::iterator it;
  1527.   for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1528.     execCreate(pObject->m_pWnd, *it);
  1529.   }
  1530.   return TRUE;
  1531. }
  1532. // Create a tab window and all its childs (tabs)
  1533. BOOL TVisualFramework::execCreateTabWnd(CWnd *pWnd, TVisualObject *pObject)
  1534. {
  1535.   ASSERT(pWnd);
  1536.   ASSERT(pObject);
  1537.   
  1538.   if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1539.     TTabWnd *pTab = (TTabWnd*)pWnd;
  1540.     TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass,
  1541.                                        pObject->m_pContext);
  1542.     ASSERT(pItem);
  1543.     pObject->m_pWnd = pItem->GetSafeWnd();
  1544.     ASSERT(pObject->m_pWnd);
  1545.     ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1546.     pObject->m_pParent = pWnd;
  1547.     setTabWndProperties(pObject);
  1548.   } else {
  1549.     CRect rect;
  1550.     pObject->m_pWnd = (CWnd*)pObject->m_pRuntimeClass->CreateObject();
  1551.     ASSERT(pObject->m_pWnd);
  1552.     pObject->m_pParent = pWnd;
  1553.     pObject->m_pWnd->Create(NULL,_T(""),WS_VISIBLE|WS_CHILD,
  1554.                                 rect,pWnd,TABWND_DEFAULT_ID);
  1555.     ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  1556.     setTabWndProperties(pObject);
  1557.   }
  1558.   TVisualObject *pObj;
  1559.   TVisualObjectList::iterator it;
  1560.   for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
  1561.     pObj = *it;
  1562.     execCreate(pObject->m_pWnd, pObj);
  1563.   }
  1564.   return TRUE;
  1565. }
  1566. // Set properties of tab window
  1567. void TVisualFramework::setTabWndProperties(TVisualObject *pObject)
  1568. {
  1569.   ASSERT(pObject);
  1570.   ASSERT(pObject->m_pWnd != NULL);
  1571.   // If this is a tab window then set the position of tabs
  1572.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1573.     TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
  1574.     if (pObject->m_dwStyle & TVisualObject::TOS_TABTOP)
  1575.       pTab->SetTabPos(TTabWnd::TP_TOP);
  1576.     else if (pObject->m_dwStyle & TVisualObject::TOS_TABBOTTOM)
  1577.       pTab->SetTabPos(TTabWnd::TP_BOTTOM);
  1578.   }
  1579.   
  1580.   // If this is a pane within a tab then check if this pane
  1581.   // should be a selected pane
  1582.   ASSERT(pObject->m_pParent);
  1583.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1584.     TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1585.     if (pObject->m_dwStyle & TVisualObject::TOS_SELECTED) {
  1586.       int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1587.       pTab->SetActivePane(nIndex);
  1588.     }
  1589.   }
  1590. }
  1591. // Get owner pointer (CFrameWnd derived class)
  1592. CWnd *TVisualFramework::GetWnd(void)
  1593. {
  1594.   return m_pOwner;
  1595. }
  1596. // Get safe owner pointer (CFrameWnd derived class)
  1597. CWnd *TVisualFramework::GetSafeWnd(void)
  1598. {
  1599.   if (m_pOwner && ::IsWindow(m_pOwner->m_hWnd))
  1600.     return m_pOwner;
  1601.   return NULL;
  1602. }
  1603. // Get window associated with the visual object specified with its id
  1604. // Can be any object (view, splitter or tab window)
  1605. CWnd *TVisualFramework::GetObject(DWORD dwId)
  1606. {
  1607.   TVisualObject *pObject = findObject(dwId);
  1608.   if (pObject == NULL)
  1609.     return NULL;
  1610.   return pObject->m_pWnd;
  1611. }
  1612. // Get ID associated with the visual object specified with its window pointer
  1613. // Can be any object (view, splitter or tab window)
  1614. DWORD TVisualFramework::GetObject(CWnd *pWnd)
  1615. {
  1616.   ASSERT(pWnd);
  1617.   TVisualObject *pObject = findObject(pWnd);
  1618.   if (pObject == NULL)
  1619.     return NULL;
  1620.   return pObject->m_dwId;
  1621. }
  1622. // Return a visual object with the specified id
  1623. TVisualObject *TVisualFramework::Get(DWORD dwId)
  1624. {
  1625.   return findObject(dwId);
  1626. }
  1627. // Return a visual object with the specified window
  1628. TVisualObject *TVisualFramework::Get(CWnd *pWnd)
  1629. {
  1630.   return findObject(pWnd);
  1631. }
  1632. // Returns an object that represents the currently active tab within the
  1633. // supplied tab window object. This may not be the active pane
  1634. TVisualObject *TVisualFramework::GetActiveTab(TVisualObject *pObject)
  1635. {
  1636.   ASSERT(pObject);
  1637.   ASSERT(pObject->m_pWnd);
  1638.   if (!pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1639.     return NULL;
  1640.   TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
  1641.   int nIndex = pTab->GetTabIndex();
  1642.   CWnd *pWnd = pTab->GetTabWnd(nIndex);
  1643.   ASSERT(pWnd);
  1644.   
  1645.   return Get(pWnd);
  1646. }
  1647. // Set the active tab of the parent tab window. This will not activate the
  1648. // pane associated with the active tab.
  1649. BOOL TVisualFramework::SetActiveTab(TVisualObject *pObject)
  1650. {
  1651.   ASSERT(pObject);
  1652.   ASSERT(pObject->m_pWnd);
  1653.   ASSERT(pObject->m_pParent);
  1654.   if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1655.     return FALSE;
  1656.   TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1657.   int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1658.   return pTab->SetActivePane(nIndex,FALSE);
  1659. }
  1660. // Returns TRUE if object is a tab within a tab window
  1661. BOOL TVisualFramework::IsTabPane(TVisualObject* pObject)
  1662. {
  1663.   ASSERT(pObject);
  1664.   ASSERT(pObject->m_pWnd);
  1665.   ASSERT(pObject->m_pParent);
  1666.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1667.     return TRUE;
  1668.   return FALSE;
  1669. }
  1670. // Returns TRUE if object is a tab window
  1671. BOOL TVisualFramework::IsTabWindow(TVisualObject* pObject)
  1672. {
  1673.   ASSERT(pObject);
  1674.   ASSERT(pObject->m_pWnd);
  1675.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1676.     return TRUE;
  1677.   return FALSE;
  1678. }
  1679. // Returns TRUE if object is a pane within a splitter window
  1680. BOOL TVisualFramework::IsSplitterPane(TVisualObject* pObject)
  1681. {
  1682.   ASSERT(pObject);
  1683.   ASSERT(pObject->m_pWnd);
  1684.   ASSERT(pObject->m_pParent);
  1685.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  1686.     return TRUE;
  1687.   return FALSE;
  1688. }
  1689. // Returns TRUE if object is a pane within a splitter window
  1690. BOOL TVisualFramework::IsSplitterWindow(TVisualObject* pObject)
  1691. {
  1692.   ASSERT(pObject);
  1693.   ASSERT(pObject->m_pWnd);
  1694.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  1695.     return TRUE;
  1696.   return FALSE;
  1697. }
  1698. // Returns TRUE if object is derived from CView
  1699. BOOL TVisualFramework::IsView(TVisualObject *pObject)
  1700. {
  1701.   ASSERT(pObject);
  1702.   ASSERT(pObject->m_pWnd);
  1703.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  1704.     return TRUE;
  1705.   return FALSE;
  1706. }
  1707. // Get the count of visual objects
  1708. int TVisualFramework::GetCount(void)
  1709. {
  1710.   return m_ObjectMap.size();
  1711. }
  1712. // Set font for complete framework
  1713. void TVisualFramework::SetFont(CFont *pFont)
  1714. {
  1715.   ASSERT(pFont);
  1716.   
  1717.   TVisualObject *pObject;
  1718.   TVisualObjectMap::iterator mapit;
  1719.   for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
  1720.     pObject = mapit->second;
  1721.     if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1722.       ((TTabWnd*)pObject->m_pWnd)->SetFont(pFont);
  1723.     } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TVisualFormView))) {
  1724.       ((TVisualFormView*)pObject->m_pWnd)->SetFont(pFont);
  1725.     } else {
  1726.       pObject->m_pWnd->SetFont(pFont);
  1727.     }
  1728.   }
  1729. }
  1730. // Enable/disable CtrlTab for tab window
  1731. void TVisualFramework::EnableCtrlTab(BOOL bEnable)
  1732. {
  1733.   // If framework is used in an MDI application, then Ctrl+Tab is used to switch
  1734.   // among open windows. If it is enabled, Ctrl+Tab will then switch among 
  1735.   // tab panes within the MDI child frame window (this disables default Ctrl+Tab
  1736.   // for MDI windows).
  1737.   // Ctrl+Tab works only if CWinApp derived class overloads PreTranslateMessage
  1738.   // and calles ProcessMessage() of the active TVisualFramework object
  1739.   m_bEnableCtrlTab = bEnable;
  1740. }
  1741. // Since CSplitterWnd does not support dynamic creation, this is a chance for
  1742. // derived class to supply CSplitterWnd derived class instead of CSplitterWnd
  1743. CSplitterWnd *TVisualFramework::CreateSplitter(DWORD dwId)
  1744. {
  1745.   return new CSplitterWnd;
  1746. }
  1747. // Set focus to visual object 
  1748. BOOL TVisualFramework::SetActivePane(TVisualObject *pObject)
  1749. {
  1750.   ASSERT(pObject);
  1751.   // Cannot set focus to splitter or tab window
  1752.   if (!pObject->CanFocus())
  1753.     return FALSE;
  1754.   // Cannot set focus to disabled window
  1755.   BOOL bEnabled;
  1756.   if (pObject->IsEnabled(bEnabled) && !bEnabled)
  1757.     return FALSE;
  1758.   // Build a list that walks thru the object hierarchy from specified 
  1759.   // object to the root
  1760.   TVisualObjectList list;
  1761.   TVisualObject *pObj = pObject;
  1762.   while (pObj) {
  1763.     list.insert(list.end(),pObj);
  1764.     pObj = pObj->m_pOwner;
  1765.   }
  1766.   
  1767.   // Reverse the list so that we can walk from root to the desired object
  1768.   list.reverse();
  1769.   
  1770.   // Now, walk thru the list and set focus as desired
  1771.   TVisualObjectList::iterator it;
  1772.   for (it = list.begin(); it != list.end(); it ++) {
  1773.     pObj = *it;
  1774.     if (pObj->m_pOwner && pObj->m_pOwner->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1775.       TTabWnd *pTab = (TTabWnd*)pObj->m_pOwner->m_pWnd;
  1776.       int nIndex = pTab->GetTabIndex(pObj->m_pWnd);
  1777.       if (!pTab->SetActivePane(nIndex)) 
  1778.         return FALSE;
  1779.     }
  1780.   }
  1781.   // Update framework owner
  1782.   CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  1783.   pFrame->SetActiveView((CView*)pObject->m_pWnd);
  1784.   return TRUE;
  1785. }
  1786. // Return a pointer to visual object that represents the currently active pane
  1787. TVisualObject *TVisualFramework::GetActivePane(void)
  1788. {
  1789.   CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  1790.   ASSERT(pFrame);
  1791.   CView *pView = pFrame->GetActiveView();
  1792.   ASSERT(pView);
  1793.   return findObject(pView);
  1794. }
  1795. // Enable/disable a view. Returns TRUE if sucessful
  1796. BOOL TVisualFramework::Enable(TVisualObject *pObject, BOOL bEnable)
  1797. {
  1798.   ASSERT(pObject);
  1799.   ASSERT(pObject->m_pWnd);
  1800.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
  1801.     pObject->m_bEnabled = bEnable;
  1802.     pObject->m_pWnd->EnableWindow(bEnable);
  1803.     if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1804.       TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1805.       int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1806.       pTab->Enable(nIndex, bEnable);
  1807.     }
  1808.     return TRUE;
  1809.   }
  1810.   return FALSE;
  1811. }
  1812. // Enable/disable a tab
  1813. BOOL TVisualFramework::EnableTab(TVisualObject *pObject, BOOL bEnable)
  1814. {
  1815.   ASSERT(pObject);
  1816.   ASSERT(pObject->m_pWnd);
  1817.   ASSERT(pObject->m_pParent);
  1818.   // Check if parent is a tab window
  1819.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1820.     TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1821.     int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1822.     if (nIndex == pTab->GetTabIndex())
  1823.       return FALSE;
  1824.     pTab->EnableTab(nIndex, bEnable);
  1825.     pObject->m_bEnabled = bEnable;
  1826.     return TRUE;
  1827.   }
  1828.   return FALSE;
  1829. }
  1830. // Show/hide a tab
  1831. BOOL TVisualFramework::ShowTab(TVisualObject *pObject, BOOL bShow)
  1832. {
  1833.   ASSERT(pObject);
  1834.   ASSERT(pObject->m_pWnd);
  1835.   ASSERT(pObject->m_pParent);
  1836.   if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1837.     return FALSE;
  1838.   TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1839.   int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1840.   if (nIndex == pTab->GetTabIndex())
  1841.     return FALSE;
  1842.   pTab->ShowTab(nIndex, bShow);
  1843.   return TRUE;
  1844. }
  1845. // Is object enabled. Returns FALSE if this is not a valid call for the supplied
  1846. // object. If return code is TRUE, check bEnabled
  1847. BOOL TVisualFramework::IsEnabled(TVisualObject *pObject, BOOL& bEnabled)
  1848. {
  1849.   ASSERT(pObject);
  1850.   ASSERT(pObject->m_pWnd);
  1851.   bEnabled = pObject->m_bEnabled;
  1852.   return TRUE;
  1853.   /*
  1854.   if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
  1855.     bEnabled = pObject->m_pWnd->IsWindowEnabled();
  1856.     return TRUE;
  1857.   }
  1858.   return FALSE;
  1859.   */
  1860. }
  1861. // Is tab enabled. Returns FALSE if this is not a valid call for the supplied
  1862. // object. If return code is TRUE, check bEnabled
  1863. BOOL TVisualFramework::IsTabEnabled(TVisualObject *pObject, BOOL& bEnabled)
  1864. {
  1865.   ASSERT(pObject);
  1866.   ASSERT(pObject->m_pWnd);
  1867.   ASSERT(pObject->m_pParent);
  1868.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1869.     TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1870.     int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1871.     bEnabled = pTab->IsTabEnabled(nIndex);
  1872.     return TRUE;
  1873.   }
  1874.   return FALSE;
  1875. }
  1876. // Is tab visible. Returns FALSE if this is not a valid call for the supplied
  1877. // object. If return code is TRUE then check bVisible.
  1878. BOOL TVisualFramework::IsTabVisible(TVisualObject *pObject, BOOL& bVisible)
  1879. {
  1880.   ASSERT(pObject);
  1881.   ASSERT(pObject->m_pWnd);
  1882.   ASSERT(pObject->m_pParent);
  1883.   if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
  1884.     TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  1885.     int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  1886.     bVisible = pTab->IsTabVisible(nIndex);
  1887.     return TRUE;
  1888.   }
  1889.   return FALSE;
  1890. }
  1891. // This should be called from CWinApp derived PreTranslateMessage to handle
  1892. // any framework related messages
  1893. BOOL TVisualFramework::ProcessMessage(MSG *pMsg)
  1894. {
  1895.   ASSERT(pMsg);
  1896.   if (pMsg->message == WM_KEYDOWN) {
  1897.     // Handle Ctrl+Tab for tab windows
  1898.     if (m_bEnableCtrlTab) {
  1899.       if ((pMsg->wParam == VK_TAB) && (::GetAsyncKeyState(VK_CONTROL) != 0)) {
  1900.         CWnd *pWnd = CWnd::FromHandle(pMsg->hwnd);
  1901.         ASSERT(pWnd);
  1902.         if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
  1903.           return FALSE;
  1904.         // If we are in form view then pWnd is a control
  1905.         while (pWnd && !pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  1906.           pWnd = pWnd->GetParent();
  1907.         // Find object for this window
  1908.         //if (!pWnd) return FALSE;//jojo
  1909. ASSERT(pWnd);
  1910.         TVisualObject *pObject = findObject(pWnd);
  1911. //if (pObject==NULL) return FALSE;//jojo
  1912.         ASSERT(pObject != NULL);
  1913.         while (pObject && !pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  1914.           pObject = pObject->m_pOwner;
  1915.         if (pObject) {
  1916.           TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
  1917.           BOOL bShift = (::GetAsyncKeyState(VK_SHIFT) != 0);
  1918.           int nIndex = pTab->GetTabIndex();
  1919.           int nNdx = nIndex;
  1920.           // Switch to new pane (skip invisible and disabled)
  1921.           do {
  1922.             if (bShift) {
  1923.               // Does not work
  1924.               nNdx--;
  1925.               if (nNdx < 0)
  1926.                 nNdx = pTab->GetTabCount()-1;
  1927.             } else {
  1928.               nNdx ++;
  1929.               if (nNdx == pTab->GetTabCount())
  1930.                 nNdx = 0;
  1931.             }
  1932.           } while (!pTab->SetActivePane(nNdx) && (nNdx != nIndex));
  1933.           return TRUE;
  1934.         }
  1935.       }
  1936.     }
  1937.   } else if (pMsg->message == WM_SYSKEYDOWN) {
  1938.     // Handle hot keys for views (if defined)
  1939.     TVisualObject *pObject;
  1940.     TVisualObjectMap::iterator mapit;
  1941.     for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
  1942.       pObject = mapit->second;
  1943.       if (pObject->m_cHotKey == pMsg->wParam) {
  1944.         if (SetActivePane(pObject))
  1945.           return TRUE;
  1946.       }
  1947.     }
  1948.   }
  1949.   return FALSE;
  1950. }
  1951. BOOL TVisualFramework::OnCmdMsg(UINT nID, int nCode, void* pExtra, 
  1952.                              AFX_CMDHANDLERINFO* pHandlerInfo) 
  1953. {
  1954. // TODO: Add your specialized code here and/or call the base class
  1955. return CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  1956. }
  1957. //=============================================================================
  1958. // class TVisualFormView
  1959. //
  1960. // This class implements the code to set the font for all child controls.
  1961. // If it belongs to the visual framework (as a pane) then setting the font
  1962. // for the framework's panes will automatically set the font for all child
  1963. // controls in the form view.
  1964. //=============================================================================
  1965. static BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam);
  1966. static BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam);
  1967. IMPLEMENT_DYNAMIC(TVisualFormView, CFormView)
  1968. BEGIN_MESSAGE_MAP(TVisualFormView, CFormView)
  1969. //{{AFX_MSG_MAP(TVisualFormView)
  1970. ON_WM_ENABLE()
  1971. //}}AFX_MSG_MAP
  1972. END_MESSAGE_MAP()
  1973. TVisualFormView::TVisualFormView(LPCTSTR lpszTemplateName)
  1974. : CFormView(lpszTemplateName)
  1975. {
  1976. }
  1977. TVisualFormView::TVisualFormView(UINT nIDTemplate)
  1978. : CFormView(nIDTemplate)
  1979. {
  1980. }
  1981. void TVisualFormView::SetFont(CFont *pFont)
  1982. {
  1983.   ASSERT(pFont);
  1984.   ::EnumChildWindows(m_hWnd, ::setChildFont, (LPARAM)pFont);
  1985. }
  1986. void TVisualFormView::OnEnable(BOOL bEnable) 
  1987. {
  1988. CFormView::OnEnable(bEnable);
  1989.   ::EnumChildWindows(m_hWnd, ::setChildEnabled, (LPARAM)bEnable);
  1990. }
  1991. // lParam is a pointer to CFont object
  1992. BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam)
  1993. {
  1994.   CFont *pFont = (CFont*)lparam;
  1995.   ASSERT(pFont);
  1996.   CWnd *pWnd = CWnd::FromHandle(hwnd);
  1997.   ASSERT(pWnd);
  1998.   pWnd->SetFont(pFont);
  1999.   return TRUE;
  2000. }
  2001. // lParam is a BOOL
  2002. BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam)
  2003. {
  2004.   BOOL bEnabled = (BOOL)lparam;
  2005.   CWnd *pWnd = CWnd::FromHandle(hwnd);
  2006.   ASSERT(pWnd);
  2007.   pWnd->EnableWindow(bEnabled);
  2008.   return TRUE;
  2009. }
  2010. /*#############################################################################
  2011. # End of file VISUALFX.CPP
  2012. #############################################################################*/