// ==========================================================================
// Class Specification :
// COXTabViewContainer
// ==========================================================================
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
/*
For most applications it's not enough to use only one window to provide its all output.
There are different solutions for this problem like splitters or docking windows.
But they usually have common inconvenience: all windows are shown at the same time.
They take a precious screen space while could be rarely used.
COXShortcutBar control can be used in order to show a number of child windows while
keeping active (fully displayed) only one at a time. But COXShortcutBar was primarily
designed to show icons and have a set of notifications that make sense only while
using such controls as treeview or listview.
Very good example of how the problem could be resolved can be found in Developer Studio
IDE. For instance "Output" window (with "Build", "Debug", "Find in Files..." panes) or
"Result List" window (with "Search", "Lookup", "See Also" and "History" panes). We call
them TabViews.
TabViews can be a good alternative for splitter window when you need to have more than
one view per document. Also TabViews can be used within docking window and used as a
container for associated windows that usually implemented as dialog bars.
So we designed new class that implements TabViews: COXTabViewContainer.
COXTabViewContainer is easy to use. If you previously worked with splitter windows,
the implementation of TabViews will be familiar to you.
Here is the list of steps that should be taken in order to deploy TabViews in
your application:
First Case: COXTabViewContainer will be used as a container for document view(s).
1) Embed a COXTabViewContainer member variable in the parent frame (main frame
window for SDI application, MDIChild window for MDI application).
2) Override the parent frame's CFrameWnd::OnCreateClient member function.
3) From within the overridden OnCreateClient, call the Create member function
of COXTabViewContainer. In this function you have to specify the parent
window and optionally you can specify the initial rectangle, window styles
and window ID.
4) After COXTabViewContainer window was successfully created you can populate
it with window objects using AddPage or InsertPage function. If you are
inserting view object you have to specify runtime class and context
information in order not to break document/view architecture. If you are
adding window object that is not a document view then you have to create
it before adding to COXTabViewContainer window. In AddPage or InsertPage
functions you can specify text that will be used as page title in tab button.
Example:
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
UNREFERENCED_PARAMETER(lpcs);
if(!m_TabViewContainer.Create(this))
return FALSE;
if(!m_TabViewContainer.AddPage(pContext->m_pNewViewClass,
pContext,_T("Primary View")))
return FALSE;
if(!m_TabViewContainer.AddPage(RUNTIME_CLASS(CMyView2),
pContext,_T("View2")))
return FALSE;
m_TabViewContainer.SetActivePageIndex(0);
return TRUE;
}
Second Case: COXTabViewContainer will be used as a container for windows within
control bar.
1) Create your own CControlBar-derived class (you can use our
COXSizeControlBar as parent class if you need sizable docking windows).
Let's call CMyControlBar.
2) Embed a COXTabViewContainer member variable in this class.
3) Override CMyControlBar::OnCreate member function.
4) From within the overridden OnCreate, call the Create member function
of COXTabViewContainer. In this function you have to specify the parent
window and optionally you can specify the initial rectangle, window styles
and window ID.
5) After COXTabViewContainer window was successfully created you can populate
it with window objects using AddPage or InsertPage function. You have to
create window object before adding it to COXTabViewContainer. In AddPage or
InsertPage functions you can specify text that will be used as page title
in tab button.
6) Override CMyControlBar::OnSize member function and resize in it
COXTabViewContainer object as appropriate
Example:
int CMyControlBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COXSizeControlBar::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_TabViewContainer.Create(this,rect))
return -1;
// edit control
if(!edit.Create(WS_CHILD|ES_MULTILINE|ES_AUTOHSCROLL|
ES_AUTOVSCROLL|WS_HSCROLL|WS_VSCROLL,CRect(0,0,0,0),
&m_TabViewContainer,1))
return -1;
m_TabViewContainer.AddPage(&edit,_T("Edit"));
// list box
if(!listBox.Create(WS_CHILD|WS_HSCROLL|WS_VSCROLL,
CRect(0,0,0,0),&m_TabViewContainer,2))
return -1;
m_TabViewContainer.AddPage(&listBox,_T("ListBox"));
// list control
if(!listCtrl.Create(WS_CHILD|LVS_REPORT,
CRect(0,0,0,0),&m_TabViewContainer,3))
return -1;
m_TabViewContainer.AddPage(&listCtrl,_T("List"));
// tree control
if(!treeCtrl.Create(WS_CHILD|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS,
CRect(0,0,0,0),&m_TabViewContainer,4))
return -1;
m_TabViewContainer.AddPage(&treeCtrl,_T("Tree"));
m_TabViewContainer.SetActivePageIndex(0);
return 0;
}
The most challenging problem was to support scrolling functionality for windows
that would be used as COXTabViewContainer pages. Unfortunately, different windows
object that support scrolling (CEdit, CTreeCtrl, CListBox, CListCtrl, CRichEditCtrl,
CEditView, CTreeView, CListView, CScrollView to name just the standrad ones)
implement it in a different way and anyway the handling of scrolling happens
internally in these objects so in order to control it we should have provided
derivations for all of above mentioned classes. Luckily we managed to resolve this
problem in different way so it doesn't require any efforts from programmer's side.
Actually, the only thing you have to do is to declare the class of window object
that is going to be used as COXTabViewContainer page in a specific way.
If you declare you class as follows:
class CMyView : public CEditView
now you have to do that in the following way:
class CMyView : public COXTabViewPage<CEditView>
or if you don't derive your own class and just use an object of existing one, e.g.:
CEdit m_edit;
instead you have to define it as:
COXTabViewPage<CEdit> m_edit;
COXTabViewPage is internal template based Ultimate Toolbox class that was designed
specifically to provide smooth integration of scrolling functionality for any
window within COXTabViewContainer object. All the details of implementation is hidden
for different type of windows is hidden. There is no any public functions that you
should specifically use.
But there is one limitation. The base class that is used for derivation has to have
default constructor. Some classes doesn't have it (e.g. CFormView). You can resolve
this problem through using intermediate class that will be derived from the one
that doesn't have default constructor and implement it. Then you just derive your
window object class that will be used as COXTabViewContainer page from this
intermediate class. Out of standard only CFormView doesn't have default constructor.
Below you will find the steps that should be taken in order to derive classes from
CFormView class.
1) Use Class Wizard to build CFormView derived class on the base of dialog template
as you usually do (let's call it CMyFormView)
2) CMyFormView will have default constructor which uses CFormView constructor with
dialog ID specified.
3) define another class that will be used as view in your application:
class CMyFormTabView : public COXTabViewPage<CMyFormView>
{
protected: // create from serialization only
DECLARE_DYNCREATE(CMyFormView)
};
The steps that should be taken in order to implement COXTabViewContainer in CControlBar
derived window can be applied in general case too. We just decided to show it using
CControlBar derived window because we feel like it's going to be used as parent window
for COXTabViewContainer in most cases.
Above we described the process of creating and populating of COXTabViewContainer object.
In most cases that would be all the code you need. For those who need to change
dynamically the contents of COXTabViewContainer object we provide a set of the
following functions.
In order to remove any page at run time you have to use DeletePage function.
To access scroll bar objects that we use internally in order to provide scrolling
functionality for COXTabViewContainer pages you have to call GetHorzScrollBar and
GetVertScrollBar functions.
To set/retrieve page title that is displayed in corresponding tab button use
GetPageTitle and SetPageTitle functions.
To set/retrive active page index call GetActivePageIndex and SetActivepageIndex
functions.
Refer to the class reference for list and description of all public functions.
Also take look at the following samples that can be found in .\samples\gui
subdirectory of your ultimate Toolbox directory:
TabViews - text editor with three panes: text editor, hex viewer,
list view with statistics on number of unique symbols
found in text.
Dynamic TabViews - MDI application that shows how to add/remove pages
dynamically
#include "OXTabView.h"
*/
#ifndef _TABVIEW_H
#define _TABVIEW_H
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include "OXDllExt.h"
#ifndef __AFXCMN_H__
#include <afxcmn.h>
#define __AFXCMN_H__
#endif
#ifndef __AFXEXT_H__
#include <afxext.h>
#define __AFXEXT_H__
#endif
#ifndef __AFXTEMPL_H__
#include <afxtempl.h>
#define __AFXTEMPL_H__
#endif
#ifndef __AFXPRIV_H__
#include <afxpriv.h>
#define __AFXPRIV_H__
#endif
#ifndef __OXMFCIMPL_H__
#if _MFC_VER < 0x0700
#include <..\src\afximpl.h>
#else
#include <..\src\mfc\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif
#ifndef __AFXCVIEW_H__
#include <afxcview.h> // MFC Common controls
#define __AFXCVIEW_H__
#endif
#ifndef __AFXRICH_H__
#include <afxrich.h>
#define __AFXRICH_H__
#endif
#include "UTB64Bit.h"
typedef struct _tagPAGEINFO
{
CWnd* pWnd;
CString sTitle;
BOOL bHasScrollHorz;
BOOL bHasScrollVert;
SCROLLINFO scrlInfoHorz;
SCROLLINFO scrlInfoVert;
// constructor
_tagPAGEINFO()
{
pWnd=NULL;
sTitle=_T("");
bHasScrollHorz=FALSE;
bHasScrollVert=FALSE;
}
// copy constructor
_tagPAGEINFO(const _tagPAGEINFO& pi)
{
ASSERT(pi.pWnd!=NULL);
pWnd=pi.pWnd;
sTitle=pi.sTitle;
bHasScrollHorz=pi.bHasScrollHorz;
bHasScrollVert=pi.bHasScrollVert;
scrlInfoHorz=pi.scrlInfoHorz;
scrlInfoVert=pi.scrlInfoVert;
}
// assignment operator
_tagPAGEINFO& operator=(const _tagPAGEINFO& pi)
{
if(this==&pi)
{
return *this;
}
ASSERT(pi.pWnd!=NULL);
pWnd=pi.pWnd;
sTitle=pi.sTitle;
bHasScrollHorz=pi.bHasScrollHorz;
bHasScrollVert=pi.bHasScrollVert;
scrlInfoHorz=pi.scrlInfoHorz;
scrlInfoVert=pi.scrlInfoVert;
return *this;
}
void GetScrollInfo(CWnd* pWnd, BOOL bHorzScrlBar)
{
ASSERT_VALID(pWnd);
if(bHorzScrlBar)
{
scrlInfoHorz.cbSize=sizeof(SCROLLINFO);
pWnd->GetScrollInfo(SB_HORZ,&scrlInfoHorz);
if(bHasScrollVert)
{
scrlInfoHorz.nMax-=::GetSystemMetrics(SM_CXVSCROLL);
// scrlInfoHorz.nPage-=::GetSystemMetrics(SM_CXVSCROLL);
if(scrlInfoHorz.nPos+(int)scrlInfoHorz.nPage>scrlInfoHorz.nMax)
{
scrlInfoHorz.nPos=scrlInfoHorz.nMax-scrlInfoHorz.nPage;
}
if(scrlInfoHorz.nTrackPos+(int)scrlInfoHorz.nPage>scrlInfoHorz.nMax)
{
scrlInfoHorz.nTrackPos=scrlInfoHorz.nMax-scrlInfoHorz.nPage;
}
}
if(scrlInfoHorz.nMax==0 || scrlInfoHorz.nPage==0)
{
bHasScrollHorz=FALSE;
}
}
else
{
scrlInfoVert.cbSize=sizeof(SCROLLINFO);
pWnd->GetScrollInfo(SB_VERT,&scrlInfoVert);
if(bHasScrollHorz)
{
scrlInfoVert.nMax-=1;
// scrlInfoVert.nPage-=1;
if(scrlInfoVert.nPos+(int)scrlInfoVert.nPage>scrlInfoVert.nMax)
{
scrlInfoVert.nPos=scrlInfoVert.nMax-scrlInfoVert.nPage;
}
if(scrlInfoVert.nTrackPos+(int)scrlInfoVert.nPage>scrlInfoVert.nMax)
{
scrlInfoVert.nTrackPos=scrlInfoVert.nMax-scrlInfoVert.nPage;
}
}
if(scrlInfoVert.nMax==0 || scrlInfoVert.nPage==0)
{
bHasScrollVert=FALSE;
}
}
}
} PAGEINFO;
////////////////////////////////////////////////////////////////////////
#define ID_TABVIEWCONTAINER_SIGN 0x3a7b4567
#define ID_SPLITTERWIDTH 6
#define ID_TABBTNOVERLAPSIZE 5
#define ID_TABBTNGAPSIZE 3
#define ID_MINSCROLLBARWIDTH 60
#define ID_INITABBTNAREAWIDTH 200
#define ID_SCROLLTABBTNAREASTEP 15
#define IDT_SCROLLPAGE_TIMER 122
#define ID_SCROLLPAGE_DELAY 200
#define IDT_CHECKSCROLLINFO_TIMER 123
#define ID_CHECKSCROLLINFO_PERIOD 1000
//////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// COXTabViewContainer window
class OX_CLASS_DECL COXTabViewContainer : public CWnd
{
DECLARE_DYNCREATE(COXTabViewContainer)
// Construction
public:
// --- In :
// --- Out :
// --- Returns:
// --- Effect : Constructs the object
COXTabViewContainer();
// Attributes
public:
typedef enum _tagHITTEST
{
SCROLLBARHORZ=-1,
SCROLLBARVERT=-2,
SCRLSTARTBTN=-3,
SCRLBACKWARDBTN=-4,
SCRLFORWARDBTN=-5,
SCRLENDBTN=-6,
SPLITTER=-7,
PAGE=-8,
TABBTNAREA=-9,
SIZEBAR=-10,
NONE=-11
} HITTEST;
protected:
// rectangle for TabView Container
//
// scroll buttons rectangles
CRect m_rectScrollToStartBtn;
CRect m_rectScrollBackwardBtn;
CRect m_rectScrollForwardBtn;
CRect m_rectScrollToEndBtn;
// tab button area origin
int m_nTabBtnAreaOrigin;
// the rectangle of the area that is covered by tab buttons
CRect m_rectTabBtnArea;
// tab buttons rectangles (coordinates relative to the top/left
// of m_rectTabBtnArea + m_nTabBtnAreaOrigin)
CArray<CRect,CRect&> m_arrTabBtnRects;
// last saved width of tab buttons area
int m_nLastTabBtnAreaWidth;
// scroll bars rectangles
CRect m_rectScrollBarHorz;
CRect m_rectScrollBarVert;
// rect for splitter
CRect m_rectSplitter;
// rect for size bar
CRect m_rectSizeBar;
// rect for page
CRect m_rectPage;
//
/////////////////////////////////////////
/////////////////////////////////////////
// array of pages
CArray<PAGEINFO,PAGEINFO> m_arrPages;
// index of currently active page
int m_nActivePageIndex;
/////////////////////////////////////////
// scroll style of the container (internal)
DWORD m_dwScrollStyle;
// internal array of unique IDs
CArray<DWORD,DWORD> m_arrUniqueIDs;
// scroll bars controls
CScrollBar m_scrlBarHorz;
CScrollBar m_scrlBarVert;
// the ID of last scroll button that was pressed
HITTEST m_nPressedScrlBtn;
// flag that specifies if last pressed scroll button is still pressed
BOOL m_bIsScrlBtnPressed;
// timer for tab buttons scrolling operations
INT_PTR m_nScrollPageTimer;
// flag that specifies that splitter has been pressed
BOOL m_bIsSplitterPressed;
// fonts to draw text in tab buttons
CFont m_fontTabBtnText;
CFont m_fontActiveTabBtnText;
// Operations
public:
// --- In : lpszClassName - ignored
// lpszWindowName - ignored
// dwStyle - The TabView container's style.
// rect - window rectangle
// pParentWnd - Pointer to the window that is the
// TabView container's parent.
// nID - The TabView container's ID.
// pContext - ignored
// --- Out :
// --- Returns: TRUE if TabView container was successfully created,
// or FALSE otherwise
// --- Effect : Creates the TabView container. Implemented in order to
// support dynamic creation of the object.
virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
CCreateContext* pContext=NULL);
// --- In : pParentWnd - Pointer to the window that is the
// TabView container's parent.
// rect - window rectangle
// dwStyle - The TabView container's style.
// nID - The TabView container's ID.
// --- Out :
// --- Returns: TRUE if TabView container was successfully created,
// or FALSE otherwise
// --- Effect : Creates the TabView container
virtual BOOL Create(CWnd* pParentWnd, CRect rect=CRect(0,0,0,0),
DWORD dwStyle=WS_CHILD|WS_VISIBLE, UINT nID=AFX_IDW_PANE_FIRST);
// --- In : pClass - pointer to runtime class information of
// the new window to be added as new page
// pContext - pointer to context info (refer to the
// description of CCreateContext class)
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns: TRUE if new page was successfully added, or FALSE otherwise
// --- Effect : Adds new page to TabView container. Use this version of
// function if you have to add CView derived class which is
// part of document/view architecture of your application
inline BOOL AddPage(
CRuntimeClass* pClass, CCreateContext* pContext, LPCTSTR lpszTitle=NULL)
{
return InsertPage(GetPageCount(),pClass,pContext,lpszTitle);
}
// --- In : pWnd - pointer to created window to be added
// as new page
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns: TRUE if new page was successfully added, or FALSE otherwise
// --- Effect : Adds new page to TabView container. Use this version of
// function if you have to add any generic window into the
// TabView container.
inline BOOL AddPage(CWnd* pWnd, LPCTSTR lpszTitle=NULL)
{
return InsertPage(GetPageCount(),pWnd,lpszTitle);
}
// --- In : nIndex - zero-based index of the new page
// pClass - pointer to runtime class information of
// the new window to be added as new page
// pContext - pointer to context info (refer to the
// description of CCreateContext class)
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns: TRUE if new page was successfully inserted, or FALSE otherwise
// --- Effect : Inserts new page to TabView container. Use this version of
// function if you have to insert CView derived class which is
// part of document/view architecture of your application
virtual BOOL InsertPage(int nIndex, CRuntimeClass* pClass,
CCreateContext* pContext, LPCTSTR lpszTitle=NULL);
// --- In : nIndex - zero-based index of the new page
// pWnd - pointer to created window to be inserted
// as new page
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns: TRUE if new page was successfully inserted, or FALSE otherwise
// --- Effect : Inserts new page to TabView container. Use this version of
// function if you have to add any generic window into the
// TabView container.
virtual BOOL InsertPage(int nIndex, CWnd* pWnd, LPCTSTR lpszTitle=NULL);
// --- In : pWnd - pointer to the page to be deleted
// bDestroy - flag that specifies if window has to be
// destroyed
// --- Out :
// --- Returns: TRUE if the specified page was successfully deleted,
// or FALSE otherwise
// --- Effect : Deletes existing page from TabView container.
BOOL DeletePage(CWnd* pWnd, BOOL bDestroy=TRUE);
// --- In : nIndex - zero-based index of the page to be deleted
// bDestroy - flag that specifies if window has to be
// destroyed
// --- Out :
// --- Returns: TRUE if the specified page was successfully deleted,
// or FALSE otherwise
// --- Effect : Deletes existing page from TabView container.
virtual BOOL DeletePage(int nIndex, BOOL bDestroy=TRUE);
// --- In : nIndex - zero-based index of the page to be retrieved
// --- Out :
// --- Returns: pointer to the corresponding page, or NULL if out of range
// index was specified
// --- Effect : Retrieves pointer to the page with specified index
inline CWnd* GetPage(int nIndex) const
{
ASSERT(nIndex>=0 && nIndex<GetPageCount());
if(nIndex>=0 && nIndex<GetPageCount())
{
return m_arrPages[nIndex].pWnd;
}
return NULL;
}
// --- In : pWnd - pointer to the page which title
// is to be retrieved
// --- Out :
// --- Returns: title of the corresponding page
// --- Effect : Retrieves title of the specified page
inline CString GetPageTitle(CWnd* pWnd) const
{
ASSERT(pWnd!=NULL);
if(pWnd==NULL)
{
return _T("");
}
int nIndex=-1;
if(!FindPage(pWnd,nIndex) || nIndex==-1)
{
return _T("");
}
return GetPageTitle(nIndex);
}
// --- In : nIndex - zero-based index of the page which title
// is to be retrieved
// --- Out :
// --- Returns: title of the corresponding page
// --- Effect : Retrieves title of the page with specified index
inline CString GetPageTitle(int nIndex) const
{
ASSERT(nIndex>=0 && nIndex<GetPageCount());
if(nIndex>=0 && nIndex<GetPageCount())
{
return m_arrPages[nIndex].sTitle;
}
return _T("");
}
// --- In : pWnd - pointer to the page which title
// is to be set
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns:
// --- Effect : Sets the title of the specified page
inline BOOL SetPageTitle(CWnd* pWnd, LPCTSTR lpszTitle)
{
ASSERT(pWnd!=NULL);
if(pWnd==NULL)
{
return FALSE;
}
int nIndex=-1;
if(!FindPage(pWnd,nIndex) || nIndex==-1)
{
return FALSE;
}
return SetPageTitle(nIndex,lpszTitle);
}
// --- In : nIndex - zero-based index of the page which title
// is to be set
// lpszTitle - text that will be used as page title in
// tab button
// --- Out :
// --- Returns:
// --- Effect : Sets title of the page with specified index
BOOL SetPageTitle(int nIndex, LPCTSTR lpszTitle);
// --- In :
// --- Out :
// --- Returns: number of pages in the Tab View container
// --- Effect : Retrieves the number of pages in the Tab View container
inline int GetPageCount() const { return PtrToInt(m_arrPages.GetSize()); }
// --- In : pTestWnd - pointer to the window to be tested
// as Tab View container's page
// hTestWnd - handle of the window to be tested
// as Tab View container's page
// --- Out : nIndex - reference to the variable where zero-based
// index of the found page will be saved
// --- Returns: TRUE if specified window is Tab View container's page,
// or FALSE otherwise
// --- Effect : Retrieves the flag that specifies whether the specified
// window is Tab View container's page and if it is TRUE then
// save the index of found page into nIndex
inline BOOL FindPage(CWnd* pTestWnd, int& nIndex) const
{
ASSERT(pTestWnd!=NULL);
if(pTestWnd!=NULL)
{
return FindPage(pTestWnd->m_hWnd,nIndex);
}
return FALSE;
}
inline BOOL FindPage(HWND hTestWnd, int& nIndex) const
{
BOOL bResult=FALSE;
for(nIndex=0; nIndex<GetPageCount(); nIndex++)
{
if(m_arrPages[nIndex].pWnd->m_hWnd==hTestWnd)
{
bResult=TRUE;
break;
}
}
return bResult;
}
// --- In : pTestWnd - pointer to the window to be tested
// as Tab View container's page
// hTestWnd - handle of the window to be tested
// as Tab View container's page
// --- Out :
// --- Returns: TRUE if specified window is Tab View container's page,
// or FALSE otherwise
// --- Effect : Retrieves the flag that specifies whether the specified
// window is Tab View container's page
inline BOOL IsPage(HWND hTestWnd) const
{
int nIndex=-1;
return FindPage(hTestWnd,nIndex);
}
inline BOOL IsPage(CWnd* pTestWnd) const
{
int nIndex=-1;
return FindPage(pTestWnd,nIndex);
}
// --- In : pTestWnd - pointer to the window to be tested as
// currently active Tab View container's page
// hTestWnd - handle of the window to be tested as
// currently active Tab View container's page
// --- Out :
// --- Returns: TRUE if specified window is currently active Tab View
// container's page, or FALSE otherwise
// --- Effect : Retrieves the flag that specifies whether the specified
// window is currently active Tab View container's page
inline BOOL IsActivePage(HWND hTestWnd) const
{
int nIndex=-1;
if(FindPage(hTestWnd,nIndex) && GetActivePageIndex()==nIndex)
{
return TRUE;
}
return FALSE;
}
inline BOOL IsActivePage(CWnd* pTestWnd) const
{
int nIndex=-1;
if(FindPage(pTestWnd,nIndex) && GetActivePageIndex()==nIndex)
{
return TRUE;
}
return FALSE;
}
// --- In :
// --- Out :
// --- Returns: index of currently active Tab View container's page
// --- Effect : Retrieves the index of currently active Tab View
// container's page
inline int GetActivePageIndex() const { return m_nActivePageIndex; }
// --- In :
// --- Out :
// --- Returns: pointer to currently active Tab View container's page
// --- Effect : Retrieves the pointer to currently active Tab View
// container's page
inline CWnd* GetActivePage() const
{
if(m_nActivePageIndex>=0 && m_nActivePageIndex<GetPageCount())
{
return m_arrPages[m_nActivePageIndex].pWnd;
}
return NULL;
}
// --- In : pWnd - pointer to the page to be set as active
// --- Out :
// --- Returns: TRUE is specified page was successfully set as active
// --- Effect : Sets the specified page as active one
inline BOOL SetActivePage(CWnd* pWnd)
{
ASSERT(pWnd!=NULL);
if(pWnd!=NULL)
{
int nIndex=-1;
if(FindPage(pWnd,nIndex))
{
return SetActivePageIndex(nIndex);
}
}
return FALSE;
}
// --- In : nIndex - index of the page to be set as active
// --- Out :
// --- Returns: TRUE is page with specified index was successfully set
// as active
// --- Effect : Sets the page with specified index as active one
virtual BOOL SetActivePageIndex(int nIndex);
// --- In :
// --- Out :
// --- Returns: pointer to the horizontal scroll bar control
// --- Effect : Retrieves pointer to the horizontal scroll bar control
inline CScrollBar* GetHorzScrollBar()
{
if(::IsWindow(m_scrlBarHorz.GetSafeHwnd()))
{
return &m_scrlBarHorz;
}
else
{
return NULL;
}
}
// --- In :
// --- Out :
// --- Returns: pointer to the vertical scroll bar control
// --- Effect : Retrieves pointer to the vertical scroll bar control
inline CScrollBar* GetVertScrollBar()
{
if(::IsWindow(m_scrlBarVert.GetSafeHwnd()))
{
return &m_scrlBarVert;
}
else
{
return NULL;
}
}
// --- In : lpszProfileName - name of hive in registry where
// all information about COXTabViewContainer
// will be saved.
// --- Out :
// --- Returns: TRUE if succeeds, or FALSE otherwise.
// --- Effect : Saves COXTabViewContainer state into registry
virtual BOOL SaveState(LPCTSTR lpszProfileName) ;
// --- In : lpszProfileName - name of hive in registry where
// all information about COXTabViewContainer
// was saved.
// --- Out :
// --- Returns: TRUE if succeeds, or FALSE otherwise.
// --- Effect : Loads COXTabViewContainer state from registry
virtual BOOL LoadState(LPCTSTR lpszProfileName);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
//}}AFX_VIRTUAL
protected:
// sets the scroll style of the Tab View container.
// dwScrollStyle can be zero or any combination of WS_VSCROLL
// and WS_HSCROLL styles
virtual void SetScrollStyle(DWORD dwScrollStyle, BOOL bForceToRebuild=FALSE);
// retrieves the scroll style of the Tab View container.
// It can be zero or any combination of WS_VSCROLL and WS_HSCROLL styles
inline DWORD GetScrollStyle() const { return m_dwScrollStyle; }
// initialize internal PAGEINFO structure with scroll info for currently
// active page
void IniScrollInfo();
// calculates position of all Tab View container elements
virtual void CalcLayout();
// calculates position of all tab buttons
virtual void CalcTabBtnRects();
// resize scroll bar controls
void UpdateScrollSizes();
// retrieves unique ID for newly added page
virtual DWORD GetUniqueId();
// empties all internal position info of all Tab View container elements
void EmptyRects();
// redraw the specified scroll button.
void RedrawScrollButton(HITTEST hitTest);
// redraw tab buttons area
inline void RedrawTabBtnArea()
{
ASSERT(::IsWindow(GetSafeHwnd()));
RedrawWindow(m_rectTabBtnArea);
}
// redraw splitter
inline void RedrawSplitter()
{
ASSERT(::IsWindow(GetSafeHwnd()));
RedrawWindow(m_rectSplitter);
}
// redraw all Tab View container elements
inline void RedrawContainer()
{
CRect rect=m_rectScrollToStartBtn;
rect.right=m_rectSplitter.right;
RedrawWindow(rect);
RedrawWindow(m_rectSizeBar);
}
// the following virtual routines are responsible for drawing
// corresponding Tab View container elements
virtual void DrawScrollButtons(CDC* pDC);
virtual void DrawTabBtnArea(CDC* pDC);
virtual void DrawSplitter(CDC* pDC);
virtual void DrawSizeBar(CDC* pDC);
/////////////////////////////////////////////////////////////
// draws specified scroll button
virtual void DrawButton(CDC* pDC, CRect rect, HITTEST nButtonType) const;
// draws tab button for the page with specified index
virtual void DrawTabButton(CDC* pDC, int nIndex) const;
// starts handling "move splitter" or "scroll tab buttons" operation
void StartTracking(const CPoint& point);
// stops "move splitter" or "scroll tab buttons" operation
void StopTracking(const CPoint& point);
// the following test functions return the flag that specifies
// if corresponding scroll tab button operation can be accomplished
//
inline BOOL CanScrollToStart() const
{
ASSERT(m_nTabBtnAreaOrigin<=0);
return (m_nTabBtnAreaOrigin<0 &&
m_rectTabBtnArea.right>m_rectTabBtnArea.left);
}
inline BOOL CanScrollBackward() const { return CanScrollToStart(); }
inline BOOL CanScrollForward() const { return CanScrollToEnd(); }
inline BOOL CanScrollToEnd() const
{
ASSERT(m_nTabBtnAreaOrigin<=0);
if(GetPageCount()>0 &&
m_rectTabBtnArea.right>m_rectTabBtnArea.left)
{
ASSERT(GetPageCount()==m_arrTabBtnRects.GetSize());
CRect rect=m_arrTabBtnRects[GetPageCount()-1];
rect+=m_rectTabBtnArea.TopLeft();
return (rect.right+m_nTabBtnAreaOrigin>m_rectTabBtnArea.right);
}
return FALSE;
}
void EnsureTabBtnVisible(int nIndex, BOOL bPartialOk=FALSE);
/////////////////////////////////////////////////////////////////////
// Implementation
public:
// --- In :
// --- Out :
// --- Returns:
// --- Effect : Destructs the object
virtual ~COXTabViewContainer();
// --- In :
// --- Out :
// --- Returns:
// --- Effect : Updates scroll info for currently active page
void UpdateScrollInfo();
// --- In :
// --- Out :
// --- Returns:
// --- Effect : Updates scroll state for currently active page
void UpdateScrollState();
// --- In : point - point in Tab View container client coordinates
// to be tested
// --- Out :
// --- Returns: if equals or more than zero then the return value specifies
// the corresponding index of tab button, otherwise it can be
// one of the following value:
//
// SCROLLBARHORZ - horizontal scroll bar control
// SCROLLBARVERT - vertical scroll bar control
// SCRLSTARTBTN - scroll button "scroll to start"
// SCRLBACKWARDBTN - scroll button "scroll backward"
// SCRLFORWARDBTN - scroll button "scroll forward"
// SCRLENDBTN - scroll button "scroll to end"
// SPLITTER - splitter
// PAGE - currently active page
// TABBTNAREA - tab buttons area
// SIZEBAR - sizebar
// NONE - out of Tab View container
//
// --- Effect : Retrieves the element of Tab View container over which
// the specified point is located
int HitTest(const CPoint& point) const;
// --- In : nScrlBtn - scroll button identifier. it can be one
// of the following:
//
// SCRLSTARTBTN - scroll button "scroll to start"
// SCRLBACKWARDBTN - scroll button "scroll backward"
// SCRLFORWARDBTN - scroll button "scroll forward"
// SCRLENDBTN - scroll button "scroll to end"
//
// --- Out :
// --- Returns:
// --- Effect : Scroll tab btn area in the specified direction
void ScrollPage(HITTEST nScrlBtn);
protected:
// special command routing to frame
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
// Generated message map functions
protected:
// overwrite standard handlers to overcome some problems with MFC
// standard painting routine
//{{AFX_MSG(COXTabViewContainer)
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnPaint();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
afx_msg void OnTimer(OXTPARAM nIDEvent);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnCancelMode();
afx_msg void OnDestroy();
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////////////////////////
// --- In : pWnd - pointer to valid window
// bOnlyActive - if it is TRUE then the function will return
// non-NULL value only if specified window
// is current active page of parent TabView
// container
// --- Out :
// --- Returns: pointer to parent TabView container, or NULL if parent of
// the specified window is not TabView container
// --- Effect : Retrieves parent TabView container foor specified window
OX_API_DECL COXTabViewContainer* PASCAL
GetParentTabViewContainer(CWnd* pWnd, BOOL bOnlyActive=TRUE);
///////////////////////////////////////////////////////////////////////////
template<class PARENTWND>
class COXTabViewPage : public PARENTWND
{
public:
// Contruction
COXTabViewPage();
COXTabViewPage(UINT nIDTemplate); // Use this constructor for form views
// Scrolling Functions
virtual CScrollBar* GetScrollBarCtrl(int nBar) const;
// return sibling scrollbar control (or NULL if none)
// --- In : pMsg - Points to a MSG structure that contains the
// message to process
// --- Out :
// --- Returns: Nonzero if the message was translated and should not be
// dispatched; 0 if the message was not translated and should be
// dispatched.
// --- Effect : Used by class CWinApp to translate window messages before
// they are dispatched to the TranslateMessage() and
// DispatchMessage() Windows functions
virtual BOOL PreTranslateMessage(MSG* pMsg);
protected:
// timer for checking scroll info of currently active page
INT_PTR m_nCheckScrollInfoTimer;
BOOL m_bCurrentlyScrolling;
BOOL m_bHasInternalScrollBars;
BOOL m_bNeedsInternalRedrawing;
int m_nLastHorzPos;
int m_nLastVertPos;
protected:
// --- In : msg - message ID
// wp - WPARAM
// lp - LPARAM
// --- Out :
// --- Returns: result of message handling. Different for different messages.
// --- Effect : Handle all messages that go to the window
virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
// call this function to force the control to check its scroll state
virtual void CheckScrollInfo();
};
template<class PARENTWND>
COXTabViewPage<PARENTWND>::COXTabViewPage()
{
m_nCheckScrollInfoTimer=-1;
m_bCurrentlyScrolling=FALSE;
m_bHasInternalScrollBars=PARENTWND::IsKindOf(RUNTIME_CLASS(CEdit)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CEditView)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CListBox)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CTreeCtrl)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CTreeView)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CListCtrl)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CListView)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CRichEditCtrl)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CRichEditView));
m_bNeedsInternalRedrawing=PARENTWND::IsKindOf(RUNTIME_CLASS(CEdit)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CEditView)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CListBox));
m_nLastHorzPos=0;
m_nLastVertPos=0;
}
template<class PARENTWND>
COXTabViewPage<PARENTWND>::COXTabViewPage(UINT nIDTemplate) :
PARENTWND(nIDTemplate)
{
ASSERT(PARENTWND::IsKindOf(RUNTIME_CLASS(CFormView)));
m_nCheckScrollInfoTimer=-1;
m_bCurrentlyScrolling=FALSE;
m_bHasInternalScrollBars=FALSE;
m_nLastHorzPos=0;
m_nLastVertPos=0;
}
template<class PARENTWND>
CScrollBar* COXTabViewPage<PARENTWND>::GetScrollBarCtrl(int nBar) const
{
COXTabViewContainer* pContainer=GetParentTabViewContainer((CWnd*)this);
if(pContainer!=NULL)
{
ASSERT(nBar==SB_HORZ || nBar==SB_VERT);
CScrollBar* pScrlBar=NULL;
if(nBar==SB_HORZ)
{
if((GetStyle()&WS_HSCROLL)!=WS_HSCROLL)
{
pScrlBar=pContainer->GetHorzScrollBar();
}
}
else if(nBar==SB_VERT)
{
if((GetStyle()&WS_VSCROLL)!=WS_VSCROLL)
{
pScrlBar=pContainer->GetVertScrollBar();
}
}
if(pScrlBar!=NULL)
{
return pScrlBar;
}
}
return PARENTWND::GetScrollBarCtrl(nBar);
}
template<class PARENTWND>
LRESULT COXTabViewPage<PARENTWND>::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT_VALID(this);
static BOOL g_bIgnoreResize=FALSE;
switch(msg)
{
case WM_CREATE:
{
LRESULT lResult=PARENTWND::WindowProc(msg,wp,lp);
if(lResult==-1)
return -1;
if(m_bHasInternalScrollBars)
{
m_nCheckScrollInfoTimer=SetTimer(IDT_CHECKSCROLLINFO_TIMER,
ID_CHECKSCROLLINFO_PERIOD,NULL);
if(m_nCheckScrollInfoTimer==-1)
{
return -1;
}
}
return 0;
}
case WM_DESTROY:
{
if(m_nCheckScrollInfoTimer!=-1)
{
KillTimer(m_nCheckScrollInfoTimer);
m_nCheckScrollInfoTimer=-1;
}
break;
}
case WM_TIMER:
{
if((int)wp==m_nCheckScrollInfoTimer)
{
if(m_bCurrentlyScrolling || ::GetCapture()==GetSafeHwnd())
{
return 0;
}
COXTabViewContainer* pContainer=GetParentTabViewContainer(this);
if(pContainer!=NULL)
{
ASSERT(m_bHasInternalScrollBars);
if(m_bNeedsInternalRedrawing)
{
pContainer->UpdateScrollState();
pContainer->UpdateScrollInfo();
}
else
{
g_bIgnoreResize=TRUE;
pContainer->UpdateScrollState();
pContainer->UpdateScrollInfo();
g_bIgnoreResize=FALSE;
}
}
return 0;
}
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
{
if(LOWORD(wp)==SB_ENDSCROLL)
{
m_bCurrentlyScrolling=FALSE;
m_nLastHorzPos=GetScrollPos(SB_HORZ);
m_nLastVertPos=GetScrollPos(SB_VERT);
}
else
{
m_bCurrentlyScrolling=TRUE;
}
COXTabViewContainer* pContainer=GetParentTabViewContainer(this);
if(pContainer!=NULL && m_bHasInternalScrollBars)
{
// set current scroll info to the window
SCROLLINFO scrollInfo={ sizeof(SCROLLINFO) };
GetScrollInfo((msg==WM_HSCROLL ? SB_HORZ : SB_VERT),&scrollInfo);
BOOL bHandled=FALSE;
if(PARENTWND::IsKindOf(RUNTIME_CLASS(CListCtrl)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CListView)))
{
bHandled=TRUE;
switch(LOWORD(wp))
{
case SB_THUMBTRACK:
{
CListCtrl* pListCtrl=(CListCtrl*)this;
CRect rectItem;
pListCtrl->GetItemRect(0,rectItem,LVIR_BOUNDS);
int nPos=HIWORD(wp);
if(msg==WM_HSCROLL && nPos!=m_nLastHorzPos)
{
SendMessage(LVM_SCROLL,(nPos-m_nLastHorzPos),0);
m_nLastHorzPos=GetScrollPos(SB_HORZ);
}
else if(msg==WM_VSCROLL && nPos!=m_nLastVertPos)
{
SendMessage(LVM_SCROLL,0,
(nPos-m_nLastVertPos)*rectItem.Height());
m_nLastVertPos=GetScrollPos(SB_VERT);
}
}
break;
default:
{
bHandled=FALSE;
}
break;
}
}
if(!bHandled)
{
if(m_bNeedsInternalRedrawing)
{
PARENTWND::WindowProc(msg,wp,lp);
pContainer->UpdateScrollInfo();
}
else
{
PARENTWND::WindowProc(msg,wp,lp);
g_bIgnoreResize=TRUE;
pContainer->UpdateScrollInfo();
g_bIgnoreResize=FALSE;
}
}
}
else
{
if(pContainer!=NULL && PARENTWND::IsKindOf(RUNTIME_CLASS(CScrollView)))
{
((CScrollView*)this)->OnScroll((msg==WM_HSCROLL ?
MAKEWORD(LOWORD(wp),-1) : MAKEWORD(-1,LOWORD(wp))),HIWORD(wp));
}
else
{
PARENTWND::WindowProc(msg,wp,lp);
}
}
return 0;
}
case WM_SIZE:
{
COXTabViewContainer* pContainer=GetParentTabViewContainer(this);
if(pContainer!=NULL)
{
if(g_bIgnoreResize)
{
return 0;
}
if((GetStyle()&WS_VISIBLE)!=WS_VISIBLE)
{
PARENTWND::WindowProc(msg,wp,lp);
}
else if(PARENTWND::IsKindOf(RUNTIME_CLASS(CRichEditView)) ||
PARENTWND::IsKindOf(RUNTIME_CLASS(CRichEditCtrl)))
{
if(!m_bCurrentlyScrolling)
{
g_bIgnoreResize=TRUE;
pContainer->UpdateScrollState();
pContainer->UpdateScrollInfo();
ModifyStyle(WS_VISIBLE,NULL);
PARENTWND::WindowProc(msg,wp,lp);
ShowScrollBar(SB_HORZ,FALSE);
ShowScrollBar(SB_VERT,FALSE);
ModifyStyle(NULL,WS_VISIBLE);
Invalidate();
g_bIgnoreResize=FALSE;
}
}
else if(m_bNeedsInternalRedrawing)
{
int nTopIndex=CB_ERR;
if(PARENTWND::IsKindOf(RUNTIME_CLASS(CListBox)))
nTopIndex=((CListBox*)this)->GetTopIndex();
pContainer->UpdateScrollState();
pContainer->UpdateScrollInfo();
PARENTWND::WindowProc(msg,wp,lp);
if(PARENTWND::IsKindOf(RUNTIME_CLASS(CListBox)))
{
if(((CListBox*)this)->GetTopIndex()!=nTopIndex)
RedrawWindow();
}
}
else if(!m_bHasInternalScrollBars)
{
ModifyStyle(WS_VISIBLE,NULL);
pContainer->GetHorzScrollBar()->
ModifyStyle(WS_VISIBLE,NULL);
pContainer->GetVertScrollBar()->
ModifyStyle(WS_VISIBLE,NULL);
PARENTWND::WindowProc(msg,wp,lp);
ShowScrollBar(SB_HORZ,FALSE);
ShowScrollBar(SB_VERT,FALSE);
pContainer->GetHorzScrollBar()->
ModifyStyle(NULL,WS_VISIBLE);
pContainer->GetVertScrollBar()->
ModifyStyle(NULL,WS_VISIBLE);
ModifyStyle(NULL,WS_VISIBLE);
}
else
{
CSize sizeScrollPos(-1,-1);
if(PARENTWND::IsKindOf(RUNTIME_CLASS(CTreeCtrl)))
{
sizeScrollPos.cx=pContainer->GetHorzScrollBar()->
GetScrollPos();
sizeScrollPos.cy=pContainer->GetVertScrollBar()->
GetScrollPos();
}
g_bIgnoreResize=TRUE;
pContainer->UpdateScrollState();
pContainer->UpdateScrollInfo();
ModifyStyle(WS_VISIBLE,NULL);
PARENTWND::WindowProc(msg,wp,lp);
ShowScrollBar(SB_HORZ,FALSE);
g_bIgnoreResize=FALSE;
ShowScrollBar(SB_VERT,FALSE);
ModifyStyle(NULL,WS_VISIBLE);
if(PARENTWND::IsKindOf(RUNTIME_CLASS(CTreeCtrl)))
{
if(sizeScrollPos.cx!=pContainer->GetHorzScrollBar()->
GetScrollPos() ||
sizeScrollPos.cy!=pContainer->GetVertScrollBar()->
GetScrollPos())
{
RedrawWindow();
}
}
}
return 0;
}
break;
}
default:
{
if(m_bHasInternalScrollBars)
{
if(msg==WM_SYSCOMMAND || msg==WM_SHOWWINDOW ||
(msg>=WM_KEYFIRST && msg<=WM_KEYLAST) ||
(msg>=WM_SYSKEYFIRST && msg<=WM_SYSKEYLAST) ||
(msg>=WM_MOUSEFIRST && msg<=WM_MOUSELAST && msg!=WM_MOUSEMOVE))
{
CheckScrollInfo();
}
}
break;
}
}
// I don't handle it: pass along
return PARENTWND::WindowProc(msg,wp,lp);
}
template<class PARENTWND>
BOOL COXTabViewPage<PARENTWND>::PreTranslateMessage(MSG* pMsg)
{
if(m_bHasInternalScrollBars && pMsg->hwnd!=GetSafeHwnd() &&
(pMsg->message==WM_SYSCOMMAND || pMsg->message==WM_SHOWWINDOW ||
(pMsg->message>=WM_KEYFIRST && pMsg->message<=WM_KEYLAST) ||
(pMsg->message>=WM_SYSKEYFIRST && pMsg->message<=WM_SYSKEYLAST) ||
(pMsg->message>=WM_MOUSEFIRST && pMsg->message<=WM_MOUSELAST &&
pMsg->message!=WM_MOUSEMOVE)))
{
CheckScrollInfo();
}
return PARENTWND::PreTranslateMessage(pMsg);
}
template<class PARENTWND>
void COXTabViewPage<PARENTWND>::CheckScrollInfo()
{
ASSERT(::IsWindow(GetSafeHwnd()));
PostMessage(WM_TIMER,m_nCheckScrollInfoTimer);
}
#endif // _TABVIEW_H
///////////////////////////////////////////////////////////////////////////