diff options
author | Andrew <forkk@forkk.net> | 2013-12-09 12:04:05 -0600 |
---|---|---|
committer | Andrew <forkk@forkk.net> | 2013-12-09 12:04:05 -0600 |
commit | 7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c (patch) | |
tree | 52097b57dc7d18ec4e35ef3a8e554af2d91545e8 /mmc_updater/depends/win32cpp/docking.h | |
parent | 9410dd042ea62224fa3e0eb3b525abbdf0c316ee (diff) | |
parent | 220e07aef4a34dca9f31ae0c5bb994e5c594125d (diff) | |
download | MultiMC-7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c.tar MultiMC-7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c.tar.gz MultiMC-7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c.tar.lz MultiMC-7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c.tar.xz MultiMC-7f52bed9e3f559adcbcf3f3c1c7ac2251964db8c.zip |
Merge branch 'feature_updater' into develop
Diffstat (limited to 'mmc_updater/depends/win32cpp/docking.h')
-rw-r--r-- | mmc_updater/depends/win32cpp/docking.h | 4214 |
1 files changed, 4214 insertions, 0 deletions
diff --git a/mmc_updater/depends/win32cpp/docking.h b/mmc_updater/depends/win32cpp/docking.h new file mode 100644 index 00000000..9e7c4486 --- /dev/null +++ b/mmc_updater/depends/win32cpp/docking.h @@ -0,0 +1,4214 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// docking.h +// Declaration of the CDocker class + +#ifndef _WIN32XX_DOCKING_H_ +#define _WIN32XX_DOCKING_H_ + + +#include "wincore.h" +#include "gdi.h" +#include "toolbar.h" +#include "tab.h" +#include "frame.h" +#include "default_resource.h" + + +// Docking Styles +#define DS_DOCKED_LEFT 0x0001 // Dock the child left +#define DS_DOCKED_RIGHT 0x0002 // Dock the child right +#define DS_DOCKED_TOP 0x0004 // Dock the child top +#define DS_DOCKED_BOTTOM 0x0008 // Dock the child bottom +#define DS_NO_DOCKCHILD_LEFT 0x0010 // Prevent a child docking left +#define DS_NO_DOCKCHILD_RIGHT 0x0020 // Prevent a child docking right +#define DS_NO_DOCKCHILD_TOP 0x0040 // Prevent a child docking at the top +#define DS_NO_DOCKCHILD_BOTTOM 0x0080 // Prevent a child docking at the bottom +#define DS_NO_RESIZE 0x0100 // Prevent resizing +#define DS_NO_CAPTION 0x0200 // Prevent display of caption when docked +#define DS_NO_CLOSE 0x0400 // Prevent closing of a docker while docked +#define DS_NO_UNDOCK 0x0800 // Prevent undocking and dock closing +#define DS_CLIENTEDGE 0x1000 // Has a 3D border when docked +#define DS_FIXED_RESIZE 0x2000 // Perfomed a fixed resize instead of a proportional resize on dock children +#define DS_DOCKED_CONTAINER 0x4000 // Dock a container within a container +#define DS_DOCKED_LEFTMOST 0x10000 // Leftmost outer docking +#define DS_DOCKED_RIGHTMOST 0x20000 // Rightmost outer docking +#define DS_DOCKED_TOPMOST 0x40000 // Topmost outer docking +#define DS_DOCKED_BOTTOMMOST 0x80000 // Bottommost outer docking + +// Required for Dev-C++ +#ifndef TME_NONCLIENT + #define TME_NONCLIENT 0x00000010 +#endif +#ifndef TME_LEAVE + #define TME_LEAVE 0x000000002 +#endif +#ifndef WM_NCMOUSELEAVE + #define WM_NCMOUSELEAVE 0x000002A2 +#endif + +namespace Win32xx +{ + // Class declarations + class CDockContainer; + class CDocker; + + typedef Shared_Ptr<CDocker> DockPtr; + + struct ContainerInfo + { + TCHAR szTitle[MAX_MENU_STRING]; + int iImage; + CDockContainer* pContainer; + }; + + /////////////////////////////////////// + // Declaration of the CDockContainer class + // A CDockContainer is a CTab window. A CTab has a view window, and optionally a toolbar control. + // A top level CDockContainer can contain other CDockContainers. The view for each container + // (including the top level container) along with possibly its toolbar, is displayed + // within the container parent's view page. + class CDockContainer : public CTab + { + public: + + // Nested class. This is the Wnd for the window displayed over the client area + // of the tab control. The toolbar and view window are child windows of the + // viewpage window. Only the ViewPage of the parent CDockContainer is displayed. It's + // contents are updated with the view window of the relevant container whenever + // a different tab is selected. + class CViewPage : public CWnd + { + + public: + CViewPage() : m_pView(NULL), m_pTab(NULL) {} + virtual ~CViewPage() {} + virtual CToolBar& GetToolBar() const {return (CToolBar&)m_ToolBar;} + virtual CWnd* GetView() const {return m_pView;} + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual void RecalcLayout(); + virtual void SetView(CWnd& wndView); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + CWnd* GetTabCtrl() const { return m_pTab;} + + private: + CToolBar m_ToolBar; + tString m_tsTooltip; + CWnd* m_pView; + CWnd* m_pTab; + }; + + public: + CDockContainer(); + virtual ~CDockContainer(); + virtual void AddContainer(CDockContainer* pContainer); + virtual void AddToolBarButton(UINT nID, BOOL bEnabled = TRUE); + virtual CDockContainer* GetContainerFromIndex(UINT nPage); + virtual CDockContainer* GetContainerFromView(CWnd* pView) const; + virtual int GetContainerIndex(CDockContainer* pContainer); + virtual SIZE GetMaxTabTextSize(); + virtual CViewPage& GetViewPage() const { return (CViewPage&)m_ViewPage; } + virtual void RecalcLayout(); + virtual void RemoveContainer(CDockContainer* pWnd); + virtual void SelectPage(int nPage); + virtual void SetTabSize(); + virtual void SetupToolBar(); + + // Attributes + CDockContainer* GetActiveContainer() const {return GetContainerFromView(GetActiveView());} + CWnd* GetActiveView() const; + std::vector<ContainerInfo>& GetAllContainers() const {return m_pContainerParent->m_vContainerInfo;} + CDockContainer* GetContainerParent() const { return m_pContainerParent; } + CString& GetDockCaption() const { return (CString&)m_csCaption; } + HICON GetTabIcon() const { return m_hTabIcon; } + LPCTSTR GetTabText() const { return m_tsTabText.c_str(); } + virtual CToolBar& GetToolBar() const { return GetViewPage().GetToolBar(); } + CWnd* GetView() const { return GetViewPage().GetView(); } + void SetActiveContainer(CDockContainer* pContainer); + void SetDockCaption(LPCTSTR szCaption) { m_csCaption = szCaption; } + void SetTabIcon(HICON hTabIcon) { m_hTabIcon = hTabIcon; } + void SetTabIcon(UINT nID_Icon); + void SetTabIcon(int i, HICON hIcon) { CTab::SetTabIcon(i, hIcon); } + void SetTabText(LPCTSTR szText) { m_tsTabText = szText; } + void SetTabText(UINT nTab, LPCTSTR szText); + void SetView(CWnd& Wnd); + + protected: + virtual void OnCreate(); + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT &cs); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + std::vector<ContainerInfo> m_vContainerInfo; + tString m_tsTabText; + CString m_csCaption; + CViewPage m_ViewPage; + int m_iCurrentPage; + CDockContainer* m_pContainerParent; + HICON m_hTabIcon; + int m_nTabPressed; + + }; + + typedef struct DRAGPOS + { + NMHDR hdr; + POINT ptPos; + UINT DockZone; + } *LPDRAGPOS; + + + ///////////////////////////////////////// + // Declaration of the CDocker class + // A CDocker window allows other CDocker windows to be "docked" inside it. + // A CDocker can dock on the top, left, right or bottom side of a parent CDocker. + // There is no theoretical limit to the number of CDockers within CDockers. + class CDocker : public CWnd + { + public: + // A nested class for the splitter bar that seperates the docked panes. + class CDockBar : public CWnd + { + public: + CDockBar(); + virtual ~CDockBar(); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS& wc); + virtual void SendNotify(UINT nMessageID); + virtual void SetColor(COLORREF color); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + CDocker* GetDock() {return m_pDock;} + int GetWidth() {return m_DockBarWidth;} + void SetDock(CDocker* pDock) {m_pDock = pDock;} + void SetWidth(int nWidth) {m_DockBarWidth = nWidth;} + + private: + CDockBar(const CDockBar&); // Disable copy construction + CDockBar& operator = (const CDockBar&); // Disable assignment operator + + CDocker* m_pDock; + DRAGPOS m_DragPos; + CBrush m_brBackground; + int m_DockBarWidth; + }; + + // A nested class for the window inside a CDocker which includes all of this docked client. + // It's the remaining part of the CDocker that doesn't belong to the CDocker's children. + // The Docker's view window is a child window of CDockClient. + class CDockClient : public CWnd + { + public: + CDockClient(); + virtual ~CDockClient() {} + virtual void Draw3DBorder(RECT& Rect); + virtual void DrawCaption(WPARAM wParam); + virtual void DrawCloseButton(CDC& DrawDC, BOOL bFocus); + virtual CRect GetCloseRect(); + virtual void SendNotify(UINT nMessageID); + + CString& GetCaption() const { return (CString&)m_csCaption; } + CWnd* GetView() const { return m_pView; } + void SetDock(CDocker* pDock) { m_pDock = pDock;} + void SetCaption(LPCTSTR szCaption) { m_csCaption = szCaption; } + void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2); + void SetClosePressed() { m_IsClosePressed = TRUE; } + void SetView(CWnd& Wnd) { m_pView = &Wnd; } + + protected: + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseActivate(WPARAM wParam, LPARAM lParam); + virtual void OnMouseMove(WPARAM wParam, LPARAM lParam); + virtual void OnNCCalcSize(WPARAM& wParam, LPARAM& lParam); + virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnNCMouseLeave(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCMouseMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCPaint(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual void PreRegisterClass(WNDCLASS& wc); + virtual void PreCreate(CREATESTRUCT& cs); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CDockClient(const CDockClient&); // Disable copy construction + CDockClient& operator = (const CDockClient&); // Disable assignment operator + + CString m_csCaption; + CPoint m_Oldpt; + CDocker* m_pDock; + CWnd* m_pView; + BOOL m_IsClosePressed; + BOOL m_bOldFocus; + BOOL m_bCaptionPressed; + BOOL m_IsTracking; + COLORREF m_Foregnd1; + COLORREF m_Backgnd1; + COLORREF m_Foregnd2; + COLORREF m_Backgnd2; + }; + + // This nested class is used to indicate where a window could dock by + // displaying a blue tinted window. + class CDockHint : public CWnd + { + public: + CDockHint(); + virtual ~CDockHint(); + virtual RECT CalcHintRectContainer(CDocker* pDockTarget); + virtual RECT CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide); + virtual RECT CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide); + virtual void DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void ShowHintWindow(CDocker* pDockTarget, CRect rcHint); + + private: + CDockHint(const CDockHint&); // Disable copy construction + CDockHint& operator = (const CDockHint&); // Disable assignment operator + + CBitmap m_bmBlueTint; + UINT m_uDockSideOld; + }; + + class CTarget : public CWnd + { + public: + CTarget() {} + virtual ~CTarget(); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + + protected: + CBitmap m_bmImage; + + private: + CTarget(const CTarget&); // Disable copy construction + CTarget& operator = (const CTarget&); // Disable assignment operator + }; + + class CTargetCentre : public CTarget + { + public: + CTargetCentre(); + virtual ~CTargetCentre(); + virtual void OnDraw(CDC* pDC); + virtual void OnCreate(); + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + BOOL IsOverContainer() { return m_bIsOverContainer; } + + private: + CTargetCentre(const CTargetCentre&); // Disable copy construction + CTargetCentre& operator = (const CTargetCentre&); // Disable assignment operator + + BOOL m_bIsOverContainer; + CDocker* m_pOldDockTarget; + }; + + class CTargetLeft : public CTarget + { + public: + CTargetLeft() {m_bmImage.LoadImage(IDW_SDLEFT,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + + private: + CTargetLeft(const CTargetLeft&); // Disable copy construction + CTargetLeft& operator = (const CTargetLeft&); // Disable assignment operator + }; + + class CTargetTop : public CTarget + { + public: + CTargetTop() {m_bmImage.LoadImage(IDW_SDTOP,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + private: + CTargetTop(const CTargetTop&); // Disable copy construction + CTargetTop& operator = (const CTargetTop&); // Disable assignment operator + }; + + class CTargetRight : public CTarget + { + public: + CTargetRight() {m_bmImage.LoadImage(IDW_SDRIGHT,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + + private: + CTargetRight(const CTargetRight&); // Disable copy construction + CTargetRight& operator = (const CTargetRight&); // Disable assignment operator + }; + + class CTargetBottom : public CTarget + { + public: + CTargetBottom() {m_bmImage.LoadImage(IDW_SDBOTTOM,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + }; + + friend class CTargetCentre; + friend class CTargetLeft; + friend class CTargetTop; + friend class CTargetRight; + friend class CTargetBottom; + friend class CDockClient; + friend class CDockContainer; + + public: + // Operations + CDocker(); + virtual ~CDocker(); + virtual CDocker* AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID = 0); + virtual CDocker* AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID = 0); + virtual void Close(); + virtual void CloseAllDockers(); + virtual void Dock(CDocker* pDocker, UINT uDockSide); + virtual void DockInContainer(CDocker* pDock, DWORD dwDockStyle); + virtual CDockContainer* GetContainer() const; + virtual CDocker* GetActiveDocker() const; + virtual CDocker* GetDockAncestor() const; + virtual CDocker* GetDockFromID(int n_DockID) const; + virtual CDocker* GetDockFromPoint(POINT pt) const; + virtual CDocker* GetDockFromView(CWnd* pView) const; + virtual CDocker* GetTopmostDocker() const; + virtual int GetDockSize() const; + virtual CTabbedMDI* GetTabbedMDI() const; + virtual int GetTextHeight(); + virtual void Hide(); + virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName); + virtual void RecalcDockLayout(); + virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName); + virtual void Undock(CPoint pt, BOOL bShowUndocked = TRUE); + virtual void UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked); + virtual BOOL VerifyDockers(); + + // Attributes + virtual CDockBar& GetDockBar() const {return (CDockBar&)m_DockBar;} + virtual CDockClient& GetDockClient() const {return (CDockClient&)m_DockClient;} + virtual CDockHint& GetDockHint() const {return m_pDockAncestor->m_DockHint;} + + + std::vector <DockPtr> & GetAllDockers() const {return GetDockAncestor()->m_vAllDockers;} + int GetBarWidth() const {return GetDockBar().GetWidth();} + CString& GetCaption() const {return GetDockClient().GetCaption();} + std::vector <CDocker*> & GetDockChildren() const {return (std::vector <CDocker*> &)m_vDockChildren;} + int GetDockID() const {return m_nDockID;} + CDocker* GetDockParent() const {return m_pDockParent;} + DWORD GetDockStyle() const {return m_DockStyle;} + CWnd* GetView() const {return GetDockClient().GetView();} + BOOL IsChildOfDocker(CWnd* pWnd) const; + BOOL IsDocked() const; + BOOL IsDragAutoResize(); + BOOL IsRelated(CWnd* pWnd) const; + BOOL IsUndocked() const; + void SetBarColor(COLORREF color) {GetDockBar().SetColor(color);} + void SetBarWidth(int nWidth) {GetDockBar().SetWidth(nWidth);} + void SetCaption(LPCTSTR szCaption); + void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2); + void SetCaptionHeight(int nHeight); + void SetDockStyle(DWORD dwDockStyle); + void SetDockSize(int DockSize); + void SetDragAutoResize(BOOL bAutoResize); + void SetView(CWnd& wndView); + + protected: + virtual CDocker* NewDockerFromID(int idDock); + virtual void OnActivate(WPARAM wParam, LPARAM lParam); + virtual void OnCaptionTimer(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual void OnDestroy(WPARAM wParam, LPARAM lParam); + virtual void OnDockDestroyed(WPARAM wParam, LPARAM lParam); + virtual void OnExitSizeMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void OnSetFocus(WPARAM wParam, LPARAM lParam); + virtual void OnSysColorChange(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnWindowPosChanging(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CDocker(const CDocker&); // Disable copy construction + CDocker& operator = (const CDocker&); // Disable assignment operator + void CheckAllTargets(LPDRAGPOS pDragPos); + void CloseAllTargets(); + void DockOuter(CDocker* pDocker, DWORD dwDockStyle); + void DrawAllCaptions(); + void DrawHashBar(HWND hBar, POINT Pos); + void ConvertToChild(HWND hWndParent); + void ConvertToPopup(RECT rc); + void MoveDockChildren(CDocker* pDockTarget); + void PromoteFirstChild(); + void RecalcDockChildLayout(CRect rc); + void ResizeDockers(LPDRAGPOS pdp); + CDocker* SeparateFromDock(); + void SendNotify(UINT nMessageID); + void SetUndockPosition(CPoint pt); + std::vector<CDocker*> SortDockers(); + + CDockBar m_DockBar; + CDockHint m_DockHint; + CDockClient m_DockClient; + CTargetCentre m_TargetCentre; + CTargetLeft m_TargetLeft; + CTargetTop m_TargetTop; + CTargetRight m_TargetRight; + CPoint m_OldPoint; + CTargetBottom m_TargetBottom; + CDocker* m_pDockParent; + CDocker* m_pDockAncestor; + CDocker* m_pDockActive; + + std::vector <CDocker*> m_vDockChildren; + std::vector <DockPtr> m_vAllDockers; // Only used in DockAncestor + + CRect m_rcBar; + CRect m_rcChild; + + BOOL m_BlockMove; + BOOL m_Undocking; + BOOL m_bIsClosing; + BOOL m_bIsDragging; + BOOL m_bDragAutoResize; + int m_DockStartSize; + int m_nDockID; + int m_nTimerCount; + int m_NCHeight; + DWORD m_dwDockZone; + double m_DockSizeRatio; + DWORD m_DockStyle; + HWND m_hOldFocus; + + }; // class CDocker + + struct DockInfo + { + DWORD DockStyle; + int DockSize; + int DockID; + int DockParentID; + RECT Rect; + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + ///////////////////////////////////////////////////////////// + // Definitions for the CDockBar class nested within CDocker + // + inline CDocker::CDockBar::CDockBar() : m_pDock(NULL), m_DockBarWidth(4) + { + m_brBackground.CreateSolidBrush(RGB(192,192,192)); + } + + inline CDocker::CDockBar::~CDockBar() + { + } + + inline void CDocker::CDockBar::OnDraw(CDC* pDC) + { + CRect rcClient = GetClientRect(); + pDC->SelectObject(&m_brBackground); + pDC->PatBlt(0, 0, rcClient.Width(), rcClient.Height(), PATCOPY); + } + + inline void CDocker::CDockBar::PreCreate(CREATESTRUCT &cs) + { + // Create a child window, initially hidden + cs.style = WS_CHILD; + } + + inline void CDocker::CDockBar::PreRegisterClass(WNDCLASS& wc) + { + wc.lpszClassName = _T("Win32++ Bar"); + wc.hbrBackground = m_brBackground; + } + + inline void CDocker::CDockBar::SendNotify(UINT nMessageID) + { + // Send a splitter bar notification to the parent + m_DragPos.hdr.code = nMessageID; + m_DragPos.hdr.hwndFrom = m_hWnd; + m_DragPos.ptPos = GetCursorPos(); + m_DragPos.ptPos.x += 1; + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&m_DragPos); + } + + inline void CDocker::CDockBar::SetColor(COLORREF color) + { + // Useful colors: + // GetSysColor(COLOR_BTNFACE) // Default Grey + // RGB(196, 215, 250) // Default Blue + + m_brBackground.CreateSolidBrush(color); + } + + inline LRESULT CDocker::CDockBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + { + switch (uMsg) + { + case WM_SETCURSOR: + { + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE)) + { + HCURSOR hCursor; + DWORD dwSide = GetDock()->GetDockStyle() & 0xF; + if ((dwSide == DS_DOCKED_LEFT) || (dwSide == DS_DOCKED_RIGHT)) + hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITH)); + else + hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITV)); + + if (hCursor) SetCursor(hCursor); + else TRACE(_T("**WARNING** Missing cursor resource for slider bar\n")); + + return TRUE; + } + else + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } + break; + + case WM_ERASEBKGND: + return 0; + + case WM_LBUTTONDOWN: + { + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE)) + { + SendNotify(UWM_BAR_START); + SetCapture(); + } + } + break; + + case WM_LBUTTONUP: + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this)) + { + SendNotify(UWM_BAR_END); + ReleaseCapture(); + } + break; + + case WM_MOUSEMOVE: + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this)) + { + SendNotify(UWM_BAR_MOVE); + } + break; + } + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CDockClient class nested within CDocker + // + inline CDocker::CDockClient::CDockClient() : m_pView(0), m_IsClosePressed(FALSE), + m_bOldFocus(FALSE), m_bCaptionPressed(FALSE), m_IsTracking(FALSE) + { + m_Foregnd1 = RGB(32,32,32); + m_Backgnd1 = RGB(190,207,227); + m_Foregnd2 = GetSysColor(COLOR_BTNTEXT); + m_Backgnd2 = GetSysColor(COLOR_BTNFACE); + } + + inline void CDocker::CDockClient::Draw3DBorder(RECT& Rect) + { + // Imitates the drawing of the WS_EX_CLIENTEDGE extended style + // This draws a 2 pixel border around the specified Rect + CWindowDC dc(this); + CRect rcw = Rect; + dc.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)); + dc.MoveTo(0, rcw.Height()); + dc.LineTo(0, 0); + dc.LineTo(rcw.Width(), 0); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DDKSHADOW)); + dc.MoveTo(1, rcw.Height()-2); + dc.LineTo(1, 1); + dc.LineTo(rcw.Width()-2, 1); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DHILIGHT)); + dc.MoveTo(rcw.Width()-1, 0); + dc.LineTo(rcw.Width()-1, rcw.Height()-1); + dc.LineTo(0, rcw.Height()-1); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DLIGHT)); + dc.MoveTo(rcw.Width()-2, 1); + dc.LineTo(rcw.Width()-2, rcw.Height()-2); + dc.LineTo(1, rcw.Height()-2); + } + + inline CRect CDocker::CDockClient::GetCloseRect() + { + // Calculate the close rect position in screen co-ordinates + CRect rcClose; + + int gap = 4; + CRect rc = GetWindowRect(); + int cx = GetSystemMetrics(SM_CXSMICON); + int cy = GetSystemMetrics(SM_CYSMICON); + + rcClose.top = 2 + rc.top + m_pDock->m_NCHeight/2 - cy/2; + rcClose.bottom = 2 + rc.top + m_pDock->m_NCHeight/2 + cy/2; + rcClose.right = rc.right - gap; + rcClose.left = rcClose.right - cx; + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + { + rcClose.left = rc.left + gap; + rcClose.right = rcClose.left + cx; + } +#endif + + + return rcClose; + } + + inline void CDocker::CDockClient::DrawCaption(WPARAM wParam) + { + if (IsWindow() && m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + BOOL bFocus = m_pDock->IsChildOfDocker(GetFocus()); + m_bOldFocus = FALSE; + + // Acquire the DC for our NonClient painting + CDC* pDC; + if ((wParam != 1) && (bFocus == m_bOldFocus)) + pDC = GetDCEx((HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN|DCX_PARENTCLIP); + else + pDC = GetWindowDC(); + + // Create and set up our memory DC + CRect rc = GetWindowRect(); + CMemDC dcMem(pDC); + int rcAdjust = (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)? 2 : 0; + int Width = MAX(rc.Width() -rcAdjust, 0); + int Height = m_pDock->m_NCHeight + rcAdjust; + dcMem.CreateCompatibleBitmap(pDC, Width, Height); + m_bOldFocus = bFocus; + + // Set the font for the title + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + dcMem.CreateFontIndirect(&info.lfStatusFont); + + // Set the Colours + if (bFocus) + { + dcMem.SetTextColor(m_Foregnd1); + dcMem.CreateSolidBrush(m_Backgnd1); + dcMem.SetBkColor(m_Backgnd1); + } + else + { + dcMem.SetTextColor(m_Foregnd2); + dcMem.CreateSolidBrush(m_Backgnd2); + dcMem.SetBkColor(m_Backgnd2); + } + + // Draw the rectangle + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 150, 140)); + dcMem.Rectangle(rcAdjust, rcAdjust, rc.Width() -rcAdjust, m_pDock->m_NCHeight +rcAdjust); + + // Display the caption + int cx = (m_pDock->GetDockStyle() & DS_NO_CLOSE)? 0 : GetSystemMetrics(SM_CXSMICON); + CRect rcText(4 +rcAdjust, rcAdjust, rc.Width() -4 - cx -rcAdjust, m_pDock->m_NCHeight +rcAdjust); + dcMem.DrawText(m_csCaption, m_csCaption.GetLength(), rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); + + // Draw the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + DrawCloseButton(dcMem, bFocus); + + // Draw the 3D border + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + Draw3DBorder(rc); + + // Copy the Memory DC to the window's DC + pDC->BitBlt(rcAdjust, rcAdjust, Width, Height, &dcMem, rcAdjust, rcAdjust, SRCCOPY); + + // Required for Win98/WinME + pDC->Destroy(); + } + } + + inline void CDocker::CDockClient::DrawCloseButton(CDC& DrawDC, BOOL bFocus) + { + // The close button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + // Determine the close button's drawing position relative to the window + CRect rcClose = GetCloseRect(); + UINT uState = GetCloseRect().PtInRect(GetCursorPos())? m_IsClosePressed && IsLeftButtonDown()? 2 : 1 : 0; + ScreenToClient(rcClose); + + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + { + rcClose.OffsetRect(2, m_pDock->m_NCHeight+2); + if (GetWindowRect().Height() < (m_pDock->m_NCHeight+4)) + rcClose.OffsetRect(-2, -2); + } + else + rcClose.OffsetRect(0, m_pDock->m_NCHeight-2); + + // Draw the outer highlight for the close button + if (!IsRectEmpty(&rcClose)) + { + switch (uState) + { + case 0: + { + // Normal button + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + break; + } + + case 1: + { + // Popped up button + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + + break; + case 2: + { + // Pressed button + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + break; + } + + // Manually Draw Close Button + if (bFocus) + DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd1); + else + DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd2); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4); + DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +4); + DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -4, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3); + } + } + } + + inline void CDocker::CDockClient::OnNCCalcSize(WPARAM& wParam, LPARAM& lParam) + { + // Sets the non-client area (and hence sets the client area) + // This function modifies lParam + + UNREFERENCED_PARAMETER(wParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + LPRECT rc = (LPRECT)lParam; + rc->top += m_pDock->m_NCHeight; + } + } + } + + inline LRESULT CDocker::CDockClient::OnNCHitTest(WPARAM wParam, LPARAM lParam) + { + // Identify which part of the non-client area the cursor is over + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + // Indicate if the point is in the close button (except for Win95) + if ((GetWinVersion() > 1400) && (GetCloseRect().PtInRect(pt))) + return HTCLOSE; + + ScreenToClient(pt); + + // Indicate if the point is in the caption + if (pt.y < 0) + return HTCAPTION; + } + } + return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam); + } + + inline LRESULT CDocker::CDockClient::OnNCLButtonDown(WPARAM wParam, LPARAM lParam) + { + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if ((HTCLOSE == wParam) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + m_IsClosePressed = TRUE; + SetCapture(); + } + + m_bCaptionPressed = TRUE; + m_Oldpt.x = GET_X_LPARAM(lParam); + m_Oldpt.y = GET_Y_LPARAM(lParam); + if (m_pDock->IsDocked()) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + ScreenToClient(pt); + m_pView->SetFocus(); + + // Update the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + + return 0L; + } + } + return CWnd::WndProcDefault(WM_NCLBUTTONDOWN, wParam, lParam); + } + + inline void CDocker::CDockClient::OnLButtonUp(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE))) + { + m_bCaptionPressed = FALSE; + if (m_IsClosePressed && GetCloseRect().PtInRect(GetCursorPos())) + { + // Destroy the docker + if (dynamic_cast<CDockContainer*>(m_pDock->GetView())) + { + CDockContainer* pContainer = ((CDockContainer*)m_pDock->GetView())->GetActiveContainer(); + CDocker* pDock = m_pDock->GetDockFromView(pContainer); + pDock->GetDockClient().SetClosePressed(); + m_pDock->UndockContainer(pContainer, GetCursorPos(), FALSE); + pDock->Destroy(); + } + else + { + m_pDock->Hide(); + m_pDock->Destroy(); + } + } + } + } + + inline void CDocker::CDockClient::OnLButtonDown(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_IsClosePressed = FALSE; + ReleaseCapture(); + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + + inline void CDocker::CDockClient::OnMouseActivate(WPARAM wParam, LPARAM lParam) + // Focus changed, so redraw the captions + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + m_pDock->GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + } + } + + inline void CDocker::CDockClient::OnMouseMove(WPARAM wParam, LPARAM lParam) + { + OnNCMouseMove(wParam, lParam); + } + + inline void CDocker::CDockClient::OnNCMouseLeave(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_IsTracking = FALSE; + CWindowDC dc(this); + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE)) && m_pDock->IsDocked()) + DrawCloseButton(dc, m_bOldFocus); + + m_IsTracking = FALSE; + } + + inline LRESULT CDocker::CDockClient::OnNCMouseMove(WPARAM wParam, LPARAM lParam) + { + if (!m_IsTracking) + { + TRACKMOUSEEVENT TrackMouseEventStruct = {0}; + TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct); + TrackMouseEventStruct.dwFlags = TME_LEAVE|TME_NONCLIENT; + TrackMouseEventStruct.hwndTrack = m_hWnd; + _TrackMouseEvent(&TrackMouseEventStruct); + m_IsTracking = TRUE; + } + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + // Discard phantom mouse move messages + if ( (m_Oldpt.x == GET_X_LPARAM(lParam) ) && (m_Oldpt.y == GET_Y_LPARAM(lParam))) + return 0L; + + if (IsLeftButtonDown() && (wParam == HTCAPTION) && (m_bCaptionPressed)) + { + CDocker* pDock = (CDocker*)GetParent(); + if (pDock) + pDock->Undock(GetCursorPos()); + } + + // Update the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + } + + m_bCaptionPressed = FALSE; + } + return CWnd::WndProcDefault(WM_MOUSEMOVE, wParam, lParam); + } + + inline LRESULT CDocker::CDockClient::OnNCPaint(WPARAM wParam, LPARAM lParam) + { + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + DefWindowProc(WM_NCPAINT, wParam, lParam); + DrawCaption(wParam); + return 0; + } + } + return CWnd::WndProcDefault(WM_NCPAINT, wParam, lParam); + } + + inline void CDocker::CDockClient::OnWindowPosChanged(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Reposition the View window to cover the DockClient's client area + CRect rc = GetClientRect(); + m_pView->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + + inline void CDocker::CDockClient::PreRegisterClass(WNDCLASS& wc) + { + wc.lpszClassName = _T("Win32++ DockClient"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDocker::CDockClient::PreCreate(CREATESTRUCT& cs) + { + DWORD dwStyle = m_pDock->GetDockStyle(); + if (dwStyle & DS_CLIENTEDGE) + cs.dwExStyle = WS_EX_CLIENTEDGE; + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (m_pDock->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + cs.dwExStyle |= WS_EX_LAYOUTRTL; +#endif + + } + + inline void CDocker::CDockClient::SendNotify(UINT nMessageID) + { + // Fill the DragPos structure with data + DRAGPOS DragPos; + DragPos.hdr.code = nMessageID; + DragPos.hdr.hwndFrom = m_hWnd; + DragPos.ptPos = GetCursorPos(); + + // Send a DragPos notification to the docker + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos); + } + + inline void CDocker::CDockClient::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF Foregnd2, COLORREF Backgnd2) + { + // Set the colors used when drawing the caption + // m_Foregnd1 Foreground colour (focused). m_Backgnd1 Background colour (focused) + // m_Foregnd2 Foreground colour (not focused). m_Backgnd2 Foreground colour (not focused) + m_Foregnd1 = Foregnd1; + m_Backgnd1 = Backgnd1; + m_Foregnd2 = Foregnd2; + m_Backgnd2 = Backgnd2; + } + + inline LRESULT CDocker::CDockClient::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_LBUTTONUP: + { + ReleaseCapture(); + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + OnLButtonUp(wParam, lParam); + } + } + break; + + case WM_MOUSEACTIVATE: + OnMouseActivate(wParam, lParam); + break; + + case WM_MOUSEMOVE: + OnMouseMove(wParam, lParam); + break; + + case WM_NCCALCSIZE: + OnNCCalcSize(wParam, lParam); + break; + + case WM_NCHITTEST: + return OnNCHitTest(wParam, lParam); + + case WM_NCLBUTTONDOWN: + return OnNCLButtonDown(wParam, lParam); + + case WM_NCMOUSEMOVE: + return OnNCMouseMove(wParam, lParam); + + case WM_NCPAINT: + return OnNCPaint(wParam, lParam); + + case WM_NCMOUSELEAVE: + OnNCMouseLeave(wParam, lParam); + break; + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + ////////////////////////////////////////////////////////////// + // Definitions for the CDockHint class nested within CDocker + // + inline CDocker::CDockHint::CDockHint() : m_uDockSideOld(0) + { + } + + inline CDocker::CDockHint::~CDockHint() + { + } + + inline RECT CDocker::CDockHint::CalcHintRectContainer(CDocker* pDockTarget) + { + // Calculate the hint window's position for container docking + CRect rcHint = pDockTarget->GetDockClient().GetWindowRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + pDockTarget->ScreenToClient(rcHint); + + return rcHint; + } + + inline RECT CDocker::CDockHint::CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide) + { + // Calculate the hint window's position for inner docking + CRect rcHint = pDockTarget->GetDockClient().GetWindowRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + pDockTarget->ScreenToClient(rcHint); + + int Width; + CRect rcDockDrag = pDockDrag->GetWindowRect(); + CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect(); + if ((uDockSide == DS_DOCKED_LEFT) || (uDockSide == DS_DOCKED_RIGHT)) + { + Width = rcDockDrag.Width(); + if (Width >= (rcDockTarget.Width() - pDockDrag->GetBarWidth())) + Width = MAX(rcDockTarget.Width()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth()); + } + else + { + Width = rcDockDrag.Height(); + if (Width >= (rcDockTarget.Height() - pDockDrag->GetBarWidth())) + Width = MAX(rcDockTarget.Height()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth()); + } + switch (uDockSide) + { + case DS_DOCKED_LEFT: + rcHint.right = rcHint.left + Width; + break; + case DS_DOCKED_RIGHT: + rcHint.left = rcHint.right - Width; + break; + case DS_DOCKED_TOP: + rcHint.bottom = rcHint.top + Width; + break; + case DS_DOCKED_BOTTOM: + rcHint.top = rcHint.bottom - Width; + break; + } + + return rcHint; + } + + inline RECT CDocker::CDockHint::CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide) + { + // Calculate the hint window's position for outer docking + CDocker* pDockTarget = pDockDrag->GetDockAncestor(); + CRect rcHint = pDockTarget->GetClientRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + + int Width; + CRect rcDockDrag = pDockDrag->GetWindowRect(); + CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect(); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if ((uDockSide == DS_DOCKED_LEFTMOST) || (uDockSide == DS_DOCKED_RIGHTMOST)) + { + Width = rcDockDrag.Width(); + int BarWidth = pDockDrag->GetBarWidth(); + if (Width >= pDockTarget->GetDockClient().GetClientRect().Width() - pDockDrag->GetBarWidth()) + Width = MAX(pDockTarget->GetDockClient().GetClientRect().Width()/2 - BarWidth, BarWidth); + } + else + { + Width = rcDockDrag.Height(); + int BarWidth = pDockDrag->GetBarWidth(); + if (Width >= pDockTarget->GetDockClient().GetClientRect().Height() - pDockDrag->GetBarWidth()) + Width = MAX(pDockTarget->GetDockClient().GetClientRect().Height()/2 - BarWidth, BarWidth); + } + switch (uDockSide) + { + case DS_DOCKED_LEFTMOST: + rcHint.right = rcHint.left + Width; + break; + case DS_DOCKED_RIGHTMOST: + rcHint.left = rcHint.right - Width; + break; + case DS_DOCKED_TOPMOST: + rcHint.bottom = rcHint.top + Width; + break; + case DS_DOCKED_BOTTOMMOST: + rcHint.top = rcHint.bottom - Width; + break; + } + + return rcHint; + } + + inline void CDocker::CDockHint::DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide) + { + // Ensure a new hint window is created if dock side changes + if (uDockSide != m_uDockSideOld) + { + Destroy(); + pDockTarget->RedrawWindow( NULL, NULL, RDW_NOERASE | RDW_UPDATENOW ); + pDockDrag->RedrawWindow(); + } + m_uDockSideOld = uDockSide; + + if (!IsWindow()) + { + CRect rcHint; + + if (uDockSide & 0xF) + rcHint = CalcHintRectInner(pDockTarget, pDockDrag, uDockSide); + else if (uDockSide & 0xF0000) + rcHint = CalcHintRectOuter(pDockDrag, uDockSide); + else if (uDockSide & DS_DOCKED_CONTAINER) + rcHint = CalcHintRectContainer(pDockTarget); + else + return; + + ShowHintWindow(pDockTarget, rcHint); + } + } + + inline void CDocker::CDockHint::OnDraw(CDC* pDC) + { + // Display the blue tinted bitmap + CRect rc = GetClientRect(); + CMemDC MemDC(pDC); + MemDC.SelectObject(&m_bmBlueTint); + pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &MemDC, 0, 0, SRCCOPY); + } + + inline void CDocker::CDockHint::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_POPUP; + + // WS_EX_TOOLWINDOW prevents the window being displayed on the taskbar + cs.dwExStyle = WS_EX_TOOLWINDOW; + + cs.lpszClass = _T("Win32++ DockHint"); + } + + inline void CDocker::CDockHint::ShowHintWindow(CDocker* pDockTarget, CRect rcHint) + { + // Save the Dock window's blue tinted bitmap + CClientDC dcDesktop(NULL); + CMemDC dcMem(&dcDesktop); + CRect rcBitmap = rcHint; + CRect rcTarget = rcHint; + pDockTarget->ClientToScreen(rcTarget); + + m_bmBlueTint.CreateCompatibleBitmap(&dcDesktop, rcBitmap.Width(), rcBitmap.Height()); + CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBlueTint); + dcMem.BitBlt(0, 0, rcBitmap.Width(), rcBitmap.Height(), &dcDesktop, rcTarget.left, rcTarget.top, SRCCOPY); + dcMem.SelectObject(pOldBitmap); + TintBitmap(&m_bmBlueTint, -64, -24, +128); + + // Create the Hint window + if (!IsWindow()) + { + Create(pDockTarget); + } + + pDockTarget->ClientToScreen(rcHint); + SetWindowPos(NULL, rcHint, SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOACTIVATE); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetCentre class nested within CDocker + // + inline CDocker::CTargetCentre::CTargetCentre() : m_bIsOverContainer(FALSE), m_pOldDockTarget(0) + { + } + + inline CDocker::CTargetCentre::~CTargetCentre() + { + } + + inline void CDocker::CTargetCentre::OnDraw(CDC* pDC) + { + CBitmap bmCentre(IDW_SDCENTER); + CBitmap bmLeft(IDW_SDLEFT); + CBitmap bmRight(IDW_SDRIGHT); + CBitmap bmTop(IDW_SDTOP); + CBitmap bmBottom(IDW_SDBOTTOM); + + if (bmCentre.GetHandle()) pDC->DrawBitmap(0, 0, 88, 88, bmCentre, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Centre\n")); + + if (bmLeft.GetHandle()) pDC->DrawBitmap(0, 29, 31, 29, bmLeft, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Left\n")); + + if (bmTop.GetHandle()) pDC->DrawBitmap(29, 0, 29, 31, bmTop, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Top\n")); + + if (bmRight.GetHandle()) pDC->DrawBitmap(55, 29, 31, 29, bmRight, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Right\n")); + + if (bmBottom.GetHandle()) pDC->DrawBitmap(29, 55, 29, 31, bmBottom, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Bottom\n")); + + if (IsOverContainer()) + { + CBitmap bmMiddle(IDW_SDMIDDLE); + pDC->DrawBitmap(31, 31, 25, 26, bmMiddle, RGB(255,0,255)); + } + } + + inline void CDocker::CTargetCentre::OnCreate() + { + // Use a region to create an irregularly shapped window + POINT ptArray[16] = { {0,29}, {22, 29}, {29, 22}, {29, 0}, + {58, 0}, {58, 22}, {64, 29}, {87, 29}, + {87, 58}, {64, 58}, {58, 64}, {58, 87}, + {29, 87}, {29, 64}, {23, 58}, {0, 58} }; + + CRgn rgnPoly; + rgnPoly.CreatePolygonRgn(ptArray, 16, WINDING); + SetWindowRgn(&rgnPoly, FALSE); + } + + inline BOOL CDocker::CTargetCentre::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pDragPos->ptPos); + if (NULL == pDockTarget) return FALSE; + + if (!IsWindow()) Create(); + m_bIsOverContainer = (dynamic_cast<CDockContainer*>(pDockTarget->GetView()) != NULL); + + // Redraw the target if the dock target changes + if (m_pOldDockTarget != pDockTarget) Invalidate(); + m_pOldDockTarget = pDockTarget; + + int cxImage = 88; + int cyImage = 88; + + CRect rcTarget = pDockTarget->GetDockClient().GetWindowRect(); + int xMid = rcTarget.left + (rcTarget.Width() - cxImage)/2; + int yMid = rcTarget.top + (rcTarget.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + + // Create the docking zone rectangles + CPoint pt = pDragPos->ptPos; + ScreenToClient(pt); + CRect rcLeft(0, 29, 31, 58); + CRect rcTop(29, 0, 58, 31); + CRect rcRight(55, 29, 87, 58); + CRect rcBottom(29, 55, 58, 87); + CRect rcMiddle(31, 31, 56, 57); + + // Test if our cursor is in one of the docking zones + if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFT); + pDockDrag->m_dwDockZone = DS_DOCKED_LEFT; + return TRUE; + } + else if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOP); + pDockDrag->m_dwDockZone = DS_DOCKED_TOP; + return TRUE; + } + else if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHT); + pDockDrag->m_dwDockZone = DS_DOCKED_RIGHT; + return TRUE; + } + else if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOM); + pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOM; + return TRUE; + } + else if ((rcMiddle.PtInRect(pt)) && (IsOverContainer())) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_CONTAINER); + pDockDrag->m_dwDockZone = DS_DOCKED_CONTAINER; + return TRUE; + } + else + return FALSE; + } + + //////////////////////////////////////////////////////////////// + // Definitions for the CTarget class nested within CDocker + // CTarget is the base class for a number of CTargetXXX classes + inline CDocker::CTarget::~CTarget() + { + } + + inline void CDocker::CTarget::OnDraw(CDC* pDC) + { + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (m_bmImage) + pDC->DrawBitmap(0, 0, cxImage, cyImage, m_bmImage, RGB(255,0,255)); + else + TRACE(_T("Missing docking resource\n")); + } + + inline void CDocker::CTarget::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_POPUP; + cs.dwExStyle = WS_EX_TOPMOST|WS_EX_TOOLWINDOW; + cs.lpszClass = _T("Win32++ DockTargeting"); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetLeft class nested within CDocker + // + inline BOOL CDocker::CTargetLeft::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int yMid = rc.top + (rc.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, rc.left + 10, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcLeft(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFTMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_LEFTMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetTop class nested within CDocker + // + inline BOOL CDocker::CTargetTop::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int xMid = rc.left + (rc.Width() - cxImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, rc.top + 10, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcTop(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOPMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_TOPMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetRight class nested within CDocker + // + inline BOOL CDocker::CTargetRight::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int yMid = rc.top + (rc.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, rc.right - 10 - cxImage, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcRight(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHTMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_RIGHTMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetBottom class nested within CDocker + // + inline BOOL CDocker::CTargetBottom::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int xMid = rc.left + (rc.Width() - cxImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, rc.bottom - 10 - cyImage, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + CRect rcBottom(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOMMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOMMOST; + return TRUE; + } + + return FALSE; + } + + + ///////////////////////////////////////// + // Definitions for the CDocker class + // + inline CDocker::CDocker() : m_pDockParent(NULL), m_pDockActive(NULL), m_BlockMove(FALSE), m_Undocking(FALSE), + m_bIsClosing(FALSE), m_bIsDragging(FALSE), m_bDragAutoResize(TRUE), m_DockStartSize(0), m_nDockID(0), + m_nTimerCount(0), m_NCHeight(0), m_dwDockZone(0), m_DockSizeRatio(1.0), m_DockStyle(0), m_hOldFocus(0) + { + // Assume this docker is the DockAncestor for now. + m_pDockAncestor = this; + } + + inline CDocker::~CDocker() + { + GetDockBar().Destroy(); + + std::vector <DockPtr>::iterator iter; + if (GetDockAncestor() == this) + { + // Destroy all dock descendants of this dock ancestor + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + (*iter)->Destroy(); + } + } + } + + inline CDocker* CDocker::AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID /* = 0*/) + // This function creates the docker, and adds it to the docker heirachy as docked + { + // Create the docker window as a child of the frame window. + // This pernamently sets the frame window as the docker window's owner, + // even when its parent is subsequently changed. + + assert(pDocker); + + // Store the Docker's pointer in the DockAncestor's vector for later deletion + GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker)); + + pDocker->SetDockStyle(dwDockStyle); + pDocker->m_nDockID = nDockID; + pDocker->m_pDockAncestor = GetDockAncestor(); + pDocker->m_pDockParent = this; + pDocker->SetDockSize(DockSize); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pDocker->Create(pFrame); + pDocker->SetParent(this); + + // Dock the docker window + if (dwDockStyle & DS_DOCKED_CONTAINER) + DockInContainer(pDocker, dwDockStyle); + else + Dock(pDocker, dwDockStyle); + + // Issue TRACE warnings for any missing resources + HMODULE hMod= GetApp()->GetResourceHandle(); + + if (!(dwDockStyle & DS_NO_RESIZE)) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITH), RT_GROUP_CURSOR)) + TRACE(_T("**WARNING** Horizontal cursor resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITV), RT_GROUP_CURSOR)) + TRACE(_T("**WARNING** Vertical cursor resource missing\n")); + } + + if (!(dwDockStyle & DS_NO_UNDOCK)) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDCENTER), RT_BITMAP)) + TRACE(_T("**WARNING** Docking center bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDLEFT), RT_BITMAP)) + TRACE(_T("**WARNING** Docking left bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDRIGHT), RT_BITMAP)) + TRACE(_T("**WARNING** Docking right bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDTOP), RT_BITMAP)) + TRACE(_T("**WARNING** Docking top bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDBOTTOM), RT_BITMAP)) + TRACE(_T("**WARNING** Docking center bottom resource missing\n")); + } + + if (dwDockStyle & DS_DOCKED_CONTAINER) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDMIDDLE), RT_BITMAP)) + TRACE(_T("**WARNING** Docking container bitmap resource missing\n")); + } + + return pDocker; + } + + inline CDocker* CDocker::AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID /* = 0*/) + // This function creates the docker, and adds it to the docker heirachy as undocked + { + assert(pDocker); + + // Store the Docker's pointer in the DockAncestor's vector for later deletion + GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker)); + + pDocker->SetDockSize(DockSize); + pDocker->SetDockStyle(dwDockStyle & 0XFFFFFF0); + pDocker->m_nDockID = nDockID; + pDocker->m_pDockAncestor = GetDockAncestor(); + + // Initially create the as a child window of the frame + // This makes the frame window the owner of our docker + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pDocker->Create(pFrame); + pDocker->SetParent(this); + + // Change the Docker to a POPUP window + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE; + pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDocker->SetRedraw(FALSE); + pDocker->SetParent(0); + pDocker->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED); + pDocker->SetRedraw(TRUE); + pDocker->RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ALLCHILDREN); + pDocker->SetWindowText(pDocker->GetCaption().c_str()); + + return pDocker; + } + + inline void CDocker::CheckAllTargets(LPDRAGPOS pDragPos) + // Calls CheckTarget for each possible target zone + { + if (!GetDockAncestor()->m_TargetCentre.CheckTarget(pDragPos)) + { + if (!GetDockAncestor()->m_TargetLeft.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetTop.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetRight.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetBottom.CheckTarget(pDragPos)) + { + // Not in a docking zone, so clean up + NMHDR nmhdr = pDragPos->hdr; + CDocker* pDockDrag = (CDocker*)FromHandle(nmhdr.hwndFrom); + if (pDockDrag) + { + if (pDockDrag->m_BlockMove) + pDockDrag->RedrawWindow(0, 0, RDW_FRAME|RDW_INVALIDATE); + + GetDockHint().Destroy(); + pDockDrag->m_dwDockZone = 0; + pDockDrag->m_BlockMove = FALSE; + } + } + } + } + } + } + } + + inline BOOL CDocker::VerifyDockers() + // A diagnostic routine which verifies the integrity of the docking layout + { + BOOL bResult = TRUE; + + // Check dock ancestor + std::vector<DockPtr>::iterator iter; + + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + if (GetDockAncestor() != (*iter)->m_pDockAncestor) + { + TRACE(_T("Invalid Dock Ancestor\n")); + bResult = FALSE; + } + } + + // Check presence of dock parent + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + if ((*iter)->IsUndocked() && (*iter)->m_pDockParent != 0) + { + TRACE(_T("Error: Undocked dockers should not have a dock parent\n")); + bResult = FALSE; + } + + if ((*iter)->IsDocked() && (*iter)->m_pDockParent == 0) + { + TRACE(_T("Error: Docked dockers should have a dock parent\n")); + bResult = FALSE; + } + } + + // Check dock parent/child relationship + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + std::vector<CDocker*>::iterator iterChild; + for (iterChild = (*iter)->GetDockChildren().begin(); iterChild != (*iter)->GetDockChildren().end(); ++iterChild) + { + if ((*iterChild)->m_pDockParent != (*iter).get()) + { + TRACE(_T("Error: Docking parent/Child information mismatch\n")); + bResult = FALSE; + } + if ((*iterChild)->GetParent() != (*iter).get()) + { + TRACE(_T("Error: Incorrect windows child parent relationship\n")); + bResult = FALSE; + } + } + } + + // Check dock parent chain + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + CDocker* pDockTopLevel = (*iter)->GetTopmostDocker(); + if (pDockTopLevel->IsDocked()) + TRACE(_T("Error: Top level parent should be undocked\n")); + } + + return bResult; + } + + inline void CDocker::Close() + { + // Destroy the docker + Hide(); + Destroy(); + } + + inline void CDocker::CloseAllDockers() + { + assert(this == GetDockAncestor()); // Must call CloseAllDockers from the DockAncestor + + std::vector <DockPtr>::iterator v; + + SetRedraw(FALSE); + std::vector<DockPtr> AllDockers = GetAllDockers(); + for (v = AllDockers.begin(); v != AllDockers.end(); ++v) + { + // The CDocker is destroyed when the window is destroyed + (*v)->m_bIsClosing = TRUE; + (*v)->Destroy(); // Destroy the window + } + + GetDockChildren().clear(); + SetRedraw(TRUE); + RecalcDockLayout(); + } + + inline void CDocker::CloseAllTargets() + { + GetDockAncestor()->m_TargetCentre.Destroy(); + GetDockAncestor()->m_TargetLeft.Destroy(); + GetDockAncestor()->m_TargetTop.Destroy(); + GetDockAncestor()->m_TargetRight.Destroy(); + GetDockAncestor()->m_TargetBottom.Destroy(); + } + + inline void CDocker::Dock(CDocker* pDocker, UINT DockStyle) + // Docks the specified docker inside this docker + { + assert(pDocker); + + pDocker->m_pDockParent = this; + pDocker->m_BlockMove = FALSE; + pDocker->SetDockStyle(DockStyle); + m_vDockChildren.push_back(pDocker); + pDocker->ConvertToChild(m_hWnd); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if (((DockStyle & 0xF) == DS_DOCKED_LEFT) || ((DockStyle &0xF) == DS_DOCKED_RIGHT)) + { + int Width = GetDockClient().GetWindowRect().Width(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Width - BarWidth)) + pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Width(); + } + else + { + int Height = GetDockClient().GetWindowRect().Height(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Height - BarWidth)) + pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Height(); + } + + // Redraw the docked windows + GetAncestor()->SetForegroundWindow(); + GetTopmostDocker()->m_hOldFocus = pDocker->GetView()->GetHwnd(); + pDocker->GetView()->SetFocus(); + + GetTopmostDocker()->SetRedraw(FALSE); + RecalcDockLayout(); + GetTopmostDocker()->SetRedraw(TRUE); + GetTopmostDocker()->RedrawWindow(); + } + + inline void CDocker::DockInContainer(CDocker* pDock, DWORD dwDockStyle) + // Add a container to an existing container + { + if ((dwDockStyle & DS_DOCKED_CONTAINER) && (dynamic_cast<CDockContainer*>(pDock->GetView()))) + { + // Transfer any dock children to this docker + pDock->MoveDockChildren(this); + + // Transfer container children to the target container + CDockContainer* pContainer = (CDockContainer*)GetView(); + CDockContainer* pContainerSource = (CDockContainer*)pDock->GetView(); + + if (pContainerSource->GetAllContainers().size() > 1) + { + // The container we're about to add has children, so transfer those first + std::vector<ContainerInfo>::reverse_iterator riter; + std::vector<ContainerInfo> AllContainers = pContainerSource->GetAllContainers(); + for ( riter = AllContainers.rbegin() ; riter < AllContainers.rend() -1; ++riter ) + { + // Remove child container from pContainerSource + CDockContainer* pContainerChild = (*riter).pContainer; + pContainerChild->ShowWindow(SW_HIDE); + pContainerSource->RemoveContainer(pContainerChild); + + // Add child container to this container + pContainer->AddContainer(pContainerChild); + + CDocker* pDockChild = GetDockFromView(pContainerChild); + pDockChild->SetParent(this); + pDockChild->m_pDockParent = this; + } + } + + pContainer->AddContainer((CDockContainer*)pDock->GetView()); + pDock->m_pDockParent = this; + pDock->m_BlockMove = FALSE; + pDock->ShowWindow(SW_HIDE); + pDock->SetWindowLongPtr(GWL_STYLE, WS_CHILD); + pDock->SetDockStyle(dwDockStyle); + pDock->SetParent(this); + } + } + + inline void CDocker::DockOuter(CDocker* pDocker, DWORD dwDockStyle) + // Docks the specified docker inside the dock ancestor + { + assert(pDocker); + + pDocker->m_pDockParent = GetDockAncestor(); + + DWORD OuterDocking = dwDockStyle & 0xF0000; + DWORD DockSide = OuterDocking / 0x10000; + dwDockStyle &= 0xFFF0FFFF; + dwDockStyle |= DockSide; + + // Set the dock styles + DWORD dwStyle = WS_CHILD | WS_VISIBLE; + pDocker->m_BlockMove = FALSE; + pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDocker->ShowWindow(SW_HIDE); + pDocker->SetDockStyle(dwDockStyle); + + // Set the docking relationships + std::vector<CDocker*>::iterator iter = GetDockAncestor()->m_vDockChildren.begin(); + GetDockAncestor()->m_vDockChildren.insert(iter, pDocker); + pDocker->SetParent(GetDockAncestor()); + pDocker->GetDockBar().SetParent(GetDockAncestor()); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if (((dwDockStyle & 0xF) == DS_DOCKED_LEFT) || ((dwDockStyle &0xF) == DS_DOCKED_RIGHT)) + { + int Width = GetDockAncestor()->GetDockClient().GetWindowRect().Width(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Width - BarWidth)) + pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Width(); + } + else + { + int Height = GetDockAncestor()->GetDockClient().GetWindowRect().Height(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Height - BarWidth)) + pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Height(); + } + + // Redraw the docked windows + GetAncestor()->SetFocus(); + pDocker->GetView()->SetFocus(); + RecalcDockLayout(); + } + + inline void CDocker::DrawAllCaptions() + { + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); iter++) + { + if ((*iter)->IsDocked()) + (*iter)->GetDockClient().DrawCaption((WPARAM)1); + } + } + + inline void CDocker::DrawHashBar(HWND hBar, POINT Pos) + // Draws a hashed bar while the splitter bar is being dragged + { + CDocker* pDock = ((CDockBar*)FromHandle(hBar))->GetDock(); + if (NULL == pDock) return; + + BOOL bVertical = ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_LEFT) || ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_RIGHT); + + CClientDC dcBar(this); + + WORD HashPattern[] = {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA}; + CBitmap bmHash; + CBrush brDithered; + bmHash.CreateBitmap(8, 8, 1, 1, HashPattern); + brDithered.CreatePatternBrush(&bmHash); + dcBar.SelectObject(&brDithered); + + CRect rc = FromHandle(hBar)->GetWindowRect(); + ScreenToClient(rc); + int cx = rc.Width(); + int cy = rc.Height(); + int BarWidth = pDock->GetDockBar().GetWidth(); + + if (bVertical) + dcBar.PatBlt(Pos.x - BarWidth/2, rc.top, BarWidth, cy, PATINVERT); + else + dcBar.PatBlt(rc.left, Pos.y - BarWidth/2, cx, BarWidth, PATINVERT); + } + + inline CDockContainer* CDocker::GetContainer() const + { + CDockContainer* pContainer = NULL; + if (dynamic_cast<CDockContainer*>(GetView())) + pContainer = (CDockContainer*)GetView(); + + return pContainer; + } + + inline CDocker* CDocker::GetActiveDocker() const + // Returns the docker whose child window has focus + { + CWnd* pWnd = GetFocus(); + CDocker* pDock= NULL; + while (pWnd && (pDock == NULL)) + { + if (IsRelated(pWnd)) + pDock = (CDocker*)pWnd; + + pWnd = pWnd->GetParent(); + } + + return pDock; + } + + inline CDocker* CDocker::GetDockAncestor() const + // The GetDockAncestor function retrieves the pointer to the + // ancestor (root docker parent) of the Docker. + { + return m_pDockAncestor; + } + + inline CDocker* CDocker::GetDockFromPoint(POINT pt) const + // Retrieves the Docker whose view window contains the specified point + { + // Step 1: Find the top level Docker the point is over + CDocker* pDockTop = NULL; + CWnd* pAncestor = GetDockAncestor()->GetAncestor(); + + // Iterate through all top level windows + CWnd* pWnd = GetWindow(GW_HWNDFIRST); + while(pWnd) + { + if (IsRelated(pWnd) || pWnd == pAncestor) + { + CDocker* pDockTest; + if (pWnd == pAncestor) + pDockTest = GetDockAncestor(); + else + pDockTest = (CDocker*)pWnd; + + CRect rc = pDockTest->GetClientRect(); + pDockTest->ClientToScreen(rc); + if ((this != pDockTest) && rc.PtInRect(pt)) + { + pDockTop = pDockTest; + break; + } + } + + pWnd = pWnd->GetWindow(GW_HWNDNEXT); + } + + // Step 2: Find the docker child whose view window has the point + CDocker* pDockTarget = NULL; + if (pDockTop) + { + CDocker* pDockParent = pDockTop; + CDocker* pDockTest = pDockParent; + + while (IsRelated(pDockTest)) + { + pDockParent = pDockTest; + CPoint ptLocal = pt; + pDockParent->ScreenToClient(ptLocal); + pDockTest = (CDocker*)pDockParent->ChildWindowFromPoint(ptLocal); + assert (pDockTest != pDockParent); + } + + CRect rc = pDockParent->GetDockClient().GetWindowRect(); + if (rc.PtInRect(pt)) pDockTarget = pDockParent; + } + + return pDockTarget; + } + + inline CDocker* CDocker::GetDockFromID(int n_DockID) const + { + std::vector <DockPtr>::iterator v; + + if (GetDockAncestor()) + { + for (v = GetDockAncestor()->m_vAllDockers.begin(); v != GetDockAncestor()->m_vAllDockers.end(); v++) + { + if (n_DockID == (*v)->GetDockID()) + return (*v).get(); + } + } + + return 0; + } + + inline CDocker* CDocker::GetDockFromView(CWnd* pView) const + { + CDocker* pDock = 0; + std::vector<DockPtr>::iterator iter; + std::vector<DockPtr> AllDockers = GetAllDockers(); + for (iter = AllDockers.begin(); iter != AllDockers.end(); ++iter) + { + if ((*iter)->GetView() == pView) + pDock = (*iter).get(); + } + + return pDock; + } + + inline int CDocker::GetDockSize() const + { + // Returns the size of the docker to be used if it is redocked + // Note: This function returns 0 if the docker has the DS_DOCKED_CONTAINER style + + CRect rcParent; + if (GetDockParent()) + rcParent = GetDockParent()->GetWindowRect(); + else + rcParent = GetDockAncestor()->GetWindowRect(); + + double DockSize = 0; + if ((GetDockStyle() & DS_DOCKED_LEFT) || (GetDockStyle() & DS_DOCKED_RIGHT)) + DockSize = (double)(rcParent.Width()*m_DockSizeRatio); + else if ((GetDockStyle() & DS_DOCKED_TOP) || (GetDockStyle() & DS_DOCKED_BOTTOM)) + DockSize = (double)(rcParent.Height()*m_DockSizeRatio); + else if ((GetDockStyle() & DS_DOCKED_CONTAINER)) + DockSize = 0; + + return (int)DockSize; + } + + inline CDocker* CDocker::GetTopmostDocker() const + // Returns the docker's parent at the top of the Z order. + // Could be the dock ancestor or an undocked docker. + { + CDocker* pDockTopLevel = (CDocker* const)this; + + while(pDockTopLevel->GetDockParent()) + { + assert (pDockTopLevel != pDockTopLevel->GetDockParent()); + pDockTopLevel = pDockTopLevel->GetDockParent(); + } + + return pDockTopLevel; + } + + inline CTabbedMDI* CDocker::GetTabbedMDI() const + { + CTabbedMDI* pTabbedMDI = NULL; + if (dynamic_cast<CTabbedMDI*>(GetView())) + pTabbedMDI = (CTabbedMDI*)GetView(); + + return pTabbedMDI; + } + + inline int CDocker::GetTextHeight() + { + NONCLIENTMETRICS nm = {0}; + nm.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &nm, 0); + LOGFONT lf = nm.lfStatusFont; + + CClientDC dc(this); + dc.CreateFontIndirect(&lf); + CSize szText = dc.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text"))); + return szText.cy; + } + + inline void CDocker::Hide() + { + // Undocks a docker (if needed) and hides it. + // Do unhide the docker, dock it. + + if (IsDocked()) + { + if (dynamic_cast<CDockContainer*>(GetView())) + { + CDockContainer* pContainer = GetContainer(); + CDocker* pDock = GetDockFromView(pContainer->GetContainerParent()); + pDock->UndockContainer(pContainer, GetCursorPos(), FALSE); + } + else + { + CDocker* pDockUndockedFrom = SeparateFromDock(); + pDockUndockedFrom->RecalcDockLayout(); + } + } + + ShowWindow(SW_HIDE); + } + + inline BOOL CDocker::IsChildOfDocker(CWnd* pWnd) const + // returns true if the specified window is a child of this docker + { + while ((pWnd != NULL) && (pWnd != GetDockAncestor())) + { + if (pWnd == (CWnd*)this) return TRUE; + if (IsRelated(pWnd)) break; + pWnd = pWnd->GetParent(); + } + + return FALSE; + } + + inline BOOL CDocker::IsDocked() const + { + return (((m_DockStyle&0xF) || (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression + } + + inline BOOL CDocker::IsDragAutoResize() + { + return m_bDragAutoResize; + } + + inline BOOL CDocker::IsRelated(CWnd* pWnd) const + // Returns TRUE if the hWnd is a docker within this dock family + { + if (GetDockAncestor() == pWnd) return TRUE; + + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + if ((*iter).get() == pWnd) return TRUE; + } + + return FALSE; + } + + inline BOOL CDocker::IsUndocked() const + { + return (!((m_DockStyle&0xF)|| (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression + } + + inline BOOL CDocker::LoadRegistrySettings(tString tsRegistryKeyName) + // Recreates the docker layout based on information stored in the registry. + // Assumes the DockAncestor window is already created. + { + BOOL bResult = FALSE; + + if (0 != tsRegistryKeyName.size()) + { + std::vector<DockInfo> vDockList; + std::vector<int> vActiveContainers; + tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\Dock Windows"); + HKEY hKey = 0; + RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey); + if (hKey) + { + DWORD dwType = REG_BINARY; + DWORD BufferSize = sizeof(DockInfo); + DockInfo di; + int i = 0; + TCHAR szNumber[20]; + tString tsSubKey = _T("DockChild"); + tsSubKey += _itot(i, szNumber, 10); + + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&di, &BufferSize)) + { + vDockList.push_back(di); + i++; + tsSubKey = _T("DockChild"); + tsSubKey += _itot(i, szNumber, 10); + } + + dwType = REG_DWORD; + BufferSize = sizeof(int); + int nID; + i = 0; + tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i, szNumber, 10); + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nID, &BufferSize)) + { + vActiveContainers.push_back(nID); + i++; + tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i, szNumber, 10); + } + + RegCloseKey(hKey); + if (vDockList.size() > 0) bResult = TRUE; + } + + // Add dockers without parents first + std::vector<DockInfo>::iterator iter; + for (iter = vDockList.begin(); iter < vDockList.end() ; ++iter) + { + DockInfo di = (*iter); + if (di.DockParentID == 0) + { + CDocker* pDocker = NewDockerFromID(di.DockID); + if (pDocker) + { + if (di.DockStyle & 0xF) + AddDockedChild(pDocker, di.DockStyle, di.DockSize, di.DockID); + else + AddUndockedChild(pDocker, di.DockStyle, di.DockSize, di.Rect, di.DockID); + } + else + { + TRACE(_T("Failed to add dockers without parents from registry")); + bResult = FALSE; + } + } + } + + // Remove dockers without parents from vDockList + for (UINT n = (UINT)vDockList.size(); n > 0; --n) + { + iter = vDockList.begin() + n-1; + if ((*iter).DockParentID == 0) + vDockList.erase(iter); + } + + // Add remaining dockers + while (vDockList.size() > 0) + { + bool bFound = false; + std::vector<DockInfo>::iterator iter; + for (iter = vDockList.begin(); iter < vDockList.end(); ++iter) + { + DockInfo di = *iter; + CDocker* pDockParent = GetDockFromID(di.DockParentID); + + if (pDockParent != 0) + { + CDocker* pDock = NewDockerFromID(di.DockID); + if(pDock) + { + pDockParent->AddDockedChild(pDock, di.DockStyle, di.DockSize, di.DockID); + bFound = true; + } + else + { + TRACE(_T("Failed to add dockers with parents from registry")); + bResult = FALSE; + } + + vDockList.erase(iter); + break; + } + } + + if (!bFound) + { + TRACE(_T("Orphaned dockers stored in registry ")); + bResult = FALSE; + break; + } + } + + std::vector<int>::iterator iterID; + for (iterID = vActiveContainers.begin(); iterID < vActiveContainers.end(); ++iterID) + { + CDocker* pDocker = GetDockFromID(*iterID); + if (pDocker) + { + CDockContainer* pContainer = pDocker->GetContainer(); + if (pContainer) + { + int nPage = pContainer->GetContainerIndex(pContainer); + if (nPage >= 0) + pContainer->SelectPage(nPage); + } + } + } + } + + if (!bResult) CloseAllDockers(); + return bResult; + } + + inline void CDocker::MoveDockChildren(CDocker* pDockTarget) + // Used internally by Dock and Undock + { + assert(pDockTarget); + + // Transfer any dock children from the current docker to the target docker + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter) + { + pDockTarget->GetDockChildren().push_back(*iter); + (*iter)->m_pDockParent = pDockTarget; + (*iter)->SetParent(pDockTarget); + (*iter)->GetDockBar().SetParent(pDockTarget); + } + GetDockChildren().clear(); + } + + inline CDocker* CDocker::NewDockerFromID(int nID) + // Used in LoadRegistrySettings. Creates a new Docker from the specified ID + { + UNREFERENCED_PARAMETER(nID); + + // Override this function to create the Docker objects as shown below + + CDocker* pDock = NULL; + /* switch(nID) + { + case ID_CLASSES: + pDock = new CDockClasses; + break; + case ID_FILES: + pDock = new CDockFiles; + break; + default: + TRACE(_T("Unknown Dock ID\n")); + break; + } */ + + return pDock; + } + + inline void CDocker::OnActivate(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + // Only top level undocked dockers get this message + if (LOWORD(wParam) == WA_INACTIVE) + { + GetTopmostDocker()->m_hOldFocus = ::GetFocus(); + + // Send a notification of focus lost + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = UWM_FRAMELOSTFOCUS; + SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + } + + inline void CDocker::OnCaptionTimer(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + if (this == GetDockAncestor()) + { + if (wParam == 1) + { + DrawAllCaptions(); + m_nTimerCount++; + if (m_nTimerCount == 10) + { + KillTimer(wParam); + m_nTimerCount = 0; + } + } + } + } + + inline void CDocker::OnCreate() + { + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (GetParent()->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + { + DWORD dwExStyle = GetWindowLongPtr(GWL_EXSTYLE); + SetWindowLongPtr(GWL_EXSTYLE, dwExStyle | WS_EX_LAYOUTRTL); + } +#endif + + // Create the various child windows + GetDockClient().SetDock(this); + GetDockClient().Create(this); + + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + GetView()->Create(&GetDockClient()); + + // Create the slider bar belonging to this docker + GetDockBar().SetDock(this); + if (GetDockAncestor() != this) + GetDockBar().Create(GetParent()); + + // Now remove the WS_POPUP style. It was required to allow this window + // to be owned by the frame window. + SetWindowLongPtr(GWL_STYLE, WS_CHILD); + SetParent(GetParent()); // Reinstate the window's parent + + // Set the default colour for the splitter bar + COLORREF rgbColour = GetSysColor(COLOR_BTNFACE); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0); + + if (pTheme && pTheme->UseThemes && pTheme->clrBkgnd2 != 0) + rgbColour =pTheme->clrBkgnd2; + + SetBarColor(rgbColour); + + // Set the caption height based on text height + m_NCHeight = MAX(20, GetTextHeight() + 5); + } + + inline void CDocker::OnDestroy(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Destroy any dock children first + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter) + { + (*iter)->Destroy(); + } + + if (dynamic_cast<CDockContainer*>(GetView()) && IsUndocked()) + { + CDockContainer* pContainer = (CDockContainer*)GetView(); + if (pContainer->GetAllContainers().size() > 1) + { + // This container has children, so destroy them now + std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers(); + std::vector<ContainerInfo>::iterator iter; + for (iter = AllContainers.begin(); iter < AllContainers.end(); ++iter) + { + if ((*iter).pContainer != pContainer) + { + // Reset container parent before destroying the dock window + CDocker* pDock = GetDockFromView((*iter).pContainer); + if (pContainer->IsWindow()) + pContainer->SetParent(&pDock->GetDockClient()); + + pDock->Destroy(); + } + } + } + } + + GetDockBar().Destroy(); + + // Post a destroy docker message + if ( GetDockAncestor()->IsWindow() ) + GetDockAncestor()->PostMessage(UWM_DOCK_DESTROYED, (WPARAM)this, 0L); + } + + inline void CDocker::OnDockDestroyed(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + CDocker* pDock = (CDocker*)wParam; + + assert( this == GetDockAncestor() ); + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + if ((*iter).get() == pDock) + { + GetAllDockers().erase(iter); + break; + } + } + } + + inline void CDocker::OnExitSizeMove(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_BlockMove = FALSE; + m_bIsDragging = FALSE; + SendNotify(UWM_DOCK_END); + } + + inline LRESULT CDocker::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + LPDRAGPOS pdp = (LPDRAGPOS)lParam; + + switch (((LPNMHDR)lParam)->code) + { + case UWM_DOCK_START: + { + if (IsDocked()) + { + Undock(GetCursorPos()); + SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pdp->ptPos.x, pdp->ptPos.y)); + } + } + break; + + case UWM_DOCK_MOVE: + { + CheckAllTargets((LPDRAGPOS)lParam); + } + break; + + case UWM_DOCK_END: + { + CDocker* pDock = (CDocker*)FromHandle(pdp->hdr.hwndFrom); + if (NULL == pDock) break; + + UINT DockZone = pdp->DockZone; + CRect rc = pDock->GetWindowRect(); + + switch(DockZone) + { + case DS_DOCKED_LEFT: + case DS_DOCKED_RIGHT: + pDock->SetDockSize(rc.Width()); + Dock(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_TOP: + case DS_DOCKED_BOTTOM: + pDock->SetDockSize(rc.Height()); + Dock(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_CONTAINER: + { + DockInContainer(pDock, pDock->GetDockStyle() | DockZone); + CDockContainer* pContainer = (CDockContainer*)GetView(); + int nPage = pContainer->GetContainerIndex((CDockContainer*)pDock->GetView()); + pContainer->SelectPage(nPage); + } + break; + case DS_DOCKED_LEFTMOST: + case DS_DOCKED_RIGHTMOST: + pDock->SetDockSize(rc.Width()); + DockOuter(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_TOPMOST: + case DS_DOCKED_BOTTOMMOST: + pDock->SetDockSize(rc.Height()); + DockOuter(pDock, pDock->GetDockStyle() | DockZone); + break; + } + + GetDockHint().Destroy(); + CloseAllTargets(); + } + break; + + case UWM_BAR_START: + { + CPoint pt = pdp->ptPos; + ScreenToClient(pt); + if (!IsDragAutoResize()) + DrawHashBar(pdp->hdr.hwndFrom, pt); + m_OldPoint = pt; + } + break; + + case UWM_BAR_MOVE: + { + CPoint pt = pdp->ptPos; + ScreenToClient(pt); + + if (pt != m_OldPoint) + { + if (IsDragAutoResize()) + ResizeDockers(pdp); + else + { + DrawHashBar(pdp->hdr.hwndFrom, m_OldPoint); + DrawHashBar(pdp->hdr.hwndFrom, pt); + } + + m_OldPoint = pt; + } + } + break; + + case UWM_BAR_END: + { + POINT pt = pdp->ptPos; + ScreenToClient(pt); + + if (!IsDragAutoResize()) + DrawHashBar(pdp->hdr.hwndFrom, pt); + + ResizeDockers(pdp); + } + break; + case NM_SETFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + break; + case UWM_FRAMEGOTFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, wParam, lParam); + break; + case UWM_FRAMELOSTFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, wParam, lParam); + break; + } + return 0L; + } + + inline void CDocker::ResizeDockers(LPDRAGPOS pdp) + // Called when the docker's splitter bar is dragged + { + assert(pdp); + + POINT pt = pdp->ptPos; + ScreenToClient(pt); + + CDocker* pDock = ((CDockBar*)FromHandle(pdp->hdr.hwndFrom))->GetDock(); + if (NULL == pDock) return; + + RECT rcDock = pDock->GetWindowRect(); + ScreenToClient(rcDock); + + double dBarWidth = pDock->GetDockBar().GetWidth(); + int iBarWidth = pDock->GetDockBar().GetWidth(); + int DockSize; + + switch (pDock->GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + DockSize = MAX(pt.x, iBarWidth/2) - rcDock.left - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_RIGHT: + DockSize = rcDock.right - MAX(pt.x, iBarWidth/2) - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_TOP: + DockSize = MAX(pt.y, iBarWidth/2) - rcDock.top - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height()); + break; + case DS_DOCKED_BOTTOM: + DockSize = rcDock.bottom - MAX(pt.y, iBarWidth/2) - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height()); + break; + } + + RecalcDockLayout(); + } + + inline void CDocker::OnSetFocus(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if (IsUndocked() && m_hOldFocus) + ::SetFocus(m_hOldFocus); + else + // Pass focus on the the view window + GetView()->SetFocus(); + + if ((this == GetTopmostDocker()) && (this != GetDockAncestor())) + { + // Send a notification to top level window + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = NM_SETFOCUS; + SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + } + + inline void CDocker::OnSysColorChange(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if (this == GetDockAncestor()) + { + COLORREF rgbColour = GetSysColor(COLOR_BTNFACE); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0); + + if (pTheme && pTheme->UseThemes && pTheme->clrBand2 != 0) + rgbColour = pTheme->clrBkgnd2; + else + rgbColour = GetSysColor(COLOR_BTNFACE); + + // Set the splitter bar colour for each docker decendant + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + (*iter)->SetBarColor(rgbColour); + + // Set the splitter bar colour for the docker ancestor + SetBarColor(rgbColour); + } + } + + inline LRESULT CDocker::OnSysCommand(WPARAM wParam, LPARAM lParam) + { + switch(wParam&0xFFF0) + { + case SC_MOVE: + // An undocked docker is being moved + { + BOOL bResult = FALSE; + m_bIsDragging = TRUE; + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if (SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bResult, 0)) + { + // Turn on DragFullWindows for this move + SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, 0, 0); + + // Process this message + DefWindowProc(WM_SYSCOMMAND, wParam, lParam); + + // Return DragFullWindows to its previous state + SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, bResult, 0, 0); + return 0L; + } + } + break; + case SC_CLOSE: + // The close button is pressed on an undocked docker + m_bIsClosing = TRUE; + break; + } + return CWnd::WndProcDefault(WM_SYSCOMMAND, wParam, lParam); + } + + inline LRESULT CDocker::OnWindowPosChanging(WPARAM wParam, LPARAM lParam) + { + // Suspend dock drag moving while over dock zone + if (m_BlockMove) + { + LPWINDOWPOS pWndPos = (LPWINDOWPOS)lParam; + pWndPos->flags |= SWP_NOMOVE|SWP_FRAMECHANGED; + return 0; + } + + return CWnd::WndProcDefault(WM_WINDOWPOSCHANGING, wParam, lParam); + } + + inline void CDocker::OnWindowPosChanged(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + if (m_bIsDragging) + { + // Send a Move notification to the parent + if ( IsLeftButtonDown() ) + { + LPWINDOWPOS wPos = (LPWINDOWPOS)lParam; + if ((!(wPos->flags & SWP_NOMOVE)) || m_BlockMove) + SendNotify(UWM_DOCK_MOVE); + } + else + { + CloseAllTargets(); + m_BlockMove = FALSE; + } + } + else if (this == GetTopmostDocker()) + { + // Reposition the dock children + if (IsUndocked() && IsWindowVisible() && !m_bIsClosing) RecalcDockLayout(); + } + } + + inline void CDocker::PreCreate(CREATESTRUCT &cs) + { + // Specify the WS_POPUP style to have this window owned + if (this != GetDockAncestor()) + cs.style = WS_POPUP; + + cs.dwExStyle = WS_EX_TOOLWINDOW; + } + + inline void CDocker::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = _T("Win32++ Docker"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDocker::RecalcDockChildLayout(CRect rc) + { + // This function positions the Docker's dock children, the Dockers client area + // and draws the dockbar bars. + + // Notes: + // 1) This function is called recursively. + // 2) The client area and child dockers are positioned simultaneously with + // DeferWindowPos to avoid drawing errors in complex docker arrangements. + // 3) The docker's client area contains the docker's caption (if any) and the docker's view window. + + // Note: All top level dockers are undocked, including the dock ancestor. + if (IsDocked()) + { + rc.OffsetRect(-rc.left, -rc.top); + } + + HDWP hdwp = BeginDeferWindowPos((int)m_vDockChildren.size() +2); + + // Step 1: Calculate the position of each Docker child, DockBar, and Client window. + // The Client area = the docker rect minus the area of dock children and the dock bar (splitter bar). + for (UINT u = 0; u < m_vDockChildren.size(); ++u) + { + CRect rcChild = rc; + double DockSize = m_vDockChildren[u]->m_DockStartSize;; + + // Calculate the size of the Docker children + switch (m_vDockChildren[u]->GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width()); + rcChild.right = rcChild.left + (int)DockSize; + break; + case DS_DOCKED_RIGHT: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width()); + rcChild.left = rcChild.right - (int)DockSize; + break; + case DS_DOCKED_TOP: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height()); + rcChild.bottom = rcChild.top + (int)DockSize; + break; + case DS_DOCKED_BOTTOM: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height()); + rcChild.top = rcChild.bottom - (int)DockSize; + break; + } + + if (m_vDockChildren[u]->IsDocked()) + { + // Position this docker's children + hdwp = m_vDockChildren[u]->DeferWindowPos(hdwp, NULL, rcChild, SWP_SHOWWINDOW|SWP_FRAMECHANGED); + m_vDockChildren[u]->m_rcChild = rcChild; + + rc.SubtractRect(rc, rcChild); + + // Calculate the dimensions of the splitter bar + CRect rcBar = rc; + DWORD DockSide = m_vDockChildren[u]->GetDockStyle() & 0xF; + + if (DS_DOCKED_LEFT == DockSide) rcBar.right = rcBar.left + m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_RIGHT == DockSide) rcBar.left = rcBar.right - m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_TOP == DockSide) rcBar.bottom = rcBar.top + m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_BOTTOM == DockSide) rcBar.top = rcBar.bottom - m_vDockChildren[u]->GetBarWidth(); + + // Save the splitter bar position. We will reposition it later. + m_vDockChildren[u]->m_rcBar = rcBar; + rc.SubtractRect(rc, rcBar); + } + } + + // Step 2: Position the Dock client and dock bar + hdwp = GetDockClient().DeferWindowPos(hdwp, NULL, rc, SWP_SHOWWINDOW |SWP_FRAMECHANGED); + EndDeferWindowPos(hdwp); + + // Position the dockbar. Only docked dockers have a dock bar. + if (IsDocked()) + { + // The SWP_NOCOPYBITS forces a redraw of the dock bar. + GetDockBar().SetWindowPos(NULL, m_rcBar, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOCOPYBITS ); + } + + // Step 3: Now recurse through the docker's children. They might have children of their own. + for (UINT v = 0; v < m_vDockChildren.size(); ++v) + { + m_vDockChildren[v]->RecalcDockChildLayout(m_vDockChildren[v]->m_rcChild); + } + } + + inline void CDocker::RecalcDockLayout() + // Repositions the dock children of a top level docker + { + if (GetDockAncestor()->IsWindow()) + { + CRect rc = GetTopmostDocker()->GetClientRect(); + GetTopmostDocker()->RecalcDockChildLayout(rc); + GetTopmostDocker()->UpdateWindow(); + } + } + + inline std::vector<CDocker*> CDocker::SortDockers() + // Returns a vector of sorted dockers, used by SaveRegistrySettings. + { + std::vector<CDocker*> vSorted; + std::vector<CDocker*>::iterator itSort; + std::vector<DockPtr>::iterator itAll; + + // Add undocked top level dockers + for (itAll = GetAllDockers().begin(); itAll < GetAllDockers().end(); ++itAll) + { + if (!(*itAll)->GetDockParent()) + vSorted.push_back((*itAll).get()); + } + + // Add dock ancestor's children + vSorted.insert(vSorted.end(), GetDockAncestor()->GetDockChildren().begin(), GetDockAncestor()->GetDockChildren().end()); + + // Add other dock children + int index = 0; + itSort = vSorted.begin(); + while (itSort < vSorted.end()) + { + vSorted.insert(vSorted.end(), (*itSort)->GetDockChildren().begin(), (*itSort)->GetDockChildren().end()); + itSort = vSorted.begin() + (++index); + } + + // Add dockers docked in containers + std::vector<CDocker*> vDockContainers; + for (itSort = vSorted.begin(); itSort< vSorted.end(); ++itSort) + { + if ((*itSort)->GetContainer()) + vDockContainers.push_back(*itSort); + } + + for (itSort = vDockContainers.begin(); itSort < vDockContainers.end(); ++itSort) + { + CDockContainer* pContainer = (*itSort)->GetContainer(); + + for (UINT i = 1; i < pContainer->GetAllContainers().size(); ++i) + { + CDockContainer* pChild = pContainer->GetContainerFromIndex(i); + CDocker* pDock = GetDockFromView(pChild); + vSorted.push_back(pDock); + } + } + + return vSorted; + } + + inline BOOL CDocker::SaveRegistrySettings(tString tsRegistryKeyName) + // Stores the docking configuration in the registry + // NOTE: This function assumes that each docker has a unique DockID + { + assert(VerifyDockers()); + + std::vector<CDocker*> vSorted = SortDockers(); + std::vector<CDocker*>::iterator iter; + std::vector<DockInfo> vDockInfo; + + if (0 != tsRegistryKeyName.size()) + { + // Fill the DockInfo vector with the docking information + for (iter = vSorted.begin(); iter < vSorted.end(); ++iter) + { + DockInfo di = {0}; + di.DockID = (*iter)->GetDockID(); + di.DockStyle = (*iter)->GetDockStyle(); + di.DockSize = (*iter)->GetDockSize(); + di.Rect = (*iter)->GetWindowRect(); + if ((*iter)->GetDockParent()) + di.DockParentID = (*iter)->GetDockParent()->GetDockID(); + + vDockInfo.push_back(di); + } + + tString tsKeyName = _T("Software\\") + tsRegistryKeyName; + HKEY hKey = NULL; + HKEY hKeyDock = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + RegDeleteKey(hKey, _T("Dock Windows")); + if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("Dock Windows"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyDock, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + // Add the Dock windows information to the registry + for (UINT u = 0; u < vDockInfo.size(); ++u) + { + DockInfo di = vDockInfo[u]; + TCHAR szNumber[16]; + tString tsSubKey = _T("DockChild"); + tsSubKey += _itot((int)u, szNumber, 10); + if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&di, sizeof(DockInfo))) + throw (CWinException(_T("RegSetValueEx failed"))); + } + + // Add Active Container to the registry + int i = 0; + for (iter = vSorted.begin(); iter < vSorted.end(); ++iter) + { + CDockContainer* pContainer = (*iter)->GetContainer(); + + if (pContainer && (pContainer == pContainer->GetActiveContainer())) + { + TCHAR szNumber[20]; + tString tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i++, szNumber, 10); + int nID = GetDockFromView(pContainer)->GetDockID(); + if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nID, sizeof(int))) + throw (CWinException(_T("RegSetValueEx failed"))); + } + } + + RegCloseKey(hKeyDock); + RegCloseKey(hKey); + } + + catch (const CWinException& e) + { + // Roll back the registry changes by deleting the subkeys + if (hKey) + { + if (hKeyDock) + { + RegDeleteKey(hKeyDock, _T("Dock Windows")); + RegCloseKey(hKeyDock); + } + + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + } + + return TRUE; + } + + inline void CDocker::SendNotify(UINT nMessageID) + // Sends a docking notification to the docker below the cursor + { + DRAGPOS DragPos; + DragPos.hdr.code = nMessageID; + DragPos.hdr.hwndFrom = m_hWnd; + DragPos.ptPos = GetCursorPos(); + DragPos.DockZone = m_dwDockZone; + m_dwDockZone = 0; + + CDocker* pDock = GetDockFromPoint(DragPos.ptPos); + + if (pDock) + pDock->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos); + else + { + if (GetDockHint().IsWindow()) GetDockHint().Destroy(); + CloseAllTargets(); + m_BlockMove = FALSE; + } + } + + inline void CDocker::SetDockStyle(DWORD dwDockStyle) + { + if (IsWindow()) + { + if ((dwDockStyle & DS_CLIENTEDGE) != (m_DockStyle & DS_CLIENTEDGE)) + { + if (dwDockStyle & DS_CLIENTEDGE) + { + DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE)|WS_EX_CLIENTEDGE; + GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle); + GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME); + } + else + { + DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE); + dwExStyle &= ~WS_EX_CLIENTEDGE; + GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle); + GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME); + } + } + + RecalcDockLayout(); + } + + m_DockStyle = dwDockStyle; + } + + inline void CDocker::SetCaption(LPCTSTR szCaption) + // Sets the caption text + { + GetDockClient().SetCaption(szCaption); + + if (IsWindow()) + SetWindowText(szCaption); + } + + inline void CDocker::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2) + { + GetDockClient().SetCaptionColors(Foregnd1, Backgnd1, ForeGnd2, BackGnd2); + } + + inline void CDocker::SetCaptionHeight(int nHeight) + // Sets the height of the caption + { + m_NCHeight = nHeight; + RedrawWindow(); + RecalcDockLayout(); + } + + inline void CDocker::SetDockSize(int DockSize) + // Sets the size of a docked docker + { + if (IsDocked()) + { + assert (m_pDockParent); + switch (GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_RIGHT: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_TOP: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height()); + break; + case DS_DOCKED_BOTTOM: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height()); + break; + } + + RecalcDockLayout(); + } + else + { + m_DockStartSize = DockSize; + m_DockSizeRatio = 1.0; + } + } + + inline void CDocker::SetDragAutoResize(BOOL bAutoResize) + { + m_bDragAutoResize = bAutoResize; + } + + inline void CDocker::SetView(CWnd& wndView) + // Assigns the view window to the docker + { + CWnd* pWnd = &wndView; + GetDockClient().SetView(wndView); + if (dynamic_cast<CDockContainer*>(pWnd)) + { + CDockContainer* pContainer = (CDockContainer*)&wndView; + SetCaption(pContainer->GetDockCaption().c_str()); + } + } + + inline void CDocker::PromoteFirstChild() + // One of the steps required for undocking + { + // Promote our first child to replace ourself + if (m_pDockParent) + { + for (UINT u = 0 ; u < m_pDockParent->m_vDockChildren.size(); ++u) + { + if (m_pDockParent->m_vDockChildren[u] == this) + { + if (m_vDockChildren.size() > 0) + // swap our first child for ourself as a child of the parent + m_pDockParent->m_vDockChildren[u] = m_vDockChildren[0]; + else + // remove ourself as a child of the parent + m_pDockParent->m_vDockChildren.erase(m_pDockParent->m_vDockChildren.begin() + u); + break; + } + } + } + + // Transfer styles and data and children to the child docker + CDocker* pDockFirstChild = NULL; + if (m_vDockChildren.size() > 0) + { + pDockFirstChild = m_vDockChildren[0]; + pDockFirstChild->m_DockStyle = (pDockFirstChild->m_DockStyle & 0xFFFFFFF0 ) | (m_DockStyle & 0xF); + pDockFirstChild->m_DockStartSize = m_DockStartSize; + pDockFirstChild->m_DockSizeRatio = m_DockSizeRatio; + + if (m_pDockParent) + { + pDockFirstChild->m_pDockParent = m_pDockParent; + pDockFirstChild->SetParent(m_pDockParent); + pDockFirstChild->GetDockBar().SetParent(m_pDockParent); + } + else + { + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin() + 1; iter < GetDockChildren().end(); ++iter) + (*iter)->ShowWindow(SW_HIDE); + + pDockFirstChild->ConvertToPopup(GetWindowRect()); + pDockFirstChild->GetDockBar().ShowWindow(SW_HIDE); + } + + m_vDockChildren.erase(m_vDockChildren.begin()); + MoveDockChildren(pDockFirstChild); + } + } + + inline void CDocker::ConvertToChild(HWND hWndParent) + { + DWORD dwStyle = WS_CHILD | WS_VISIBLE; + SetWindowLongPtr(GWL_STYLE, dwStyle); + SetParent(FromHandle(hWndParent)); + GetDockBar().SetParent(FromHandle(hWndParent)); + } + + inline void CDocker::ConvertToPopup(RECT rc) + { + // Change the window to an "undocked" style + ShowWindow(SW_HIDE); + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME; + SetWindowLongPtr(GWL_STYLE, dwStyle); + + // Change the window's parent and reposition it + GetDockBar().ShowWindow(SW_HIDE); + SetWindowPos(0, 0, 0, 0, 0, SWP_NOSENDCHANGING|SWP_HIDEWINDOW|SWP_NOREDRAW); + m_pDockParent = 0; + SetParent(0); + SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOOWNERZORDER); + GetDockClient().SetWindowPos(NULL, GetClientRect(), SWP_SHOWWINDOW); + + SetWindowText(GetCaption().c_str()); + } + + inline CDocker* CDocker::SeparateFromDock() + { + // This performs some of the tasks required for undocking. + // It is also used when a docker is hidden. + CDocker* pDockUndockedFrom = GetDockParent(); + if (!pDockUndockedFrom && (GetDockChildren().size() > 0)) + pDockUndockedFrom = GetDockChildren()[0]; + + GetTopmostDocker()->m_hOldFocus = 0; + PromoteFirstChild(); + m_pDockParent = 0; + + GetDockBar().ShowWindow(SW_HIDE); + m_DockStyle = m_DockStyle & 0xFFFFFFF0; + m_DockStyle &= ~DS_DOCKED_CONTAINER; + + return pDockUndockedFrom; + } + + inline void CDocker::SetUndockPosition(CPoint pt) + { + m_Undocking = TRUE; + CRect rc; + rc = GetDockClient().GetWindowRect(); + CRect rcTest = rc; + rcTest.bottom = MIN(rcTest.bottom, rcTest.top + m_NCHeight); + if ( !rcTest.PtInRect(pt)) + rc.SetRect(pt.x - rc.Width()/2, pt.y - m_NCHeight/2, pt.x + rc.Width()/2, pt.y - m_NCHeight/2 + rc.Height()); + + ConvertToPopup(rc); + m_Undocking = FALSE; + + // Send the undock notification to the frame + NMHDR nmhdr = {0}; + nmhdr.hwndFrom = m_hWnd; + nmhdr.code = UWM_UNDOCKED; + nmhdr.idFrom = m_nDockID; + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pFrame->SendMessage(WM_NOTIFY, m_nDockID, (LPARAM)&nmhdr); + + // Initiate the window move + SetCursorPos(pt.x, pt.y); + ScreenToClient(pt); + PostMessage(WM_SYSCOMMAND, (WPARAM)(SC_MOVE|0x0002), MAKELPARAM(pt.x, pt.y)); + } + + inline void CDocker::Undock(CPoint pt, BOOL bShowUndocked) + { + // Return if we shouldn't undock + if (GetDockStyle() & DS_NO_UNDOCK) return; + + // Undocking isn't supported on Win95 + if (1400 == GetWinVersion()) return; + + CDocker* pDockUndockedFrom = SeparateFromDock(); + + // Position and draw the undocked window, unless it is about to be closed + if (bShowUndocked) + { + SetUndockPosition(pt); + } + + RecalcDockLayout(); + if ((pDockUndockedFrom) && (pDockUndockedFrom->GetTopmostDocker() != GetTopmostDocker())) + pDockUndockedFrom->RecalcDockLayout(); + } + + inline void CDocker::UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked) + { + assert(pContainer); + assert(this == GetDockFromView(pContainer->GetContainerParent())); + + // Return if we shouldn't undock + if (GetDockFromView(pContainer)->GetDockStyle() & DS_NO_UNDOCK) return; + + // Undocking isn't supported on Win95 + if (1400 == GetWinVersion()) return; + + GetTopmostDocker()->m_hOldFocus = 0; + CDocker* pDockUndockedFrom = GetDockFromView(pContainer->GetContainerParent()); + pDockUndockedFrom->ShowWindow(SW_HIDE); + if (GetView() == pContainer) + { + // The parent container is being undocked, so we need + // to transfer our child containers to a different docker + + // Choose a new docker from among the dockers for child containers + CDocker* pDockNew = 0; + CDocker* pDockOld = GetDockFromView(pContainer); + std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers(); + std::vector<ContainerInfo>::iterator iter = AllContainers.begin(); + while ((0 == pDockNew) && (iter < AllContainers.end())) + { + if ((*iter).pContainer != pContainer) + pDockNew = (CDocker*)FromHandle(::GetParent((*iter).pContainer->GetParent()->GetHwnd())); + + ++iter; + } + + if (pDockNew) + { + // Move containers from the old docker to the new docker + CDockContainer* pContainerNew = (CDockContainer*)pDockNew->GetView(); + for (iter = AllContainers.begin(); iter != AllContainers.end(); ++iter) + { + if ((*iter).pContainer != pContainer) + { + CDockContainer* pChildContainer = (CDockContainer*)(*iter).pContainer; + pContainer->RemoveContainer(pChildContainer); + if (pContainerNew != pChildContainer) + { + pContainerNew->AddContainer(pChildContainer); + CDocker* pDock = GetDockFromView(pChildContainer); + pDock->SetParent(pDockNew); + pDock->m_pDockParent = pDockNew; + } + } + } + + // Now transfer the old docker's settings to the new one. + pDockUndockedFrom = pDockNew; + pDockNew->m_DockStyle = pDockOld->m_DockStyle; + pDockNew->m_DockStartSize = pDockOld->m_DockStartSize; + pDockNew->m_DockSizeRatio = pDockOld->m_DockSizeRatio; + if (pDockOld->IsDocked()) + { + pDockNew->m_pDockParent = pDockOld->m_pDockParent; + pDockNew->SetParent(pDockOld->GetParent()); + } + else + { + CRect rc = pDockOld->GetWindowRect(); + pDockNew->ShowWindow(SW_HIDE); + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE; + pDockNew->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDockNew->m_pDockParent = 0; + pDockNew->SetParent(0); + pDockNew->SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED| SWP_NOOWNERZORDER); + } + pDockNew->GetDockBar().SetParent(pDockOld->GetParent()); + pDockNew->GetView()->SetFocus(); + + // Transfer the Dock children to the new docker + pDockOld->MoveDockChildren(pDockNew); + + // insert pDockNew into its DockParent's DockChildren vector + if (pDockNew->m_pDockParent) + { + std::vector<CDocker*>::iterator p; + for (p = pDockNew->m_pDockParent->m_vDockChildren.begin(); p != pDockNew->m_pDockParent->m_vDockChildren.end(); ++p) + { + if (*p == this) + { + pDockNew->m_pDockParent->m_vDockChildren.insert(p, pDockNew); + break; + } + } + } + } + } + else + { + // This is a child container, so simply remove it from the parent + CDockContainer* pContainerParent = (CDockContainer*)GetView(); + pContainerParent->RemoveContainer(pContainer); + pContainerParent->SetTabSize(); + pContainerParent->SetFocus(); + pContainerParent->GetViewPage().SetParent(pContainerParent); + } + + // Finally do the actual undocking + CDocker* pDock = GetDockFromView(pContainer); + CRect rc = GetDockClient().GetWindowRect(); + ScreenToClient(rc); + pDock->GetDockClient().SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + pDock->Undock(pt, bShowUndocked); + pDockUndockedFrom->ShowWindow(); + pDockUndockedFrom->RecalcDockLayout(); + pDock->BringWindowToTop(); + } + + inline LRESULT CDocker::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_ACTIVATE: + OnActivate(wParam, lParam); + break; + case WM_SYSCOMMAND: + return OnSysCommand(wParam, lParam); + + case WM_EXITSIZEMOVE: + OnExitSizeMove(wParam, lParam); + break; + case WM_WINDOWPOSCHANGING: + return OnWindowPosChanging(wParam, lParam); + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + case WM_DESTROY: + OnDestroy(wParam, lParam); + break; + case WM_SETFOCUS: + OnSetFocus(wParam, lParam); + break; + case WM_TIMER: + OnCaptionTimer(wParam, lParam); + break; + case UWM_DOCK_DESTROYED: + OnDockDestroyed(wParam, lParam); + break; + case UWM_DOCK_ACTIVATED: + DrawAllCaptions(); + SetTimer(1, 100, NULL); + break; + case WM_SYSCOLORCHANGE: + OnSysColorChange(wParam, lParam); + break; + case WM_NCLBUTTONDBLCLK : + m_bIsDragging = FALSE; + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + ////////////////////////////////////// + // Declaration of the CDockContainer class + inline CDockContainer::CDockContainer() : m_iCurrentPage(0), m_hTabIcon(0), m_nTabPressed(-1) + { + m_pContainerParent = this; + } + + inline CDockContainer::~CDockContainer() + { + if (m_hTabIcon) + DestroyIcon(m_hTabIcon); + } + + inline void CDockContainer::AddContainer(CDockContainer* pContainer) + { + assert(pContainer); + + if (this == m_pContainerParent) + { + ContainerInfo ci = {0}; + ci.pContainer = pContainer; + lstrcpy(ci.szTitle, pContainer->GetTabText()); + ci.iImage = ImageList_AddIcon(GetImageList(), pContainer->GetTabIcon()); + int iNewPage = (int)m_vContainerInfo.size(); + m_vContainerInfo.push_back(ci); + + if (m_hWnd) + { + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = ci.iImage; + tie.pszText = m_vContainerInfo[iNewPage].szTitle; + TabCtrl_InsertItem(m_hWnd, iNewPage, &tie); + + SetTabSize(); + } + + pContainer->m_pContainerParent = this; + if (pContainer->IsWindow()) + { + // Set the parent container relationships + pContainer->GetViewPage().SetParent(this); + pContainer->GetViewPage().ShowWindow(SW_HIDE); + } + } + } + + inline void CDockContainer::AddToolBarButton(UINT nID, BOOL bEnabled /* = TRUE */) + // Adds Resource IDs to toolbar buttons. + // A resource ID of 0 is a separator + { + GetToolBar().AddButton(nID, bEnabled); + } + + inline CDockContainer* CDockContainer::GetContainerFromIndex(UINT nPage) + { + CDockContainer* pContainer = NULL; + if (nPage < m_vContainerInfo.size()) + pContainer = (CDockContainer*)m_vContainerInfo[nPage].pContainer; + + return pContainer; + } + + inline CWnd* CDockContainer::GetActiveView() const + // Returns a pointer to the active view window, or NULL if there is no active veiw. + { + CWnd* pWnd = NULL; + if (m_pContainerParent->m_vContainerInfo.size() > 0) + { + CDockContainer* pActiveContainer = m_pContainerParent->m_vContainerInfo[m_pContainerParent->m_iCurrentPage].pContainer; + if (pActiveContainer->GetViewPage().GetView()->IsWindow()) + pWnd = pActiveContainer->GetViewPage().GetView(); + } + + return pWnd; + } + + inline CDockContainer* CDockContainer::GetContainerFromView(CWnd* pView) const + { + assert(pView); + + std::vector<ContainerInfo>::iterator iter; + CDockContainer* pViewContainer = 0; + for (iter = GetAllContainers().begin(); iter != GetAllContainers().end(); ++iter) + { + CDockContainer* pContainer = (*iter).pContainer; + if (pContainer->GetView() == pView) + pViewContainer = pContainer; + } + + return pViewContainer; + } + + inline int CDockContainer::GetContainerIndex(CDockContainer* pContainer) + { + assert(pContainer); + int iReturn = -1; + + for (int i = 0; i < (int)m_pContainerParent->m_vContainerInfo.size(); ++i) + { + if (m_pContainerParent->m_vContainerInfo[i].pContainer == pContainer) + iReturn = i; + } + + return iReturn; + } + + inline SIZE CDockContainer::GetMaxTabTextSize() + { + CSize Size; + + // Allocate an iterator for the ContainerInfo vector + std::vector<ContainerInfo>::iterator iter; + + for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter) + { + CSize TempSize; + CClientDC dc(this); + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + dc.CreateFontIndirect(&info.lfStatusFont); + TempSize = dc.GetTextExtentPoint32(iter->szTitle, lstrlen(iter->szTitle)); + if (TempSize.cx > Size.cx) + Size = TempSize; + } + + return Size; + } + + inline void CDockContainer::SetupToolBar() + { + // Use this function to set the Resource IDs for the toolbar(s). + +/* // Set the Resource IDs for the toolbar buttons + AddToolBarButton( IDM_FILE_NEW ); + AddToolBarButton( IDM_FILE_OPEN ); + AddToolBarButton( IDM_FILE_SAVE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_EDIT_CUT ); + AddToolBarButton( IDM_EDIT_COPY ); + AddToolBarButton( IDM_EDIT_PASTE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_FILE_PRINT ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_HELP_ABOUT ); +*/ + } + + inline void CDockContainer::OnCreate() + { + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + + ContainerInfo ci = {0}; + ci.pContainer = this; + lstrcpy(ci.szTitle, GetTabText()); + ci.iImage = ImageList_AddIcon(GetImageList(), GetTabIcon()); + m_vContainerInfo.push_back(ci); + + // Create the page window + GetViewPage().Create(this); + + // Create the toolbar + GetToolBar().Create(&GetViewPage()); + DWORD style = (DWORD)GetToolBar().GetWindowLongPtr(GWL_STYLE); + style |= CCS_NODIVIDER ; + GetToolBar().SetWindowLongPtr(GWL_STYLE, style); + SetupToolBar(); + if (GetToolBar().GetToolBarData().size() > 0) + { + // Set the toolbar images + // A mask of 192,192,192 is compatible with AddBitmap (for Win95) + if (!GetToolBar().SendMessage(TB_GETIMAGELIST, 0L, 0L)) + GetToolBar().SetImages(RGB(192,192,192), IDW_MAIN, 0, 0); + + GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L); + } + else + GetToolBar().Destroy(); + + SetFixedWidth(TRUE); + SetOwnerDraw(TRUE); + + // Add tabs for each container. + for (int i = 0; i < (int)m_vContainerInfo.size(); ++i) + { + // Add tabs for each view. + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = i; + tie.pszText = m_vContainerInfo[i].szTitle; + TabCtrl_InsertItem(m_hWnd, i, &tie); + } + } + + inline void CDockContainer::OnLButtonDown(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnLButtonDown + + UNREFERENCED_PARAMETER(wParam); + + CPoint pt((DWORD)lParam); + TCHITTESTINFO info = {0}; + info.pt = pt; + m_nTabPressed = HitTest(info); + } + + inline void CDockContainer::OnLButtonUp(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnLButtonUp and takes no action + + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + } + + inline void CDockContainer::OnMouseLeave(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnMouseLeave + + if (IsLeftButtonDown() && (m_nTabPressed >= 0)) + { + CDocker* pDock = (CDocker*)FromHandle(::GetParent(GetParent()->GetHwnd())); + if (dynamic_cast<CDocker*>(pDock)) + { + CDockContainer* pContainer = GetContainerFromIndex(m_iCurrentPage); + pDock->UndockContainer(pContainer, GetCursorPos(), TRUE); + } + } + + m_nTabPressed = -1; + CTab::OnMouseLeave(wParam, lParam); + } + + inline LRESULT CDockContainer::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + { + // Display the newly selected tab page + int nPage = GetCurSel(); + SelectPage(nPage); + } + break; + } + + return 0L; + } + + inline void CDockContainer::PreCreate(CREATESTRUCT &cs) + { + // For Tabs on the bottom, add the TCS_BOTTOM style + CTab::PreCreate(cs); + cs.style |= TCS_BOTTOM; + } + + inline void CDockContainer::RecalcLayout() + { + if (GetContainerParent() == this) + { + // Set the tab sizes + SetTabSize(); + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + CDockContainer* pContainer = m_vContainerInfo[m_iCurrentPage].pContainer; + pContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + } + } + + inline void CDockContainer::RemoveContainer(CDockContainer* pWnd) + { + assert(pWnd); + + // Remove the tab + int iTab = GetContainerIndex(pWnd); + if (iTab > 0) + { + // DeleteItem(iTab); + TabCtrl_DeleteItem(m_hWnd, iTab); + } + + // Remove the ContainerInfo entry + std::vector<ContainerInfo>::iterator iter; + int iImage = -1; + for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter) + { + if (iter->pContainer == pWnd) + { + iImage = (*iter).iImage; + if (iImage >= 0) + TabCtrl_RemoveImage(m_hWnd, iImage); + + m_vContainerInfo.erase(iter); + break; + } + } + + // Set the parent container relationships + pWnd->GetViewPage().SetParent(pWnd); + pWnd->m_pContainerParent = pWnd; + + // Display the first page + m_iCurrentPage = 0; + if (IsWindow()) + SelectPage(0); + } + + inline void CDockContainer::SelectPage(int nPage) + { + if (this != m_pContainerParent) + m_pContainerParent->SelectPage(nPage); + else + { + if ((nPage >= 0) && (nPage < (int)m_vContainerInfo.size() )) + { + if (GetCurSel() != nPage) + SetCurSel(nPage); + + // Create the new container window if required + if (!m_vContainerInfo[nPage].pContainer->IsWindow()) + { + CDockContainer* pContainer = m_vContainerInfo[nPage].pContainer; + pContainer->Create(GetParent()); + pContainer->GetViewPage().SetParent(this); + } + + // Determine the size of the tab page's view area + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + + // Swap the pages over + CDockContainer* pOldContainer = m_vContainerInfo[m_iCurrentPage].pContainer; + CDockContainer* pNewContainer = m_vContainerInfo[nPage].pContainer; + pOldContainer->GetViewPage().ShowWindow(SW_HIDE); + pNewContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + pNewContainer->GetViewPage().GetView()->SetFocus(); + + // Adjust the docking caption + CDocker* pDock = (CDocker*)FromHandle(::GetParent(::GetParent(m_hWnd))); + if (dynamic_cast<CDocker*>(pDock)) + { + pDock->SetCaption(pNewContainer->GetDockCaption().c_str()); + pDock->RedrawWindow(); + } + + m_iCurrentPage = nPage; + } + } + } + + inline void CDockContainer::SetActiveContainer(CDockContainer* pContainer) + { + int nPage = GetContainerIndex(pContainer); + assert (0 <= nPage); + SelectPage(nPage); + } + + inline void CDockContainer::SetTabIcon(UINT nID_Icon) + { + HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(nID_Icon), IMAGE_ICON, 0, 0, LR_SHARED); + SetTabIcon(hIcon); + } + + inline void CDockContainer::SetTabSize() + { + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + if (rc.Width() < 0 ) + rc.SetRectEmpty(); + + int nItemWidth = MIN(25 + GetMaxTabTextSize().cx, (rc.Width()-2)/(int)m_vContainerInfo.size()); + int nItemHeight = MAX(20, GetTextHeight() + 5); + SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, nItemHeight)); + } + + inline void CDockContainer::SetTabText(UINT nTab, LPCTSTR szText) + { + CDockContainer* pContainer = GetContainerParent()->GetContainerFromIndex(nTab); + pContainer->SetTabText(szText); + + CTab::SetTabText(nTab, szText); + } + + inline void CDockContainer::SetView(CWnd& Wnd) + { + GetViewPage().SetView(Wnd); + } + + inline LRESULT CDockContainer::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_SIZE: + RecalcLayout(); + return 0; + + // The following are called in CTab::WndProcDefault + // case WM_LBUTTONDOWN: + // OnLButtonDown(wParam, lParam); + // break; + // case WM_LBUTTONUP: + // OnLButtonUp(wParam, lParam); + // break; + // case WM_MOUSELEAVE: + // OnMouseLeave(wParam, lParam); + // break; + + case WM_SETFOCUS: + { + // Pass focus on to the current view + GetActiveView()->SetFocus(); + } + break; + } + + // pass unhandled messages on to CTab for processing + return CTab::WndProcDefault(uMsg, wParam, lParam); + } + + + /////////////////////////////////////////// + // Declaration of the nested CViewPage class + inline BOOL CDockContainer::CViewPage::OnCommand(WPARAM wParam, LPARAM lParam) + { + CDockContainer* pContainer = (CDockContainer*)GetParent(); + BOOL bResult = FALSE; + if (pContainer && pContainer->GetActiveContainer()) + bResult = (BOOL)pContainer->GetActiveContainer()->SendMessage(WM_COMMAND, wParam, lParam); + + return bResult; + } + + inline void CDockContainer::CViewPage::OnCreate() + { + if (m_pView) + m_pView->Create(this); + } + + inline LRESULT CDockContainer::CViewPage::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + + // Display tooltips for the toolbar + case TTN_GETDISPINFO: + { + int iIndex = GetToolBar().HitTest(); + LPNMTTDISPINFO lpDispInfo = (LPNMTTDISPINFO)lParam; + if (iIndex >= 0) + { + int nID = GetToolBar().GetCommandID(iIndex); + if (nID > 0) + { + m_tsTooltip = LoadString(nID); + lpDispInfo->lpszText = (LPTSTR)m_tsTooltip.c_str(); + } + else + m_tsTooltip = _T(""); + } + } + break; + } // switch LPNMHDR + + return 0L; + } + + inline void CDockContainer::CViewPage::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = _T("Win32++ TabPage"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDockContainer::CViewPage::RecalcLayout() + { + CRect rc = GetClientRect(); + if (GetToolBar().IsWindow()) + { + GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L); + CRect rcToolBar = m_ToolBar.GetClientRect(); + rc.top += rcToolBar.Height(); + } + + GetView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + + inline void CDockContainer::CViewPage::SetView(CWnd& wndView) + // Sets or changes the View window displayed within the frame + { + // Assign the view window + m_pView = &wndView; + + if (m_hWnd) + { + if (!m_pView->IsWindow()) + { + // The container is already created, so create and position the new view too + GetView()->Create(this); + } + + RecalcLayout(); + } + } + + inline LRESULT CDockContainer::CViewPage::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_SIZE: + RecalcLayout(); + break; + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->code) + { + // Send the focus change notifications to the grandparent + case NM_KILLFOCUS: + case NM_SETFOCUS: + case UWM_FRAMELOSTFOCUS: + ::SendMessage(::GetParent(::GetParent(m_hWnd)), WM_NOTIFY, wParam, lParam); + break; + } + + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // _WIN32XX_DOCKING_H_ + |