📄 docktabsplitpane.h
字号:
#pragma once
#ifndef __DOCKSPLITPANE_H__
#define __DOCKSPLITPANE_H__
#ifndef __cplusplus
#error DockTabSplitPane.h requires C++ compilation (use a .cpp suffix)
#endif
#ifndef __ATLWIN_H__
#error DockTabSplitPane.h requires atlwin.h to be included first
#endif
#ifndef __ATLAPP_H__
#error DockTabSplitPane.h requires atlapp.h to be included first
#endif
#ifndef __ATLCOLL_H__
#error DockTabSplitPane.h requires atlcoll.h to be included first
#endif
#ifndef __ATLSTR_H__
#error DockTabSplitPane.h requires atlstr.h to be included first
#endif
#ifndef __ATLTYPES_H__
#error DockTabSplitPane.h requires atltypes.h to be included first
#endif
#ifndef __ATLSPLIT_H__
#error DockTabSplitPane.h requires atlsplit.h to be included first
#endif
#ifndef __ATLGDI_H__
#error DockTabSplitPane.h requires atlgdi.h to be included first
#endif
#ifndef __ATLCTRLS_H__
#error DockTabSplitPane.h requires atlctrls.h to be included first
#endif
/*********************************************************************
DockSplitTab::SplitPane class implementation
Written by Igor Katrayev.
Copyright (c) 2003 Igor Katrayev.
This code may be used in compiled form in any way you desire. This
file may be redistributed unmodified by any means PROVIDING it is
not sold for profit without the authors written consent, and
providing that this notice and the authors name is included.
This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability if it causes any damage to you or your
computer whatsoever. It's free, so don't hassle me about it.
Beware of bugs.
**********************************************************************/
#include "DockTabPane.h"
namespace DockSplitTab {
// destroy pane message
#define WM_USER_DESTROY_PANE WM_USER+1
_declspec(selectany) POINT pointNULL = { -1, -1};
_declspec(selectany) RECT rectNULL = { -1, -1, -1, -1};
template <bool t_bVertical = true>
class SplitterT: public CSplitterWindowImpl<SplitterT<t_bVertical>, t_bVertical> {
protected:
public:
~SplitterT() {
if ( this->m_hWnd) {
::DestroyWindow( this->m_hWnd);
this->m_hWnd = NULL;
}
}
DECLARE_WND_CLASS( _T("DockTab::Splitter"))
};
typedef SplitterT<true> VSplitter;
typedef SplitterT<false> HSplitter;
class RectTracker {
protected:
HWND trackWindow;
CRect trackRect;
static void drawRect( HDC hdc, LPRECT rect) {
int width = rect->right - rect->left;
int height = rect->bottom - rect->top;
const int size = 4;
if ( width <= size || height <= size) {
::PatBlt( hdc, rect->left, rect->top, width, height, PATINVERT);
} else {
::PatBlt( hdc, rect->left, rect->top, width-size, size, PATINVERT);
::PatBlt( hdc, rect->left+size, rect->bottom-size, width-size, size, PATINVERT);
::PatBlt( hdc, rect->left, rect->top+size, size, height-size, PATINVERT);
::PatBlt( hdc, rect->right-size, rect->top, size, height-size, PATINVERT);
}
}
public:
RectTracker()
: trackWindow( NULL)
, trackRect( -1, -1, -1, -1)
{}
RectTracker( HWND trackWindow)
: trackWindow( trackWindow)
, trackRect( -1, -1, -1, -1)
{}
void emptyRect() {
this->trackRect = rectNULL;
}
LPRECT getTrackRect() {
return this->trackRect;
}
const HWND getTrackWindow() const {
return this->trackWindow;
}
void drawTrackRectangle( HWND trackWindow, LPRECT rect, bool clear = true) {
if ( this->trackRect.EqualRect( rect) && this->trackWindow == trackWindow)
return;
// draw a new track rectangle
CWindowDC windowDC( trackWindow);
CBrush brush = CDCHandle::GetHalftoneBrush();
if ( NULL == brush.m_hBrush)
return;
CBrushHandle brushOld = windowDC.SelectBrush( brush);
if ( clear) {
if ( this->trackWindow != trackWindow)
this->clearTrackRectangle();
else if ( this->trackRect.TopLeft() != pointNULL)
this->drawRect( windowDC, this->trackRect);
}
this->drawRect( windowDC, rect);
windowDC.SelectBrush(brushOld);
this->trackRect = rect;
this->trackWindow = trackWindow;
}
void clearTrackRectangle( bool clear = true) {
if ( this->trackRect.TopLeft() != pointNULL) {
HWND wnd = this->trackWindow;
this->trackWindow = reinterpret_cast<HWND>( -1);
this->drawTrackRectangle( wnd, this->trackRect, false);
if ( clear) {
this->trackRect = rectNULL;
this->trackWindow = reinterpret_cast<HWND>( -1);
} else
this->trackWindow = wnd;
}
}
};
class SplitPane
: public CWindowImplBaseT< CWindow, CControlWinTraits>
, public CallBackListener
{
typedef CWindow TBase;
typedef CControlWinTraits TWinTraits;
typedef CWindowImplBaseT< CWindow, CControlWinTraits> ParentClass;
//----------------- protected classes
protected:
class DragContext
: public RectTracker {
public:
Pane::TabPaneHitTest tabPaneHitTest;
// Constructor/destructor for DragContext class
DragContext()
: RectTracker()
, tabPaneHitTest( Pane::TabPaneHitTest_Unknown)
{}
~DragContext() {
this->clearTrackRectangle();
}
};
//----------------- protected members
protected:
WTL::CImageList imageList;
CAtlMap< HWND, VSplitter*> verSplitterMap;
CAtlMap< HWND, HSplitter*> horSplitterMap;
CAtlMap< HWND, Pane*> paneMap;
CAtlMap< HWND, Pane*> clientViewPaneMap;
Pane* currentPane;
HWND rootWnd;
DragContext* dragContext;
CallBackListener* cbListener;
bool tabOnTop;
//----------------- protected interface
protected:
// Get a pane by a given point in screen coordinates
Pane* getPane( long x, long y) {
CPoint dropPoint( x, y);
Pane* result = NULL;
POSITION position = this->paneMap.GetStartPosition();
if ( position)
do {
Pane* pane = this->paneMap.GetNextValue( position);
CPoint pt = dropPoint;
pane->ScreenToClient( &pt);
CRect rect;
pane->GetClientRect( &rect);
if ( rect.PtInRect( pt)) {
result = pane;
break;
}
} while ( position);
return result;
} // Pane* getPane( long x, long y)
void moveCurrentTab( Pane* sourcePane, Pane* targetPane) {
ATLASSERT( NULL != sourcePane && NULL != targetPane && targetPane != sourcePane);
HWND clientWin = sourcePane->moveCurrentTabTo( targetPane);
this->clientViewPaneMap[clientWin] = targetPane;
HWND sourcePaneParentWnd = sourcePane->GetParent();
targetPane->SetFocus();
targetPane->updateLayout();
if ( sourcePane->isEmpty() && sourcePaneParentWnd != this->m_hWnd) {
// important:
// destroy the source pane via the message queue becouse this call maybe done by a tab control
// belonging to the source pane. we don't want to destroy it before it ends.
this->PostMessage( WM_USER_DESTROY_PANE, 0, (LPARAM) sourcePane->m_hWnd);
}
return;
} // moveCurrentTab( Pane* sourcePane, Pane* targetPane);
void splitWithCurrentTab( Pane* sourcePane, Pane* targetPane, Pane::TabPaneHitTest where) {
ATLASSERT( NULL != sourcePane);
ATLASSERT( NULL != targetPane);
ATLASSERT( Pane::TabPaneHitTest_Right == where
|| Pane::TabPaneHitTest_Left == where
|| Pane::TabPaneHitTest_Top == where
|| Pane::TabPaneHitTest_Bottom == where
);
VSplitter* verSplitter;
HSplitter* horSplitter;
CAtlMap< HWND, HSplitter*>::CPair* horSplitPair;
CAtlMap< HWND, VSplitter*>::CPair* verSplitPair;
CRect newPaneRect;
CRect newSplitterRect;
CRect sourcePaneRect;
CRect targetPaneRect;
HWND newSplitterWnd = NULL;
HWND targetPaneParent = ::GetParent( targetPane->m_hWnd);
CRect targetRect;
targetPane->GetWindowRect( &targetRect);
// create a new splitter
bool isVertical;
switch ( where) {
case Pane::TabPaneHitTest_Right:
case Pane::TabPaneHitTest_Left: {
isVertical = true;
verSplitter = new VSplitter();
verSplitter->Create( targetPaneParent, &rectNULL, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0);
this->verSplitterMap.SetAt( verSplitter->m_hWnd, verSplitter);
newSplitterWnd = verSplitter->m_hWnd;
}
break;
case Pane::TabPaneHitTest_Top:
case Pane::TabPaneHitTest_Bottom: {
isVertical = false;
horSplitter = new HSplitter();
horSplitter->Create( targetPaneParent, &rectNULL, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0);
this->horSplitterMap.SetAt( horSplitter->m_hWnd, horSplitter);
newSplitterWnd = horSplitter->m_hWnd;
}
break;
}
// create a new pane and move the current tab from the target pane to the new one
Pane* newPane = new Pane( this, this->tabOnTop);
newPane->Create( newSplitterWnd);
this->paneMap[ newPane->m_hWnd] = newPane;
HWND clientWin = sourcePane->moveCurrentTabTo( newPane);
this->clientViewPaneMap[ clientWin] = newPane;
// assign the splitter as the parent window to the target pane
targetPane->SetParent( newSplitterWnd);
// work out with the splitter parent window
if ( this->m_hWnd == targetPaneParent) {
// the first splitter created
this->rootWnd = newSplitterWnd;
newSplitterRect = CRect( 0, 0, targetRect.Width(), targetRect.Height());
} else {
// get a parent splitter for the target pane and reassign the pane to the new splitter created
VSplitter* parentVerSplitter = NULL;
HSplitter* parentHorSplitter = NULL;
::SetParent( newSplitterWnd, targetPaneParent);
if ( NULL != ( verSplitPair = this->verSplitterMap.Lookup( targetPaneParent))) {
// it is a vertical splitter
parentVerSplitter = verSplitPair->m_value;
if ( parentVerSplitter->GetSplitterPane( SPLIT_PANE_LEFT) == targetPane->m_hWnd) {
parentVerSplitter->SetSplitterPane( SPLIT_PANE_LEFT, newSplitterWnd, false);
parentVerSplitter->GetSplitterPaneRect( SPLIT_PANE_LEFT, &newSplitterRect);
} else if ( parentVerSplitter->GetSplitterPane( SPLIT_PANE_RIGHT) == targetPane->m_hWnd) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -