// ========================================================================== // 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 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 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 { 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 #define __AFXCMN_H__ #endif #ifndef __AFXEXT_H__ #include #define __AFXEXT_H__ #endif #ifndef __AFXTEMPL_H__ #include #define __AFXTEMPL_H__ #endif #ifndef __AFXPRIV_H__ #include #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 // MFC Common controls #define __AFXCVIEW_H__ #endif #ifndef __AFXRICH_H__ #include #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 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 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 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=0 && nIndex=0 && nIndex=0 && nIndexm_hWnd,nIndex); } return FALSE; } inline BOOL FindPage(HWND hTestWnd, int& nIndex) const { BOOL bResult=FALSE; for(nIndex=0; nIndexm_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_nActivePageIndexm_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); afx_msg void OnTimer(UINT 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 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 COXTabViewPage::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 COXTabViewPage::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 CScrollBar* COXTabViewPage::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 LRESULT COXTabViewPage::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 BOOL COXTabViewPage::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 void COXTabViewPage::CheckScrollInfo() { ASSERT(::IsWindow(GetSafeHwnd())); PostMessage(WM_TIMER,m_nCheckScrollInfoTimer); } #endif // _TABVIEW_H ///////////////////////////////////////////////////////////////////////////