///////////////////////////////////////////////////////////////////////////// // Name: controlbar.cpp // Purpose: Implementation for main controlbar classes. // Author: Aleksandras Gluchovas // Modified by: // Created: 06/09/98 // RCS-ID: $Id: controlbar.cpp 50934 2007-12-28 07:16:38Z MR $ // Copyright: (c) Aleksandras Gluchovas // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include #include #include "wx/string.h" #include "wx/utils.h" // import wxMin,wxMax macros #include "wx/minifram.h" #include "wx/fl/controlbar.h" // import classes of default plugins #include "wx/fl/panedrawpl.h" #include "wx/fl/rowlayoutpl.h" #include "wx/fl/antiflickpl.h" #include "wx/fl/bardragpl.h" #include "wx/fl/cbcustom.h" #include "wx/fl/gcupdatesmgr.h" // import default updates manager class ("garbage-collecting" one) #include "wx/fl/updatesmgr.h" #include "wx/fl/toolwnd.h" // These are the event IDs being initialized to a value to // meet the new event paradigm as of wx2.3.0. Probably we // should find a way to make these be non-global, but this // works for right now. wxEventType cbEVT_PL_LEFT_DOWN = wxNewEventType(); wxEventType cbEVT_PL_LEFT_UP = wxNewEventType(); wxEventType cbEVT_PL_RIGHT_DOWN = wxNewEventType(); wxEventType cbEVT_PL_RIGHT_UP = wxNewEventType(); wxEventType cbEVT_PL_MOTION = wxNewEventType(); wxEventType cbEVT_PL_LEFT_DCLICK = wxNewEventType(); wxEventType cbEVT_PL_LAYOUT_ROW = wxNewEventType(); wxEventType cbEVT_PL_RESIZE_ROW = wxNewEventType(); wxEventType cbEVT_PL_LAYOUT_ROWS = wxNewEventType(); wxEventType cbEVT_PL_INSERT_BAR = wxNewEventType(); wxEventType cbEVT_PL_RESIZE_BAR = wxNewEventType(); wxEventType cbEVT_PL_REMOVE_BAR = wxNewEventType(); wxEventType cbEVT_PL_SIZE_BAR_WND = wxNewEventType(); wxEventType cbEVT_PL_DRAW_BAR_DECOR = wxNewEventType(); wxEventType cbEVT_PL_DRAW_ROW_DECOR = wxNewEventType(); wxEventType cbEVT_PL_DRAW_PANE_DECOR = wxNewEventType(); wxEventType cbEVT_PL_DRAW_BAR_HANDLES = wxNewEventType(); wxEventType cbEVT_PL_DRAW_ROW_HANDLES = wxNewEventType(); wxEventType cbEVT_PL_DRAW_ROW_BKGROUND = wxNewEventType(); wxEventType cbEVT_PL_DRAW_PANE_BKGROUND = wxNewEventType(); wxEventType cbEVT_PL_START_BAR_DRAGGING = wxNewEventType(); wxEventType cbEVT_PL_DRAW_HINT_RECT = wxNewEventType(); wxEventType cbEVT_PL_START_DRAW_IN_AREA = wxNewEventType(); wxEventType cbEVT_PL_FINISH_DRAW_IN_AREA = wxNewEventType(); wxEventType cbEVT_PL_CUSTOMIZE_BAR = wxNewEventType(); wxEventType cbEVT_PL_CUSTOMIZE_LAYOUT = wxNewEventType(); wxEventType wxCUSTOM_CB_PLUGIN_EVENTS_START_AT = wxNewEventType(); // some ascii-art, still can't get these *nice* cursors working on wx... :-( /* // FIXME:: see places where _gHorizCursorImg is used static const char* _gHorizCursorImg[] = { "............XX....XX............", "............XX....XX............", "............XX....XX............", "............XX....XX............", "............XX....XX............", "...X........XX....XX........X...", "..XX........XX....XX........XX..", ".XXX........XX....XX........XXX.", "XXXXXXXXXXXXXX....XXXXXXXXXXXXXX", ".XXX........XX....XX........XXX.", "..XX........XX....XX........XX..", "...X........XX....XX........X...", "............XX....XX............", "............XX....XX............", "............XX....XX............", "............XX....XX............" }; static const char* _gVertCursorImg[] = { "................X...............", "...............XXX..............", "..............XXXXX.............", ".............XXXXXXX............", "................X...............", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "................................", "................................", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "................X...............", ".............XXXXXXX............", "..............XXXXX.............", "...............XXX..............", "................X..............." }; */ // helper inline functions static inline bool rect_contains_point( const wxRect& rect, int x, int y ) { return ( x >= rect.x && y >= rect.y && x < rect.x + rect.width && y < rect.y + rect.height ); } static inline bool rect_hits_rect( const wxRect& r1, const wxRect& r2 ) { if ( ( r2.x >= r1.x && r2.x <= r1.x + r1.width ) || ( r1.x >= r2.x && r1.x <= r2.x + r2.width ) ) if ( ( r2.y >= r1.y && r2.y <= r1.y + r1.height ) || ( r1.y >= r2.y && r1.y <= r2.y + r2.height ) ) return 1; return 0; } static inline void hide_rect( wxRect& r ) { r.x = 32768; r.y = 32768; r.width = 1; r.height = 1; } static inline void clip_rect_against_rect( wxRect& r1, const wxRect& r2 ) { if ( r1.x < r2.x || r1.y < r2.y || r1.x >= r2.x + r2.width || r1.y >= r2.y + r2.height ) { hide_rect( r1 ); return; } else { if ( r1.x + r1.width > r2.x + r2.width ) r1.width = r2.x + r2.width - r1.x; if ( r1.y + r1.height > r2.y + r2.height ) r1.height = r2.y + r2.height - r1.y; } } /***** Implementation for class cbBarSpy *****/ IMPLEMENT_DYNAMIC_CLASS( cbBarSpy, wxEvtHandler ) cbBarSpy::cbBarSpy(void) : mpLayout(0), mpBarWnd(0) {} cbBarSpy::cbBarSpy( wxFrameLayout* pPanel ) : mpLayout(pPanel), mpBarWnd(0) {} void cbBarSpy::SetBarWindow( wxWindow* pWnd ) { mpBarWnd = pWnd; } bool cbBarSpy::ProcessEvent(wxEvent& event) { bool handled = wxEvtHandler::ProcessEvent( event ); int type = event.GetEventType(); if ( !handled && ( type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_DCLICK ) ) { wxMouseEvent& mevent = *((wxMouseEvent*)&event); int x = mevent.m_x; int y = mevent.m_y; mpBarWnd->ClientToScreen( &x, &y ); mpLayout->GetParentFrame().ScreenToClient( &x, &y ); mevent.m_x = x; mevent.m_y = y; // forwared not-handled event to frame-layout if ( type == wxEVT_LEFT_DOWN ) { //mpLayout->OnLButtonDown( mevent ); event.Skip(); } else mpLayout->OnLDblClick( mevent ); //event.Skip(false); } return handled; } /***** Implementation for class wxFrameLayout *****/ IMPLEMENT_DYNAMIC_CLASS( wxFrameLayout, wxEvtHandler ) BEGIN_EVENT_TABLE( wxFrameLayout, wxEvtHandler ) EVT_PAINT ( wxFrameLayout::OnPaint ) EVT_SIZE ( wxFrameLayout::OnSize ) EVT_LEFT_DOWN ( wxFrameLayout::OnLButtonDown ) EVT_LEFT_UP ( wxFrameLayout::OnLButtonUp ) EVT_RIGHT_DOWN ( wxFrameLayout::OnRButtonDown ) EVT_RIGHT_UP ( wxFrameLayout::OnRButtonUp ) EVT_MOTION ( wxFrameLayout::OnMouseMove ) EVT_LEFT_DCLICK( wxFrameLayout::OnLDblClick ) EVT_IDLE ( wxFrameLayout::OnIdle ) EVT_ERASE_BACKGROUND( wxFrameLayout::OnEraseBackground ) END_EVENT_TABLE() // FIXME:: how to eliminate these cut&pasted constructors? wxFrameLayout::wxFrameLayout(void) : mpFrame ( NULL ), mpFrameClient( NULL ), mDarkPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID ), mLightPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT), 1, wxSOLID ), mGrayPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ), mBlackPen ( wxColour( 0, 0, 0), 1, wxSOLID ), mBorderPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ), mNullPen( wxColour(0,0,0), 1, wxTRANSPARENT ), mpPaneInFocus( NULL ), mpLRUPane ( NULL ), mpTopPlugin ( NULL ), mpCaputesInput( NULL ), mClientWndRefreshPending( false ), mRecalcPending( true ), mCheckFocusWhenIdle( false ) { CreateCursors(); int i; for ( i = 0; i != MAX_PANES; ++i ) mPanes[i] = NULL; mFloatingOn = CanReparent(); } wxFrameLayout::wxFrameLayout( wxWindow* pParentFrame, wxWindow* pFrameClient, bool activateNow ) : mpFrame( pParentFrame ), mpFrameClient(pFrameClient), mDarkPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID ), mLightPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT), 1, wxSOLID ), mGrayPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ), mBlackPen ( wxColour( 0, 0, 0), 1, wxSOLID ), mBorderPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ), mNullPen( wxColour(0,0,0), 1, wxTRANSPARENT ), mpPaneInFocus( NULL ), mpLRUPane ( NULL ), mFloatingOn ( true ), mpTopPlugin ( NULL ), mpCaputesInput( NULL ), mClientWndRefreshPending( false ), mRecalcPending( true ), mCheckFocusWhenIdle( false ), mpUpdatesMgr( NULL ) { CreateCursors(); int i; for ( i = 0; i != MAX_PANES; ++i ) mPanes[i] = new cbDockPane( i, this ); if ( activateNow ) { HookUpToFrame(); // FOR NOW:: // DBG:: set RED color of frame's background for the // prurpose of tracking engine bugs "visually" GetParentFrame().SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE) ); } mFloatingOn = CanReparent(); } // NOTE:: below are the only platform-check "ifdef"s in the docking system! bool wxFrameLayout::CanReparent() { #ifdef __WXMSW__ return true; #elif defined(__WXGTK20__) return true; #elif defined (__WXGTK__) //return true; return false; #else return false; // reparenting is not yet supported by Motif and others #endif } /* #ifdef __WXMSW__ #inlcude "windows.h" #endif */ void wxFrameLayout::ReparentWindow( wxWindow* pChild, wxWindow* pNewParent ) { #if defined(__WXMSW__) || defined(__WXGTK20__) || defined(__WXMAC__) pChild->Reparent(pNewParent); return; #elif defined(__WXGTK__) || defined(__WXX11__) // FOR NOW:: floating with wxGtk still very buggy return; //pChild->ReParent( pNewParent ); //return; #else wxUnusedVar(pChild); wxUnusedVar(pNewParent); wxMessageBox( _("Sorry, docking is not supported for ports other than wxMSW, wxMac and wxGTK") ); #endif } void wxFrameLayout::DestroyBarWindows() { wxObjectList::compatibility_iterator pSpy = mBarSpyList.GetFirst(); while( pSpy ) { cbBarSpy& spy = *((cbBarSpy*)pSpy->GetData()); if ( spy.mpBarWnd->GetEventHandler() == &spy ) spy.mpBarWnd->PopEventHandler(); delete &spy; pSpy = pSpy->GetNext(); } mBarSpyList.Clear(); size_t i; for ( i = 0; i != mAllBars.Count(); ++i ) { if ( mAllBars[i]->mpBarWnd ) { mAllBars[i]->mpBarWnd->Destroy(); mAllBars[i]->mpBarWnd = NULL; } } } void wxFrameLayout::ShowFloatedWindows( bool show ) { wxObjectList::compatibility_iterator pNode = mFloatedFrames.GetFirst(); while( pNode ) { cbFloatedBarWindow* pFFrm = ((cbFloatedBarWindow*)pNode->GetData()); pFFrm->Show( show ); pNode = pNode->GetNext(); } } wxFrameLayout::~wxFrameLayout() { size_t i; UnhookFromFrame(); if ( mpUpdatesMgr ) delete mpUpdatesMgr; PopAllPlugins(); // destoy the chain of plugins from left to right wxEvtHandler* pCur = mpTopPlugin; if ( pCur ) while ( pCur->GetPreviousHandler() ) pCur = pCur->GetPreviousHandler(); while ( pCur ) { wxEvtHandler* pNext = pCur->GetNextHandler(); delete pCur; pCur = pNext; } // destroy contents of arrays and lists for ( i = 0; i != MAX_PANES; ++i ) { if ( mPanes[i] ) delete mPanes[i]; } if ( mpHorizCursor ) delete mpHorizCursor; if ( mpVertCursor ) delete mpVertCursor; if ( mpNormalCursor ) delete mpNormalCursor; if ( mpDragCursor ) delete mpDragCursor; if ( mpNECursor ) delete mpNECursor; wxObjectList::compatibility_iterator pSpy = mBarSpyList.GetFirst(); while( pSpy ) { cbBarSpy& spy = *((cbBarSpy*)pSpy->GetData()); if ( spy.mpBarWnd->GetEventHandler() == &spy ) spy.mpBarWnd->PopEventHandler(); delete &spy; pSpy = pSpy->GetNext(); } for ( i = 0; i != mAllBars.Count(); ++i ) delete mAllBars[i]; } void wxFrameLayout::EnableFloating( bool enable ) { mFloatingOn = enable && CanReparent(); } void wxFrameLayout::Activate() { HookUpToFrame(); RefreshNow( true ); ShowFloatedWindows( true ); } void wxFrameLayout::Deactivate() { ShowFloatedWindows( false ); UnhookFromFrame(); HideBarWindows(); } void wxFrameLayout::SetFrameClient( wxWindow* pFrameClient ) { mpFrameClient = pFrameClient; } wxWindow* wxFrameLayout::GetFrameClient() { return mpFrameClient; } cbUpdatesManagerBase& wxFrameLayout::GetUpdatesManager() { if ( !mpUpdatesMgr ) mpUpdatesMgr = CreateUpdatesManager(); return *mpUpdatesMgr; } void wxFrameLayout::SetUpdatesManager( cbUpdatesManagerBase* pUMgr ) { if ( mpUpdatesMgr ) delete mpUpdatesMgr; mpUpdatesMgr = pUMgr; mpUpdatesMgr->SetLayout( this ); } cbUpdatesManagerBase* wxFrameLayout::CreateUpdatesManager() { return new cbGCUpdatesMgr( this ); //return new cbSimpleUpdatesMgr( this ); } void wxFrameLayout::AddBar( wxWindow* pBarWnd, const cbDimInfo& dimInfo, int alignment, int rowNo, int columnPos, const wxString& name, bool spyEvents, int state ) { if ( pBarWnd && spyEvents ) { // hook up spy to bar window cbBarSpy* pSpy = new cbBarSpy( this ); pSpy->SetBarWindow( pBarWnd ); pBarWnd->PushEventHandler( pSpy ); mBarSpyList.Append( pSpy ); } cbBarInfo* pInfo = new cbBarInfo(); pInfo->mName = name; pInfo->mpBarWnd = pBarWnd; pInfo->mDimInfo = dimInfo; pInfo->mDimInfo.mLRUPane = alignment; pInfo->mState = state; pInfo->mAlignment = alignment; pInfo->mRowNo = rowNo; pInfo->mBounds.x = columnPos; mAllBars.Add( pInfo ); DoSetBarState( pInfo ); } bool wxFrameLayout::RedockBar( cbBarInfo* pBar, const wxRect& shapeInParent, cbDockPane* pToPane, bool updateNow ) { if ( !pToPane ) pToPane = HitTestPanes( shapeInParent, NULL ); if ( !pToPane ) return false; // bar's shape does not hit any pane // - redocking is NOT possible cbDockPane* pBarPane = GetBarPane( pBar ); if ( updateNow ) GetUpdatesManager().OnStartChanges(); pBarPane->RemoveBar( pBar ); // FIXME FIXME:: the recalculation below may be a *huge* performance // hit, it could be eliminated though... // but first the "pane-postion-changed" problem // has to be fixed RecalcLayout( false ); pToPane->InsertBar( pBar, shapeInParent ); RecalcLayout( false ); // finish update "transaction" if ( updateNow ) { GetUpdatesManager().OnFinishChanges(); GetUpdatesManager().UpdateNow(); } return true; } cbBarInfo* wxFrameLayout::FindBarByName( const wxString& name ) { size_t i; for ( i = 0; i != mAllBars.Count(); ++i ) if ( mAllBars[i]->mName == name ) return mAllBars[i]; return NULL; } cbBarInfo* wxFrameLayout::FindBarByWindow( const wxWindow* pWnd ) { size_t i; for ( i = 0; i != mAllBars.Count(); ++i ) if ( mAllBars[i]->mpBarWnd == pWnd ) return mAllBars[i]; return NULL; } BarArrayT& wxFrameLayout::GetBars() { return mAllBars; } void wxFrameLayout::SetBarState( cbBarInfo* pBar, int newState, bool updateNow ) { if ( newState == wxCBAR_FLOATING && !(mFloatingOn && pBar->mFloatingOn)) return; if ( updateNow ) GetUpdatesManager().OnStartChanges(); pBar->mUMgrData.SetDirty(true); // check bar's previous state if ( pBar->mState != wxCBAR_HIDDEN && pBar->mState != wxCBAR_FLOATING ) { cbDockPane* pPane; cbRowInfo* pRow; #ifdef __WXDEBUG__ bool success = #endif LocateBar( pBar, &pRow, &pPane ); wxASSERT( success ); // DBG:: // save LRU-dim info before removing bar pBar->mDimInfo.mLRUPane = pPane->GetAlignment(); pBar->mDimInfo.mBounds[ pPane->GetAlignment() ] = pBar->mBounds; // remove it from the pane it was docked on pPane->RemoveBar( pBar ); } if ( pBar->mState == wxCBAR_FLOATING && newState != wxCBAR_FLOATING ) { // remove bar's window from the containing mini-frame // and set its parent to be layout's parent frame if ( pBar->mpBarWnd ) { pBar->mpBarWnd->Show(false); // to avoid flicker upon reparenting wxObjectList::compatibility_iterator pNode = mFloatedFrames.GetFirst(); while( pNode ) { cbFloatedBarWindow* pFFrm = ((cbFloatedBarWindow*)pNode->GetData()); if ( pFFrm->GetBar() == pBar ) { pFFrm->Show( false ); // reduces flicker sligthly ReparentWindow( pBar->mpBarWnd, &GetParentFrame() ); pBar->mBounds = pBar->mDimInfo.mBounds[ pBar->mDimInfo.mLRUPane ]; if ( newState != wxCBAR_HIDDEN ) pBar->mAlignment = pBar->mDimInfo.mLRUPane; mFloatedFrames.Erase( pNode ); pFFrm->Show( false ); // Workaround assert that causes a crash on the next time something tries to CaptureMouse if (pFFrm->HasCapture()) pFFrm->ReleaseMouse(); pFFrm->Destroy(); break; } pNode = pNode->GetNext(); } // FOR NOW:: excessive! //if ( mpFrameClient ) mpFrameClient->Refresh(); if ( mpFrameClient ) mClientWndRefreshPending = true; } } if ( pBar->mDimInfo.GetDimHandler() ) { pBar->mDimInfo.GetDimHandler()->OnChangeBarState( pBar, newState ); } pBar->mState = newState; DoSetBarState( pBar ); if ( updateNow ) { RecalcLayout(false); GetUpdatesManager().OnFinishChanges(); GetUpdatesManager().UpdateNow(); } } void wxFrameLayout::InverseVisibility( cbBarInfo* pBar ) { wxASSERT( pBar ); // DBG:: // "inverse" bar-visibility of the selected bar int newState; if ( pBar->mState == wxCBAR_HIDDEN ) { if ( pBar->mAlignment == -1 ) { pBar->mAlignment = 0; // just remove "-1" marking newState = wxCBAR_FLOATING; } else if ( pBar->mAlignment == FL_ALIGN_TOP || pBar->mAlignment == FL_ALIGN_BOTTOM ) newState = wxCBAR_DOCKED_HORIZONTALLY; else newState = wxCBAR_DOCKED_VERTICALLY; } else { newState = wxCBAR_HIDDEN; if ( pBar->mState == wxCBAR_FLOATING ) pBar->mAlignment = -1; } this->SetBarState( pBar, newState, true ); if ( newState == wxCBAR_FLOATING ) this->RepositionFloatedBar( pBar ); } void wxFrameLayout::ApplyBarProperties( cbBarInfo* pBar ) { if ( pBar->mState == wxCBAR_FLOATING ) { RepositionFloatedBar( pBar ); } else if ( pBar->mState == wxCBAR_DOCKED_HORIZONTALLY || pBar->mState == wxCBAR_DOCKED_VERTICALLY ) { // FOR NOW:: nothing } } void wxFrameLayout::RepositionFloatedBar( cbBarInfo* pBar ) { if ( !(mFloatingOn && pBar->mFloatingOn)) return; wxObjectList::compatibility_iterator pNode = mFloatedFrames.GetFirst(); while( pNode ) { cbFloatedBarWindow* pFFrm = ((cbFloatedBarWindow*)pNode->GetData()); if ( pFFrm->GetBar() == pBar ) { wxRect& bounds = pBar->mDimInfo.mBounds[wxCBAR_FLOATING]; int x = bounds.x, y = bounds.y; GetParentFrame().ClientToScreen( &x, &y ); pFFrm->PositionFloatedWnd( x,y, bounds.width, bounds.height ); break; } pNode = pNode->GetNext(); } } void wxFrameLayout::DoSetBarState( cbBarInfo* pBar ) { if ( pBar->mState != wxCBAR_FLOATING && pBar->mState != wxCBAR_HIDDEN ) // dock it mPanes[pBar->mAlignment]->InsertBar( pBar ); else if ( pBar->mState == wxCBAR_HIDDEN ) { // hide it if ( pBar->mpBarWnd ) pBar->mpBarWnd->Show( false ); } else { if ( !(mFloatingOn && pBar->mFloatingOn) ) return; // float it if ( pBar->mpBarWnd == NULL || !CanReparent() ) { // FOR NOW:: just hide it if ( pBar->mpBarWnd ) pBar->mpBarWnd->Show( false ); pBar->mState = wxCBAR_HIDDEN; return; } cbFloatedBarWindow* pMiniFrm = new cbFloatedBarWindow(); pMiniFrm->SetBar( pBar ); pMiniFrm->SetLayout( this ); pMiniFrm->Create( &GetParentFrame(), wxID_ANY, pBar->mName, wxPoint( 50,50 ), wxSize ( 0, 0 ), wxFRAME_TOOL_WINDOW | wxFRAME_FLOAT_ON_PARENT ); pMiniFrm->SetClient( pBar->mpBarWnd ); ReparentWindow( pBar->mpBarWnd, pMiniFrm ); mFloatedFrames.Append( pMiniFrm ); wxRect& bounds = pBar->mDimInfo.mBounds[wxCBAR_FLOATING]; // check if it wasn't floated anytime before if ( bounds.width == -1 ) { wxRect& clntRect = GetClientRect(); // adjust position into which the next floated bar will be placed if ( mNextFloatedWndPos.x + bounds.width > clntRect.width ) mNextFloatedWndPos.x = mFloatingPosStep.x; if ( mNextFloatedWndPos.y + bounds.height > clntRect.height ) mNextFloatedWndPos.y = mFloatingPosStep.y; bounds.x = mNextFloatedWndPos.x + clntRect.x; bounds.y = mNextFloatedWndPos.y + clntRect.y; bounds.width = pBar->mDimInfo.mSizes[wxCBAR_FLOATING].x; bounds.height = pBar->mDimInfo.mSizes[wxCBAR_FLOATING].y; mNextFloatedWndPos.x += mFloatingPosStep.x; mNextFloatedWndPos.y += mFloatingPosStep.y; } pMiniFrm->Show( true ); RepositionFloatedBar(pMiniFrm->GetBar()); // FIXME:: this is excessive pBar->mpBarWnd->Show(true); } } void wxFrameLayout::RemoveBar( cbBarInfo* pBarInfo ) { // first, try to "guess" what was the perviouse state of the bar cbDockPane* pPane; cbRowInfo* pRow; if ( LocateBar( pBarInfo, &pRow, &pPane ) ) { // ...aha, bar was docked into one of the panes, // remove it from there pPane->RemoveBar( pBarInfo ); } size_t i; for ( i = 0; i != mAllBars.Count(); ++i ) { if ( mAllBars[i] == pBarInfo ) { #if wxCHECK_VERSION(2,3,2) mAllBars.RemoveAt(i); #else mAllBars.Remove(i); #endif if ( pBarInfo->mpBarWnd ) // hides it's window pBarInfo->mpBarWnd->Show( false ); delete pBarInfo; return; } } wxFAIL_MSG(wxT("bar info should be present in the list of all bars of all panes")); } bool wxFrameLayout::LocateBar( cbBarInfo* pBarInfo, cbRowInfo** ppRow, cbDockPane** ppPane ) { (*ppRow) = NULL; (*ppPane) = NULL; int n; for ( n = 0; n != MAX_PANES; ++n ) { wxBarIterator i( mPanes[n]->GetRowList() ); while ( i.Next() ) if ( &i.BarInfo() == pBarInfo ) { (*ppPane) = mPanes[n]; (*ppRow ) = &i.RowInfo(); return true; } } return false; } void wxFrameLayout::RecalcLayout( bool repositionBarsNow ) { mRecalcPending = false; int frmWidth, frmHeight; mpFrame->GetClientSize( &frmWidth, &frmHeight ); int curY = 0; int curX = 0; wxRect rect; // pane positioning priorities in decreasing order: // top, bottom, left, right // setup TOP pane cbDockPane* pPane = mPanes[ FL_ALIGN_TOP ]; pPane->SetPaneWidth( frmWidth ); pPane->RecalcLayout(); int paneHeight = pPane->GetPaneHeight(); rect.x = curX; rect.y = curY; rect.width = frmWidth; rect.height = wxMin( paneHeight, frmHeight - curY ); pPane->SetBoundsInParent( rect ); curY += paneHeight; // setup BOTTOM pane pPane = mPanes[ FL_ALIGN_BOTTOM ]; pPane->SetPaneWidth( frmWidth ); pPane->RecalcLayout(); paneHeight = pPane->GetPaneHeight(); rect.x = curX; rect.y = wxMax( frmHeight - paneHeight, curY ); rect.width = frmWidth; rect.height = frmHeight - rect.y; pPane->SetBoundsInParent( rect ); // setup LEFT pane pPane = mPanes[ FL_ALIGN_LEFT ]; // bottom pane's y pPane->SetPaneWidth( rect.y - curY ); pPane->RecalcLayout(); paneHeight = pPane->GetPaneHeight(); // bottom rect's y rect.height = rect.y - curY; rect.x = curX; rect.y = curY; rect.width = wxMin( paneHeight, frmWidth ); pPane->SetBoundsInParent( rect ); curX += rect.width; // setup RIGHT pane pPane = mPanes[ FL_ALIGN_RIGHT ]; // left pane's height pPane->SetPaneWidth( rect.height ); pPane->RecalcLayout(); paneHeight = pPane->GetPaneHeight(); // left pane's height rect.height = rect.height; rect.x = wxMax( frmWidth - paneHeight, curX ); rect.y = curY; rect.width = frmWidth - rect.x; pPane->SetBoundsInParent( rect ); // recalc bounds of the client-window mClntWndBounds.x = mPanes[FL_ALIGN_LEFT]->mBoundsInParent.x + mPanes[FL_ALIGN_LEFT]->mBoundsInParent.width; mClntWndBounds.y = mPanes[FL_ALIGN_TOP ]->mBoundsInParent.y + mPanes[FL_ALIGN_TOP ]->mBoundsInParent.height; mClntWndBounds.width = mPanes[FL_ALIGN_RIGHT]->mBoundsInParent.x - mClntWndBounds.x; mClntWndBounds.height = mPanes[FL_ALIGN_BOTTOM]->mBoundsInParent.y - mClntWndBounds.y; if ( repositionBarsNow ) PositionPanes(); } int wxFrameLayout::GetClientHeight() { // for better portablility wxWindow::GetSzie() is not used here return mClntWndBounds.height; } int wxFrameLayout::GetClientWidth() { // for better portablility wxWindow::GetSzie() is not used here return mClntWndBounds.width; } void wxFrameLayout::PositionClientWindow() { if ( mpFrameClient ) { if ( mClntWndBounds.width >= 1 && mClntWndBounds.height >= 1 ) { mpFrameClient->SetSize( mClntWndBounds.x, mClntWndBounds.y, mClntWndBounds.width, mClntWndBounds.height, 0 ); if ( !mpFrameClient->IsShown() ) mpFrameClient->Show( true ); } else mpFrameClient->Show( false ); } } void wxFrameLayout::PositionPanes() { PositionClientWindow(); // FOR NOW:: excessive updates! // reposition bars within all panes int i; for ( i = 0; i != MAX_PANES; ++i ) mPanes[i]->SizePaneObjects(); } void wxFrameLayout::OnSize( wxSizeEvent& event ) { mpFrame->ProcessEvent( event ); event.Skip( false ); // stop its progpagation if ( event.GetEventObject() == (wxObject*) mpFrame ) { GetUpdatesManager().OnStartChanges(); RecalcLayout(true); GetUpdatesManager().OnFinishChanges(); GetUpdatesManager().UpdateNow(); } } /*** protected members ***/ void wxFrameLayout::HideBarWindows() { size_t i; for ( i = 0; i != mAllBars.Count(); ++i ) if ( mAllBars[i]->mpBarWnd && mAllBars[i]->mState != wxCBAR_FLOATING ) mAllBars[i]->mpBarWnd->Show( false ); // then floated frames ShowFloatedWindows( false ); if ( mpFrameClient ) mpFrameClient->Show( false ); } void wxFrameLayout::UnhookFromFrame() { // NOTE:: the SetEvtHandlerEnabled() method is not used // here, since it is assumed that unhooking layout // from window may result destroying of the layout itself // // BUG BUG BUG (wx):: this would not be a problem if // wxEvtHandler's destructor checked if // this handler is currently the top-most // handler of some window, and additionally // to the reconnecting itself from the chain. // It would also re-setup current event handler // of the window using wxWindow::SetEventHandler() // FOR NOW:: if ( mpFrame->GetEventHandler() == this ) { mpFrame->PopEventHandler(); return; } if ( mpFrame ) { if ( this == mpFrame->GetEventHandler() ) { mpFrame->SetEventHandler( this->GetNextHandler() ); } else { wxEvtHandler* pCur = mpFrame->GetEventHandler(); while ( pCur ) { if ( pCur == this ) break; pCur = pCur->GetNextHandler(); } // do not try to unhook ourselves if we're not hooked yet if ( !pCur ) return; } if ( GetPreviousHandler() ) GetPreviousHandler()->SetNextHandler( GetNextHandler() ); else { mpFrame->PopEventHandler(); return; } if ( GetNextHandler() ) GetNextHandler()->SetPreviousHandler( GetPreviousHandler() ); SetNextHandler( NULL ); SetPreviousHandler( NULL ); } } void wxFrameLayout::HookUpToFrame() { // unhook us first, we're already hooked up UnhookFromFrame(); // put ourselves on top mpFrame->PushEventHandler( this ); } cbDockPane* wxFrameLayout::GetBarPane( cbBarInfo* pBar ) { int i; for ( i = 0; i != MAX_PANES; ++i ) if ( mPanes[i]->BarPresent( pBar ) ) return mPanes[i]; return NULL; } void wxFrameLayout::CreateCursors() { /* // FIXME:: The below code somehow doesn't work - cursors remain unchanged char bits[64]; set_cursor_bits( _gHorizCursorImg, bits, 32, 16 ); mpHorizCursor = new wxCursor( bits, 32, 16 ); set_cursor_bits( _gVertCursorImg, bits, 32, 16 ); mpVertCursor = new wxCursor( bits, 32, 16 ); */ // FOR NOW:: use standard ones mpHorizCursor = new wxCursor(wxCURSOR_SIZEWE); mpVertCursor = new wxCursor(wxCURSOR_SIZENS); mpNormalCursor = new wxCursor(wxCURSOR_ARROW ); mpDragCursor = new wxCursor(wxCURSOR_CROSS ); mpNECursor = new wxCursor(wxCURSOR_NO_ENTRY); mFloatingPosStep.x = 25; mFloatingPosStep.y = 25; mNextFloatedWndPos.x = mFloatingPosStep.x; mNextFloatedWndPos.y = mFloatingPosStep.y; } bool wxFrameLayout::HitTestPane( cbDockPane* pPane, int x, int y ) { return rect_contains_point( pPane->GetRealRect(), x, y ); } cbDockPane* wxFrameLayout::HitTestPanes( const wxRect& rect, cbDockPane* pCurPane ) { // first, give the privilege to the current pane if ( pCurPane && rect_hits_rect( pCurPane->GetRealRect(), rect ) ) return pCurPane; int i; for ( i = 0; i != MAX_PANES; ++i ) { if ( pCurPane != mPanes[i] && rect_hits_rect( mPanes[i]->GetRealRect(), rect ) ) { return mPanes[i]; } } return 0; } void wxFrameLayout::ForwardMouseEvent( wxMouseEvent& event, cbDockPane* pToPane, int eventType ) { wxPoint pos( event.m_x, event.m_y ); pToPane->FrameToPane( &pos.x, &pos.y ); if ( eventType == cbEVT_PL_LEFT_DOWN ) { cbLeftDownEvent evt( pos, pToPane ); FirePluginEvent( evt ); } else if ( eventType == cbEVT_PL_LEFT_DCLICK ) { cbLeftDClickEvent evt( pos, pToPane ); FirePluginEvent( evt ); } else if ( eventType == cbEVT_PL_LEFT_UP ) { cbLeftUpEvent evt( pos, pToPane ); FirePluginEvent( evt ); } else if ( eventType == cbEVT_PL_RIGHT_DOWN ) { cbRightDownEvent evt( pos, pToPane ); FirePluginEvent( evt ); } else if ( eventType == cbEVT_PL_RIGHT_UP ) { cbRightUpEvent evt( pos, pToPane ); FirePluginEvent( evt ); } else if ( eventType == cbEVT_PL_MOTION ) { cbMotionEvent evt( pos, pToPane ); FirePluginEvent( evt ); } } // wxFrameLayout::ForwardMouseEvent() void wxFrameLayout::RouteMouseEvent( wxMouseEvent& event, int pluginEvtType ) { if ( mpPaneInFocus ) ForwardMouseEvent( event, mpPaneInFocus, pluginEvtType ); else { int i; for ( i = 0; i != MAX_PANES; ++i ) { if ( HitTestPane( mPanes[i], event.m_x, event.m_y ) ) { ForwardMouseEvent( event, mPanes[i], pluginEvtType ); return; } } } } /*** event handlers ***/ void wxFrameLayout::OnRButtonDown( wxMouseEvent& event ) { RouteMouseEvent( event, cbEVT_PL_RIGHT_DOWN ); } void wxFrameLayout::OnRButtonUp( wxMouseEvent& event ) { RouteMouseEvent( event, cbEVT_PL_RIGHT_UP ); } void wxFrameLayout::OnLButtonDown( wxMouseEvent& event ) { RouteMouseEvent( event, cbEVT_PL_LEFT_DOWN ); } void wxFrameLayout::OnLDblClick( wxMouseEvent& event ) { RouteMouseEvent( event, cbEVT_PL_LEFT_DCLICK ); } void wxFrameLayout::OnLButtonUp( wxMouseEvent& event ) { RouteMouseEvent( event, cbEVT_PL_LEFT_UP ); } void wxFrameLayout::OnMouseMove( wxMouseEvent& event ) { if ( mpPaneInFocus ) ForwardMouseEvent( event, mpPaneInFocus, cbEVT_PL_MOTION ); else { int i; for ( i = 0; i != MAX_PANES; ++i ) { if ( HitTestPane( mPanes[i], event.m_x, event.m_y ) ) { if ( mpLRUPane && mpLRUPane != mPanes[i] ) { // simulate "mouse-leave" event ForwardMouseEvent( event, mpLRUPane, cbEVT_PL_MOTION ); } ForwardMouseEvent( event, mPanes[i], cbEVT_PL_MOTION ); mpLRUPane = mPanes[i]; return; } } } if ( mpLRUPane ) { // simulate "mouse-leave" event ForwardMouseEvent( event, mpLRUPane, cbEVT_PL_MOTION ); mpLRUPane = 0; } } void wxFrameLayout::OnPaint( wxPaintEvent& event ) { if ( mRecalcPending ) RecalcLayout( true ); wxPaintDC dc(mpFrame); int i; for ( i = 0; i != MAX_PANES; ++i ) { wxRect& rect = mPanes[i]->mBoundsInParent; dc.SetClippingRegion( rect.x, rect.y, rect.width, rect.height ); mPanes[i]->PaintPane(dc); dc.DestroyClippingRegion(); } event.Skip(); } void wxFrameLayout::OnEraseBackground( wxEraseEvent& WXUNUSED(event) ) { // do nothing } void wxFrameLayout::OnIdle( wxIdleEvent& event ) { wxWindow* focus = wxWindow::FindFocus(); if ( !focus && mCheckFocusWhenIdle ) { wxMessageBox(wxT("Hi, no more focus in this app!")); mCheckFocusWhenIdle = false; //ShowFloatedWindows( false ); } mCheckFocusWhenIdle = false; event.Skip(); } void wxFrameLayout::GetPaneProperties( cbCommonPaneProperties& props, int alignment ) { props = mPanes[alignment]->mProps; } void wxFrameLayout::SetPaneProperties( const cbCommonPaneProperties& props, int paneMask ) { int i; for ( i = 0; i != MAX_PANES; ++i ) { if ( mPanes[i]->MatchesMask( paneMask ) ) mPanes[i]->mProps = props; } } void wxFrameLayout::SetMargins( int top, int bottom, int left, int right, int paneMask ) { int i; for ( i = 0; i != MAX_PANES; ++i ) { cbDockPane& pane = *mPanes[i]; if ( pane.MatchesMask( paneMask ) ) { pane.mTopMargin = top; pane.mBottomMargin = bottom; pane.mLeftMargin = left; pane.mRightMargin = right; } } } void wxFrameLayout::SetPaneBackground( const wxColour& colour ) { mBorderPen.SetColour( colour ); } void wxFrameLayout::RefreshNow( bool recalcLayout ) { if ( recalcLayout ) RecalcLayout( true ); if ( mpFrame ) mpFrame->Refresh(); } /*** plugin-related methods ***/ void wxFrameLayout::FirePluginEvent( cbPluginEvent& event ) { // check state of input capture, before processing the event if ( mpCaputesInput ) { bool isInputEvt = true; #if wxCHECK_VERSION(2,3,0) if ( event.GetEventType() != cbEVT_PL_LEFT_DOWN && event.GetEventType() != cbEVT_PL_LEFT_UP && event.GetEventType() != cbEVT_PL_RIGHT_DOWN && event.GetEventType() != cbEVT_PL_RIGHT_UP && event.GetEventType() != cbEVT_PL_MOTION ) isInputEvt = false; #else switch ( event.m_eventType ) { case cbEVT_PL_LEFT_DOWN : break; case cbEVT_PL_LEFT_UP : break; case cbEVT_PL_RIGHT_DOWN : break; case cbEVT_PL_RIGHT_UP : break; case cbEVT_PL_MOTION : break; default : isInputEvt = false; break; } #endif // #if wxCHECK_VERSION(2,3,0) if ( isInputEvt ) { mpCaputesInput->ProcessEvent( event ); return; } } GetTopPlugin().ProcessEvent( event ); } void wxFrameLayout::CaptureEventsForPlugin ( cbPluginBase* pPlugin ) { // cannot capture events for more than one plugin at a time wxASSERT( mpCaputesInput == NULL ); mpCaputesInput = pPlugin; } void wxFrameLayout::ReleaseEventsFromPlugin( cbPluginBase* WXUNUSED(pPlugin) ) { // events should be captured first wxASSERT( mpCaputesInput != NULL ); mpCaputesInput = NULL; } void wxFrameLayout::CaptureEventsForPane( cbDockPane* toPane ) { // cannot capture events twice (without releasing) wxASSERT( mpPaneInFocus == NULL ); mpFrame->CaptureMouse(); mpPaneInFocus = toPane; } void wxFrameLayout::ReleaseEventsFromPane( cbDockPane* WXUNUSED(fromPane) ) { // cannot release events without capturing them wxASSERT( mpPaneInFocus != NULL ); mpFrame->ReleaseMouse(); mpPaneInFocus = NULL; } cbPluginBase& wxFrameLayout::GetTopPlugin() { if ( !mpTopPlugin ) PushDefaultPlugins(); // automatic configuration return *mpTopPlugin; } void wxFrameLayout::SetTopPlugin( cbPluginBase* pPlugin ) { mpTopPlugin = pPlugin; } bool wxFrameLayout::HasTopPlugin() { return ( mpTopPlugin != NULL ); } void wxFrameLayout::PushPlugin( cbPluginBase* pPlugin ) { if ( !mpTopPlugin ) mpTopPlugin = pPlugin; else { pPlugin->SetNextHandler( mpTopPlugin ); mpTopPlugin->SetPreviousHandler( pPlugin ); mpTopPlugin = pPlugin; } mpTopPlugin->OnInitPlugin(); // notification } void wxFrameLayout::PopPlugin() { wxASSERT( mpTopPlugin ); // DBG:: at least one plugin should be present cbPluginBase* pPopped = mpTopPlugin; mpTopPlugin = (cbPluginBase*)mpTopPlugin->GetNextHandler(); delete pPopped; } void wxFrameLayout::PopAllPlugins() { while( mpTopPlugin ) PopPlugin(); } void wxFrameLayout::PushDefaultPlugins() { // FIXME:: to much of the stuff for the default... AddPlugin( CLASSINFO( cbRowLayoutPlugin ) ); AddPlugin( CLASSINFO( cbBarDragPlugin ) ); AddPlugin( CLASSINFO( cbPaneDrawPlugin ) ); } void wxFrameLayout::AddPlugin( wxClassInfo* pPlInfo, int paneMask ) { if ( FindPlugin ( pPlInfo ) ) return; // same type of plugin cannot be added twice cbPluginBase* pObj = (cbPluginBase*)pPlInfo->CreateObject(); wxASSERT(pObj); // DBG:: plugin's class should be dynamic pObj->mPaneMask = paneMask; pObj->mpLayout = this; PushPlugin( pObj ); } void wxFrameLayout::AddPluginBefore( wxClassInfo* pNextPlInfo, wxClassInfo* pPlInfo, int paneMask ) { wxASSERT( pNextPlInfo != pPlInfo ); // DBG:: no sense cbPluginBase* pNextPl = FindPlugin( pNextPlInfo ); if ( !pNextPl ) { AddPlugin( pPlInfo, paneMask ); return; } // remove existing one if present cbPluginBase* pExistingPl = FindPlugin( pPlInfo ); if ( pExistingPl ) RemovePlugin( pPlInfo ); // create an instance cbPluginBase* pNewPl = (cbPluginBase*)pPlInfo->CreateObject(); wxASSERT(pNewPl); // DBG:: plugin's class should be dynamic // insert it to the chain if ( pNextPl->GetPreviousHandler() ) pNextPl->GetPreviousHandler()->SetNextHandler( pNewPl ); else mpTopPlugin = pNewPl; pNewPl->SetNextHandler( pNextPl ); pNewPl->SetPreviousHandler( pNextPl->GetPreviousHandler() ); pNextPl->SetPreviousHandler( pNewPl ); // set it up pNewPl->mPaneMask = paneMask; pNewPl->mpLayout = this; pNewPl->OnInitPlugin(); } void wxFrameLayout::RemovePlugin( wxClassInfo* pPlInfo ) { cbPluginBase* pPlugin = FindPlugin( pPlInfo ); if ( !pPlugin ) return; // it's OK to remove not-existing plugin ;-) if ( pPlugin->GetPreviousHandler() == NULL ) mpTopPlugin = (cbPluginBase*)pPlugin->GetNextHandler(); delete pPlugin; } cbPluginBase* wxFrameLayout::FindPlugin( wxClassInfo* pPlInfo ) { cbPluginBase *pCur = mpTopPlugin; while( pCur ) { // NOTE:: it might appear useful matching plugin // classes "polymorphically": if ( pCur->GetClassInfo()->IsKindOf( pPlInfo ) ) return pCur; pCur = (cbPluginBase*)pCur->GetNextHandler(); } return NULL; } /***** Implementation for class cbUpdateMgrData *****/ IMPLEMENT_DYNAMIC_CLASS( cbUpdateMgrData, wxObject ) cbUpdateMgrData::cbUpdateMgrData() : mPrevBounds( -1,-1,0,0 ), mIsDirty( true ), // inidicate initial change mpCustomData(0) {} void cbUpdateMgrData::StoreItemState( const wxRect& boundsInParent ) { mPrevBounds = boundsInParent; } void cbUpdateMgrData::SetDirty( bool isDirty ) { mIsDirty = isDirty; } void cbUpdateMgrData::SetCustomData( wxObject* pCustomData ) { mpCustomData = pCustomData; } /***** Implementation for class cbDockPane *****/ void wxBarIterator::Reset() { mpRow = ( mpRows->Count() ) ? (*mpRows)[0] : NULL; mpBar = NULL; } wxBarIterator::wxBarIterator( RowArrayT& rows ) : mpRows( &rows ), mpRow ( NULL ), mpBar ( NULL ) { Reset(); } bool wxBarIterator::Next() { if ( mpRow ) { if ( mpBar ) mpBar = mpBar->mpNext; else { if ( mpRow->mBars.GetCount() == 0 ) { return false; } mpBar = mpRow->mBars[0]; } if ( !mpBar ) { // skip to the next row mpRow = mpRow->mpNext; if ( mpRow ) mpBar = mpRow->mBars[0]; else return false; } return true; } else return false; } cbBarInfo& wxBarIterator::BarInfo() { return *mpBar; } cbRowInfo& wxBarIterator::RowInfo() { return *mpRow; } /***** Implementation for class cbBarDimHandlerBase *****/ IMPLEMENT_ABSTRACT_CLASS( cbBarDimHandlerBase, wxObject ) cbBarDimHandlerBase::cbBarDimHandlerBase() : mRefCount(0) {} void cbBarDimHandlerBase::AddRef() { ++mRefCount; } void cbBarDimHandlerBase::RemoveRef() { if ( --mRefCount <= 0 ) delete this; } /***** Implementation for class cbDimInfo *****/ IMPLEMENT_DYNAMIC_CLASS( cbDimInfo, wxObject ) cbDimInfo::cbDimInfo() : mVertGap ( 0 ), mHorizGap( 0 ), mIsFixed(true), mpHandler( NULL ) { size_t i; for ( i = 0; i != MAX_BAR_STATES; ++i ) { mSizes[i].x = 20; mSizes[i].y = 20; mBounds[i] = wxRect( -1,-1,-1,-1 ); } } cbDimInfo::cbDimInfo( cbBarDimHandlerBase* pDimHandler, bool isFixed ) : mVertGap ( 0 ), mHorizGap( 0 ), mIsFixed ( isFixed ), mpHandler( pDimHandler ) { if ( mpHandler ) { // int vtad = *((int*)mpHandler); mpHandler->AddRef(); } size_t i; for ( i = 0; i != MAX_BAR_STATES; ++i ) { mSizes[i].x = -1; mSizes[i].y = -1; mBounds[i] = wxRect( -1,-1,-1,-1 ); } } cbDimInfo::cbDimInfo( int dh_x, int dh_y, int dv_x, int dv_y, int f_x, int f_y, bool isFixed, int horizGap, int vertGap, cbBarDimHandlerBase* pDimHandler ) : mVertGap ( vertGap ), mHorizGap ( horizGap ), mIsFixed ( isFixed ), mpHandler( pDimHandler ) { if ( mpHandler ) { // int vtad = *((int*)mpHandler); mpHandler->AddRef(); } mSizes[wxCBAR_DOCKED_HORIZONTALLY].x = dh_x; mSizes[wxCBAR_DOCKED_HORIZONTALLY].y = dh_y; mSizes[wxCBAR_DOCKED_VERTICALLY ].x = dv_x; mSizes[wxCBAR_DOCKED_VERTICALLY ].y = dv_y; mSizes[wxCBAR_FLOATING ].x = f_x; mSizes[wxCBAR_FLOATING ].y = f_y; size_t i; for ( i = 0; i != MAX_BAR_STATES; ++i ) mBounds[i] = wxRect( -1,-1,-1,-1 ); } cbDimInfo::cbDimInfo( int x, int y, bool isFixed, int gap, cbBarDimHandlerBase* pDimHandler) : mVertGap ( gap ), mHorizGap ( gap ), mIsFixed ( isFixed ), mpHandler( pDimHandler ) { if ( mpHandler ) { // int vtad = *((int*)mpHandler); mpHandler->AddRef(); } mSizes[wxCBAR_DOCKED_HORIZONTALLY].x = x; mSizes[wxCBAR_DOCKED_HORIZONTALLY].y = y; mSizes[wxCBAR_DOCKED_VERTICALLY ].x = x; mSizes[wxCBAR_DOCKED_VERTICALLY ].y = y; mSizes[wxCBAR_FLOATING ].x = x; mSizes[wxCBAR_FLOATING ].y = y; size_t i; for ( i = 0; i != MAX_BAR_STATES; ++i ) mBounds[i] = wxRect( -1,-1,-1,-1 ); } cbDimInfo::~cbDimInfo() { if ( mpHandler ) mpHandler->RemoveRef(); } const cbDimInfo& cbDimInfo::operator=( const cbDimInfo& other ) { if ( this == &other ) return *this; int i; for ( i = 0; i != MAX_BAR_STATES; ++i ) mSizes[i] = other.mSizes[i]; mIsFixed = other.mIsFixed; mpHandler = other.mpHandler; mVertGap = other.mVertGap; mHorizGap = other.mHorizGap; if ( mpHandler ) mpHandler->AddRef(); return *this; } /***** Implementation for structure cbCommonPaneProperties *****/ IMPLEMENT_DYNAMIC_CLASS( cbCommonPaneProperties, wxObject ) cbCommonPaneProperties::cbCommonPaneProperties(void) : mRealTimeUpdatesOn ( true ), mOutOfPaneDragOn ( true ), mExactDockPredictionOn( false ), mNonDestructFrictionOn( false ), mShow3DPaneBorderOn ( true ), mBarFloatingOn ( false ), mRowProportionsOn ( false ), mColProportionsOn ( true ), mBarCollapseIconsOn ( false ), mBarDragHintsOn ( false ), mMinCBarDim( 16, 16 ), mResizeHandleSize( 4 ) {} cbCommonPaneProperties::cbCommonPaneProperties(const cbCommonPaneProperties& props) : wxObject(), mRealTimeUpdatesOn (props.mRealTimeUpdatesOn), mOutOfPaneDragOn (props.mOutOfPaneDragOn), mExactDockPredictionOn(props.mExactDockPredictionOn), mNonDestructFrictionOn(props.mNonDestructFrictionOn), mShow3DPaneBorderOn (props.mShow3DPaneBorderOn), mBarFloatingOn (props.mBarFloatingOn), mRowProportionsOn (props.mRowProportionsOn), mColProportionsOn (props.mColProportionsOn), mBarCollapseIconsOn (props.mBarCollapseIconsOn), mBarDragHintsOn (props.mBarDragHintsOn), mMinCBarDim(props.mMinCBarDim), mResizeHandleSize(props.mResizeHandleSize) {} cbCommonPaneProperties& cbCommonPaneProperties::operator=(const cbCommonPaneProperties& props) { mRealTimeUpdatesOn = props.mRealTimeUpdatesOn; mOutOfPaneDragOn = props.mOutOfPaneDragOn; mExactDockPredictionOn = props.mExactDockPredictionOn; mNonDestructFrictionOn = props.mNonDestructFrictionOn; mShow3DPaneBorderOn = props.mShow3DPaneBorderOn; mBarFloatingOn = props.mBarFloatingOn; mRowProportionsOn = props.mRowProportionsOn; mColProportionsOn = props.mColProportionsOn; mBarCollapseIconsOn = props.mBarCollapseIconsOn; mBarDragHintsOn = props.mBarDragHintsOn; mMinCBarDim = props.mMinCBarDim; mResizeHandleSize = props.mResizeHandleSize; return *this; } /***** Implementation for class cbRowInfo *****/ IMPLEMENT_DYNAMIC_CLASS( cbRowInfo, wxObject ) cbRowInfo::cbRowInfo(void) : mNotFixedBarsCnt( false ), mpNext ( NULL ), mpPrev ( NULL ), mpExpandedBar ( NULL ) {} cbRowInfo::~cbRowInfo() { // nothing! all bars are removed using global bar // list in wxFrameLayout class } /***** Implementation for class cbBarInfo *****/ IMPLEMENT_DYNAMIC_CLASS( cbBarInfo, wxObject ) cbBarInfo::cbBarInfo(void) : mpRow( NULL ), mFloatingOn( true ), mpNext( NULL ), mpPrev( NULL ) {} cbBarInfo::~cbBarInfo() { // nothing } /***** Implementation for class cbDockPane *****/ IMPLEMENT_DYNAMIC_CLASS( cbDockPane, wxObject ) // FIXME:: how to eliminate these cut&pasted constructors? cbDockPane::cbDockPane(void) : mLeftMargin ( 1 ), mRightMargin ( 1 ), mTopMargin ( 1 ), mBottomMargin( 1 ), mPaneWidth ( 32768 ), // fake-up very large pane dims, // since the real dimensions of the pane may not // be known, while inserting bars initially mPaneHeight( 32768 ), mAlignment ( -1 ), mpLayout ( 0 ), mpStoredRow( NULL ) {} cbDockPane::cbDockPane( int alignment, wxFrameLayout* pPanel ) : mLeftMargin ( 1 ), mRightMargin ( 1 ), mTopMargin ( 1 ), mBottomMargin( 1 ), mPaneWidth ( 32768 ), // fake-up very large pane dims, // since the real dimensions of the pane may not // be known, while inserting bars initially mPaneHeight( 32768 ), mAlignment ( alignment ), mpLayout ( pPanel ), mpStoredRow( NULL ) {} cbDockPane::~cbDockPane() { size_t i; for ( i = 0; i != mRows.Count(); ++i ) delete mRows[i]; WX_CLEAR_LIST(wxList,mRowShapeData) // NOTE:: control bar infromation structures are cleaned-up // in wxFrameLayout's destructor, using global control-bar list } void cbDockPane::SetMargins( int top, int bottom, int left, int right ) { mTopMargin = top; mBottomMargin = bottom; mLeftMargin = left; mRightMargin = right; } /*** helpers of cbDockPane ***/ void cbDockPane::PaintBarDecorations( cbBarInfo* pBar, wxDC& dc ) { cbDrawBarDecorEvent evt( pBar, dc, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::PaintBarHandles( cbBarInfo* pBar, wxDC& dc ) { cbDrawBarHandlesEvent evt( pBar, dc, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::PaintBar( cbBarInfo* pBar, wxDC& dc ) { PaintBarDecorations( pBar, dc ); PaintBarHandles( pBar, dc ); } void cbDockPane::PaintRowHandles( cbRowInfo* pRow, wxDC& dc ) { cbDrawRowHandlesEvent evt( pRow, dc, this ); mpLayout->FirePluginEvent( evt ); cbDrawRowDecorEvent evt1( pRow, dc, this ); mpLayout->FirePluginEvent( evt1 ); } void cbDockPane::PaintRowBackground ( cbRowInfo* pRow, wxDC& dc ) { cbDrawRowBkGroundEvent evt( pRow, dc, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::PaintRowDecorations( cbRowInfo* pRow, wxDC& dc ) { size_t i; // decorations first for ( i = 0; i != pRow->mBars.Count(); ++i ) PaintBarDecorations( pRow->mBars[i], dc ); // then handles if present for ( i = 0; i != pRow->mBars.Count(); ++i ) PaintBarHandles( pRow->mBars[i], dc ); } void cbDockPane::PaintRow( cbRowInfo* pRow, wxDC& dc ) { PaintRowBackground ( pRow, dc ); PaintRowDecorations( pRow, dc ); PaintRowHandles ( pRow, dc ); } void cbDockPane::PaintPaneBackground( wxDC& dc ) { cbDrawPaneBkGroundEvent evt( dc, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::PaintPaneDecorations( wxDC& dc ) { cbDrawPaneDecorEvent evt( dc, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::PaintPane( wxDC& dc ) { size_t i; PaintPaneBackground( dc ); // first decorations for ( i = 0; i != mRows.Count(); ++i ) { PaintRowBackground( mRows[i], dc ); PaintRowDecorations( mRows[i], dc ); } // than handles for ( i = 0; i != mRows.Count(); ++i ) PaintRowHandles( mRows[i], dc ); // and finally PaintPaneDecorations( dc ); } void cbDockPane::SizeBar( cbBarInfo* pBar ) { cbSizeBarWndEvent evt( pBar, this ); mpLayout->FirePluginEvent( evt ); return; } void cbDockPane::SizeRowObjects( cbRowInfo* pRow ) { size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) SizeBar( pRow->mBars[i] ); } void cbDockPane::SizePaneObjects() { size_t i; for ( i = 0; i != mRows.Count(); ++i ) SizeRowObjects( mRows[i] ); } wxDC* cbDockPane::StartDrawInArea( const wxRect& area ) { wxDC* pDc = 0; cbStartDrawInAreaEvent evt( area, &pDc, this ); mpLayout->FirePluginEvent( evt ); return pDc; } void cbDockPane::FinishDrawInArea( const wxRect& area ) { cbFinishDrawInAreaEvent evt( area, this ); mpLayout->FirePluginEvent( evt ); } bool cbDockPane::IsFixedSize( cbBarInfo* pInfo ) { return ( pInfo->mDimInfo.mIsFixed ); } int cbDockPane::GetNotFixedBarsCount( cbRowInfo* pRow ) { int cnt = 0; size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { if ( !pRow->mBars[i]->IsFixed() ) ++cnt; } return cnt; } void cbDockPane::RemoveBar( cbBarInfo* pBar ) { bool needsRestoring = mProps.mNonDestructFrictionOn && mpStoredRow == pBar->mpRow; cbRemoveBarEvent evt( pBar, this ); mpLayout->FirePluginEvent( evt ); if ( needsRestoring ) { SetRowShapeData( mpStoredRow, &mRowShapeData ); mpStoredRow = NULL; } } void cbDockPane::SyncRowFlags( cbRowInfo* pRow ) { // setup mHasOnlyFixedBars flag for the row information pRow->mHasOnlyFixedBars = true; pRow->mNotFixedBarsCnt = 0; size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { cbBarInfo& bar = *pRow->mBars[i]; bar.mpRow = pRow; if ( !bar.IsFixed() ) { pRow->mHasOnlyFixedBars = false; ++pRow->mNotFixedBarsCnt; } } } void cbDockPane::FrameToPane( int* x, int* y ) { *x -= mLeftMargin; *y -= mTopMargin; if ( mAlignment == FL_ALIGN_TOP || mAlignment == FL_ALIGN_BOTTOM ) { *x -= mBoundsInParent.x; *y -= mBoundsInParent.y; } else { int rx = *x, ry = *y; *x = ry - mBoundsInParent.y; *y = rx - mBoundsInParent.x; } } void cbDockPane::PaneToFrame( int* x, int* y ) { if ( mAlignment == FL_ALIGN_TOP || mAlignment == FL_ALIGN_BOTTOM ) { *x += mBoundsInParent.x; *y += mBoundsInParent.y; } else { int rx = *x, ry = *y; *x = ry + mBoundsInParent.x; *y = mBoundsInParent.y + rx; } *x += mLeftMargin; *y += mTopMargin; } void cbDockPane::FrameToPane( wxRect* pRect ) { wxPoint upperLeft ( pRect->x, pRect->y ); wxPoint lowerRight( pRect->x + pRect->width, pRect->y + pRect->height ); FrameToPane( &upperLeft.x, &upperLeft.y ); FrameToPane( &lowerRight.x, &lowerRight.y ); pRect->x = wxMin(upperLeft.x,lowerRight.x); pRect->y = wxMin(upperLeft.y,lowerRight.y); pRect->width = abs( lowerRight.x - upperLeft.x ); pRect->height = abs( lowerRight.y - upperLeft.y ); } void cbDockPane::PaneToFrame( wxRect* pRect ) { wxPoint upperLeft ( pRect->x, pRect->y ); wxPoint lowerRight( pRect->x + pRect->width, pRect->y + pRect->height ); PaneToFrame( &upperLeft.x, &upperLeft.y ); PaneToFrame( &lowerRight.x, &lowerRight.y ); //wxRect newRect = wxRect( upperLeft, lowerRight ); pRect->x = wxMin(upperLeft.x,lowerRight.x); pRect->y = wxMin(upperLeft.y,lowerRight.y); pRect->width = abs( lowerRight.x - upperLeft.x ); pRect->height = abs( lowerRight.y - upperLeft.y ); } int cbDockPane::GetRowAt( int paneY ) { if ( paneY < 0 ) return -1; int curY = 0; size_t i = 0; for ( ; i != mRows.Count(); ++i ) { int rowHeight = mRows[i]->mRowHeight; int third = rowHeight/3; if ( paneY >= curY && paneY < curY + third ) return i-1; if ( paneY >= curY + third && paneY < curY + rowHeight - third ) return i; curY += rowHeight; } return i; } int cbDockPane::GetRowAt( int upperY, int lowerY ) { /* // OLD STUFF:: int range = lowerY - upperY; int oneThird = range / 3; wxNode* pRow = mRows.GetFirst(); int row = 0; int curY = 0; if ( lowerY <= 0 ) return -1; while( pRow ) { int rowHeight = GetRowHeight( (wxList*)pRow->GetData() ); if ( upperY >= curY && lowerY < curY ) return row; if ( upperY <= curY && lowerY >= curY && curY - upperY >= oneThird ) return row-1; if ( ( upperY < curY + rowHeight && lowerY >= curY + rowHeight && curY + rowHeight - lowerY >= oneThird ) ) return row+1; if ( lowerY <= curY + rowHeight ) return row; ++row; curY += rowHeight; pRow = pRow->GetNext(); } */ int mid = upperY + (lowerY - upperY)/2; if ( mid < 0 ) return -1; int curY = 0; size_t i = 0; for ( ; i != mRows.Count(); ++i ) { int rowHeight = mRows[i]->mRowHeight; if ( mid >= curY && mid < curY + rowHeight ) return i; curY += rowHeight; } return i; } int cbDockPane::GetRowY( cbRowInfo* pRow ) { int curY = 0; size_t i; for ( i = 0; i != mRows.Count(); ++i ) { if ( mRows[i] == pRow ) break; curY += mRows[i]->mRowHeight; } return curY; } bool cbDockPane::HasNotFixedRowsAbove( cbRowInfo* pRow ) { while ( pRow->mpPrev ) { pRow = pRow->mpPrev; if ( pRow->mHasOnlyFixedBars ) return true; } return false; } bool cbDockPane::HasNotFixedRowsBelow( cbRowInfo* pRow ) { while( pRow->mpNext ) { pRow = pRow->mpNext; if ( pRow->mHasOnlyFixedBars ) return true; } return false; } bool cbDockPane::HasNotFixedBarsLeft( cbBarInfo* pBar ) { while( pBar->mpPrev ) { pBar = pBar->mpPrev; if ( pBar->IsFixed() ) return true; } return false; } bool cbDockPane::HasNotFixedBarsRight( cbBarInfo* pBar ) { while( pBar->mpNext ) { pBar = pBar->mpNext; if ( pBar->IsFixed() ) return true; } return false; } void cbDockPane::CalcLengthRatios( cbRowInfo* pInRow ) { size_t i; int totalWidth = 0; // calc current-maximal-total-length of all maximized bars for ( i = 0; i != pInRow->mBars.GetCount(); ++i ) { cbBarInfo& bar = *pInRow->mBars[i]; if ( !bar.IsFixed() ) totalWidth += bar.mBounds.width; } // set up percentages of occupied space for each maximized bar for ( i = 0; i != pInRow->mBars.Count(); ++i ) { cbBarInfo& bar = *pInRow->mBars[i]; if ( !bar.IsFixed() ) bar.mLenRatio = double(bar.mBounds.width)/double(totalWidth); } } void cbDockPane::RecalcRowLayout( cbRowInfo* pRow ) { cbLayoutRowEvent evt( pRow, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::ExpandBar( cbBarInfo* pBar ) { mpLayout->GetUpdatesManager().OnStartChanges(); if ( !pBar->mpRow->mpExpandedBar ) { // save ratios only when there arent any bars expanded yet cbArrayFloat& ratios = pBar->mpRow->mSavedRatios; ratios.Clear(); ratios.Alloc( pBar->mpRow->mNotFixedBarsCnt ); cbBarInfo* pCur = pBar->mpRow->mBars[0]; while( pCur ) { if ( !pCur->IsFixed() ) { ratios.Add( 0.0 ); ratios[ ratios.GetCount() - 1 ] = pCur->mLenRatio; } pCur = pCur->mpNext; } } cbBarInfo* pCur = pBar->mpRow->mBars[0]; while( pCur ) { pCur->mLenRatio = 0.0; // minimize the rest pCur = pCur->mpNext; } pBar->mLenRatio = 1.0; // 100% pBar->mBounds.width = 0; pBar->mpRow->mpExpandedBar = pBar; mpLayout->RecalcLayout( false ); mpLayout->GetUpdatesManager().OnFinishChanges(); mpLayout->GetUpdatesManager().UpdateNow(); } void cbDockPane::ContractBar( cbBarInfo* pBar ) { mpLayout->GetUpdatesManager().OnStartChanges(); // FIXME: What's the purpose of this??? // double ratio = 1.0/ double( pBar->mpRow->mNotFixedBarsCnt ); // restore ratios which were present before expansion cbBarInfo* pCur = pBar->mpRow->mBars[0]; cbArrayFloat& ratios = pBar->mpRow->mSavedRatios; size_t i = 0; while( pCur ) { if ( !pCur->IsFixed() ) { pCur->mLenRatio = ratios[i]; ++i; } pCur = pCur->mpNext; } ratios.Clear(); ratios.Shrink(); pBar->mpRow->mpExpandedBar = NULL; mpLayout->RecalcLayout( false ); mpLayout->GetUpdatesManager().OnFinishChanges(); mpLayout->GetUpdatesManager().UpdateNow(); } void cbDockPane::InitLinksForRow( cbRowInfo* pRow ) { size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { cbBarInfo& bar = *pRow->mBars[i]; if ( i == 0 ) bar.mpPrev = NULL; else bar.mpPrev = pRow->mBars[i-1]; if ( i == pRow->mBars.Count() - 1 ) bar.mpNext = NULL; else bar.mpNext = pRow->mBars[i+1]; } } void cbDockPane::InitLinksForRows() { size_t i; for ( i = 0; i != mRows.Count(); ++i ) { cbRowInfo& row = *mRows[i]; if ( i == 0 ) row.mpPrev = NULL; else row.mpPrev = mRows[i-1]; if ( i == mRows.Count() - 1 ) row.mpNext = NULL; else row.mpNext = mRows[i+1]; } } void cbDockPane::DoInsertBar( cbBarInfo* pBar, int rowNo ) { cbRowInfo* pRow; if ( rowNo == -1 || rowNo >= (int)mRows.Count() ) { pRow = new cbRowInfo(); if ( rowNo == -1 && mRows.Count() ) mRows.Insert( pRow, 0 ); else mRows.Add( pRow ); InitLinksForRows(); } else { pRow = mRows[rowNo]; if ( mProps.mNonDestructFrictionOn == true ) { // store original shape of the row (before the bar is inserted) mpStoredRow = pRow; GetRowShapeData( mpStoredRow, &mRowShapeData ); } } if ( pRow->mBars.Count() ) pRow->mpExpandedBar = NULL; cbInsertBarEvent insEvt( pBar, pRow, this ); mpLayout->FirePluginEvent( insEvt ); mpLayout->GetUpdatesManager().OnRowWillChange( pRow, this ); } void cbDockPane::InsertBar( cbBarInfo* pBarInfo, const wxRect& atRect ) { wxRect rect = atRect; FrameToPane( &rect ); pBarInfo->mBounds.x = rect.x; pBarInfo->mBounds.width = rect.width; pBarInfo->mBounds.height = rect.height; int row = GetRowAt( rect.y, rect.y + rect.height ); DoInsertBar( pBarInfo, row ); } void cbDockPane::InsertBar( cbBarInfo* pBar, cbRowInfo* pIntoRow ) { cbInsertBarEvent insEvt( pBar, pIntoRow, this ); mpLayout->FirePluginEvent( insEvt ); mpLayout->GetUpdatesManager().OnRowWillChange( pIntoRow, this ); } void cbDockPane::InsertBar( cbBarInfo* pBarInfo ) { // set transient properties pBarInfo->mpRow = NULL; pBarInfo->mHasLeftHandle = false; pBarInfo->mHasRightHandle = false; pBarInfo->mLenRatio = 0.0; // set preferred bar dimensions, according to the state in which // the bar is being inserted pBarInfo->mBounds.width = pBarInfo->mDimInfo.mSizes[ pBarInfo->mState ].x; pBarInfo->mBounds.height = pBarInfo->mDimInfo.mSizes[ pBarInfo->mState ].y; DoInsertBar( pBarInfo, pBarInfo->mRowNo ); } void cbDockPane::RemoveRow( cbRowInfo* pRow ) { size_t i; // first, hide all bar-windows in the removed row for ( i = 0; i != pRow->mBars.Count(); ++i ) { if ( pRow->mBars[i]->mpBarWnd ) pRow->mBars[i]->mpBarWnd->Show( false ); } mRows.Remove( pRow ); pRow->mUMgrData.SetDirty(true); } void cbDockPane::InsertRow( cbRowInfo* pRow, cbRowInfo* pBeforeRow ) { if ( !pBeforeRow ) mRows.Add( pRow ); else mRows.Insert( pRow, mRows.Index( pBeforeRow ) ); InitLinksForRows(); pRow->mUMgrData.SetDirty(true); size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) pRow->mBars[i]->mUMgrData.SetDirty( true ); SyncRowFlags( pRow ); } void cbDockPane::SetPaneWidth(int width) { if ( IsHorizontal() ) mPaneWidth = width - mLeftMargin - mRightMargin; else mPaneWidth = width - mTopMargin - mBottomMargin; } void cbDockPane::SetBoundsInParent( const wxRect& rect ) { mBoundsInParent = rect; // set pane dimensions in local coordinates if ( IsHorizontal() ) { mPaneWidth = mBoundsInParent.width - ( mRightMargin + mLeftMargin ); mPaneHeight = mBoundsInParent.height - ( mTopMargin + mBottomMargin ); } else { mPaneWidth = mBoundsInParent.height - ( mTopMargin + mBottomMargin ); mPaneHeight = mBoundsInParent.width - ( mRightMargin + mLeftMargin ); } // convert bounding rectangles of all pane items into parent frame's coordinates wxBarIterator i( mRows ); wxRect noMarginsRect = mBoundsInParent; noMarginsRect.x += mLeftMargin; noMarginsRect.y += mTopMargin; noMarginsRect.width -= ( mLeftMargin + mRightMargin ); noMarginsRect.height -= ( mTopMargin + mBottomMargin ); // hide the whole pane, if it's bounds became reverted (i.e. pane vanished) if ( mBoundsInParent.width < 0 || mBoundsInParent.height < 0 ) hide_rect( mBoundsInParent ); if ( noMarginsRect.width < 0 || noMarginsRect.height < 0 ) hide_rect( noMarginsRect ); // calculate mBoundsInParent for each item in the pane while( i.Next() ) { cbBarInfo& bar = i.BarInfo(); cbRowInfo* pRowInfo = bar.mpRow; // set up row info, if this is first bar in the row if ( pRowInfo && bar.mpPrev == NULL ) { pRowInfo->mBoundsInParent.y = pRowInfo->mRowY; pRowInfo->mBoundsInParent.x = 0; pRowInfo->mBoundsInParent.width = mPaneWidth; pRowInfo->mBoundsInParent.height = pRowInfo->mRowHeight; PaneToFrame( &pRowInfo->mBoundsInParent ); clip_rect_against_rect( pRowInfo->mBoundsInParent, noMarginsRect ); } wxRect bounds = bar.mBounds; // exclude dimensions of handles, when calculating // bar's bounds in parent (i.e. "visual bounds") if ( bar.mHasLeftHandle ) { bounds.x += mProps.mResizeHandleSize; bounds.width -= mProps.mResizeHandleSize; } if ( bar.mHasRightHandle ) bounds.width -= mProps.mResizeHandleSize; PaneToFrame( &bounds ); clip_rect_against_rect( bounds, noMarginsRect ); bar.mBoundsInParent = bounds; } } bool cbDockPane::BarPresent( cbBarInfo* pBar ) { wxBarIterator iter( mRows ); while( iter.Next() ) if ( &iter.BarInfo() == pBar ) return true; return false; } cbRowInfo* cbDockPane::GetRow( int row ) { if ( row >= (int)mRows.Count() ) return NULL; return mRows[ row ]; } int cbDockPane::GetRowIndex( cbRowInfo* pRow ) { size_t i; for ( i = 0; i != mRows.Count(); ++i ) { if ( mRows[i] == pRow ) return i; } wxFAIL_MSG(wxT("Row must be present to call cbDockPane::GetRowIndex()")); return 0; } int cbDockPane::GetPaneHeight() { // first, recalculate row heights and the Y-positions cbLayoutRowsEvent evt( this ); mpLayout->FirePluginEvent( evt ); int height = 0; if ( IsHorizontal() ) height += mTopMargin + mBottomMargin; else height += mLeftMargin + mRightMargin; int count = mRows.Count(); if ( count ) height += mRows[count-1]->mRowY + mRows[count-1]->mRowHeight; return height; } int cbDockPane::GetAlignment() { return mAlignment; } bool cbDockPane::MatchesMask( int paneMask ) { int thisMask = 0; // FIXME:: use array instead of switch() switch (mAlignment) { case FL_ALIGN_TOP : thisMask = FL_ALIGN_TOP_PANE; break; case FL_ALIGN_BOTTOM : thisMask = FL_ALIGN_BOTTOM_PANE;break; case FL_ALIGN_LEFT : thisMask = FL_ALIGN_LEFT_PANE; break; case FL_ALIGN_RIGHT : thisMask = FL_ALIGN_RIGHT_PANE; break; default: wxFAIL_MSG(wxT("Bad FL alignment type detected in cbDockPane::MatchesMask()")); } return ( thisMask & paneMask ) != 0; } void cbDockPane::RecalcLayout() { // first, reposition rows and items vertically cbLayoutRowsEvent evt( this ); mpLayout->FirePluginEvent( evt ); // then horizontally in each row size_t i; for ( i = 0; i != mRows.Count(); ++i ) RecalcRowLayout( mRows[i] ); } int cbDockPane::GetDockingState() { if ( mAlignment == FL_ALIGN_TOP || mAlignment == FL_ALIGN_BOTTOM ) { return wxCBAR_DOCKED_HORIZONTALLY; } else return wxCBAR_DOCKED_VERTICALLY; } inline bool cbDockPane::HasPoint( const wxPoint& pos, int x, int y, int width, int height ) { return ( pos.x >= x && pos.y >= y && pos.x < x + width && pos.y < y + height ); } int cbDockPane::HitTestPaneItems( const wxPoint& pos, cbRowInfo** ppRow, cbBarInfo** ppBar ) { (*ppRow) = NULL; (*ppBar) = NULL; size_t i; for ( i = 0; i != mRows.Count(); ++i ) { cbRowInfo& row = *mRows[i]; *ppRow = &row; // hit-test handles of the row, if present if ( row.mHasUpperHandle ) { if ( HasPoint( pos, 0, row.mRowY, row.mRowWidth, mProps.mResizeHandleSize ) ) return CB_UPPER_ROW_HANDLE_HITTED; } else if ( row.mHasLowerHandle ) { if ( HasPoint( pos, 0, row.mRowY + row.mRowHeight - mProps.mResizeHandleSize, row.mRowWidth, mProps.mResizeHandleSize ) ) return CB_LOWER_ROW_HANDLE_HITTED; } // hit-test bar handles and bar content size_t k; for ( k = 0; k != row.mBars.Count(); ++k ) { cbBarInfo& bar = *row.mBars[k]; wxRect& bounds = bar.mBounds; *ppBar = &bar; if ( bar.mHasLeftHandle ) { if ( HasPoint( pos, bounds.x, bounds.y, mProps.mResizeHandleSize, bounds.height ) ) return CB_LEFT_BAR_HANDLE_HITTED; } else if ( bar.mHasRightHandle ) { if ( HasPoint( pos, bounds.x + bounds.width - mProps.mResizeHandleSize, bounds.y, mProps.mResizeHandleSize, bounds.height ) ) return CB_RIGHT_BAR_HANDLE_HITTED; } if ( HasPoint( pos, bounds.x, bounds.y, bounds.width, bounds.height ) ) return CB_BAR_CONTENT_HITTED; } // hit-test next bar } // next row return CB_NO_ITEMS_HITTED; } void cbDockPane::GetBarResizeRange( cbBarInfo* pBar, int* from, int *till, bool forLeftHandle ) { cbBarInfo* pGivenBar = pBar; int notFree = 0; // calc unavailable space from the left while( pBar->mpPrev ) { pBar = pBar->mpPrev; if ( !pBar->IsFixed() ) notFree += mProps.mMinCBarDim.x; else notFree += pBar->mBounds.width; } *from = notFree; pBar = pGivenBar; notFree = 0; // calc unavailable space from the right while( pBar->mpNext ) { pBar = pBar->mpNext; if ( pBar->mBounds.x >= mPaneWidth ) break; // treat not-fixed bars as minimized if ( !pBar->IsFixed() ) notFree += mProps.mMinCBarDim.x; else { if ( pBar->mBounds.x + pBar->mBounds.width >= mPaneWidth ) { notFree += mPaneWidth - pBar->mBounds.x; break; } else notFree += pBar->mBounds.width; } } *till = mPaneWidth - notFree; // do not let resizing totally deform the bar itself if ( forLeftHandle ) (*till) -= mProps.mMinCBarDim.x; else (*from) += mProps.mMinCBarDim.x; } int cbDockPane::GetMinimalRowHeight( cbRowInfo* pRow ) { int height = mProps.mMinCBarDim.y; size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { if ( pRow->mBars[i]->IsFixed() ) height = wxMax( height, pRow->mBars[i]->mBounds.height ); } if ( pRow->mHasUpperHandle ) height += mProps.mResizeHandleSize; if ( pRow->mHasLowerHandle ) height += mProps.mResizeHandleSize; return height; } void cbDockPane::SetRowHeight( cbRowInfo* pRow, int newHeight ) { if ( pRow->mHasUpperHandle ) newHeight -= mProps.mResizeHandleSize; if ( pRow->mHasLowerHandle ) newHeight -= mProps.mResizeHandleSize; size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { if ( !pRow->mBars[i]->IsFixed() ) pRow->mBars[i]->mBounds.height = newHeight; } } void cbDockPane::GetRowResizeRange( cbRowInfo* pRow, int* from, int* till, bool forUpperHandle ) { cbRowInfo* pGivenRow = pRow; // calc unavailable space from above int notFree = 0; while( pRow->mpPrev ) { pRow = pRow->mpPrev; notFree += GetMinimalRowHeight( pRow ); }; *from = notFree; // allow accupy the client window space by resizing pane rows if ( mAlignment == FL_ALIGN_BOTTOM ) *from -= mpLayout->GetClientHeight(); else if ( mAlignment == FL_ALIGN_RIGHT ) *from -= mpLayout->GetClientWidth(); // calc unavailable space from below pRow = pGivenRow; notFree = 0; while( pRow->mpNext ) { pRow = pRow->mpNext; notFree += GetMinimalRowHeight( pRow ); } *till = mPaneHeight - notFree; // allow adjustinig pane space vs. client window space by resizing pane row heights if ( mAlignment == FL_ALIGN_TOP ) *till += mpLayout->GetClientHeight(); else if ( mAlignment == FL_ALIGN_LEFT ) *till += mpLayout->GetClientWidth(); // do not let the resizing of the row totally squeeze the row itself cbRowInfo& row = *pGivenRow; if ( forUpperHandle ) { *till = row.mRowY + row.mRowHeight - GetMinimalRowHeight( pGivenRow ); if ( row.mHasUpperHandle ) *till -= mProps.mResizeHandleSize; } else { *from += GetMinimalRowHeight( pGivenRow ); if ( row.mHasLowerHandle ) *from -= mProps.mResizeHandleSize; } } void cbDockPane::ResizeRow( cbRowInfo* pRow, int ofs, bool forUpperHandle ) { cbResizeRowEvent evt( pRow, ofs, forUpperHandle, this ); mpLayout->FirePluginEvent( evt ); } void cbDockPane::ResizeBar( cbBarInfo* pBar, int ofs, bool forLeftHandle ) { pBar->mpRow->mpExpandedBar = NULL; mpLayout->GetUpdatesManager().OnStartChanges(); wxRect& bounds = pBar->mBounds; if ( forLeftHandle ) { // do not allow bar width become less then minimal if ( bounds.x + ofs > bounds.x + bounds.width - mProps.mMinCBarDim.x ) { bounds.width = mProps.mMinCBarDim.x; bounds.x += ofs; } else { bounds.x += ofs; bounds.width -= ofs; } } else { // move bar left if necessary if ( bounds.width + ofs < mProps.mMinCBarDim.x ) { bounds.x = bounds.x + bounds.width + ofs - mProps.mMinCBarDim.x; bounds.width = mProps.mMinCBarDim.x; } else // resize right border only bounds.width += ofs; } cbRowInfo* pToRow = pBar->mpRow; this->RemoveBar( pBar ); InsertBar( pBar, pToRow ); mpLayout->RecalcLayout(false); mpLayout->GetUpdatesManager().OnFinishChanges(); mpLayout->GetUpdatesManager().UpdateNow(); } /*** row/bar resizing related methods ***/ void cbDockPane::DrawVertHandle( wxDC& dc, int x, int y, int height ) { int lower = y + height; dc.SetPen( mpLayout->mLightPen ); dc.DrawLine( x,y, x, lower ); dc.SetPen( mpLayout->mGrayPen ); int i; for ( i = 0; i != mProps.mResizeHandleSize-1; ++i ) { ++x; dc.DrawLine( x,y, x, lower ); } dc.SetPen( mpLayout->mDarkPen ); ++x; dc.DrawLine( x,y, x, lower ); dc.SetPen( mpLayout->mBlackPen ); ++x; dc.DrawLine( x,y, x, lower ); } void cbDockPane::DrawHorizHandle( wxDC& dc, int x, int y, int width ) { int right = x + width; dc.SetPen( mpLayout->mLightPen ); dc.DrawLine( x,y, right, y ); dc.SetPen( mpLayout->mGrayPen ); int i; for ( i = 0; i != mProps.mResizeHandleSize-1; ++i ) { ++y; dc.DrawLine( x,y, right, y ); } ++y; dc.SetPen( mpLayout->mDarkPen ); dc.DrawLine( x,y, right, y ); ++y; dc.SetPen( mpLayout->mBlackPen ); dc.DrawLine( x,y, right, y ); } cbBarInfo* cbDockPane::GetBarInfoByWindow( wxWindow* pBarWnd ) { wxBarIterator i( mRows ); while( i.Next() ) if ( i.BarInfo().mpBarWnd == pBarWnd ) return &i.BarInfo(); return NULL; } void cbDockPane::GetRowShapeData( cbRowInfo* pRow, wxList* pLst ) { if(pLst) { WX_CLEAR_LIST(wxList,*pLst); } pLst->Clear(); size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { cbBarInfo& bar = *pRow->mBars[i]; cbBarShapeData* pData = new cbBarShapeData(); pLst->Append( (wxObject*)pData ); pData->mBounds = bar.mBounds; pData->mLenRatio = bar.mLenRatio; } } void cbDockPane::SetRowShapeData( cbRowInfo* pRow, wxList* pLst ) { if ( pLst->GetFirst() == NULL ) return; wxObjectList::compatibility_iterator pData = pLst->GetFirst(); size_t i; for ( i = 0; i != pRow->mBars.Count(); ++i ) { wxASSERT( pData ); // DBG:: cbBarInfo& bar = *pRow->mBars[i];; cbBarShapeData& data = *((cbBarShapeData*)pData->GetData()); bar.mBounds = data.mBounds; bar.mLenRatio = data.mLenRatio; pData = pData->GetNext(); } } /***** Implementation for class cbUpdatesManagerBase *****/ IMPLEMENT_ABSTRACT_CLASS( cbUpdatesManagerBase, wxObject ) /***** Implementation for class cbPluginBase *****/ IMPLEMENT_ABSTRACT_CLASS( cbPluginBase, wxEvtHandler ) cbPluginBase::~cbPluginBase() { // nothing } bool cbPluginBase::ProcessEvent(wxEvent& event) { if ( mPaneMask == wxALL_PANES ) return wxEvtHandler::ProcessEvent( event ); // extract mask info. from received event cbPluginEvent& evt = *( (cbPluginEvent*)&event ); if ( evt.mpPane == 0 && mPaneMask == wxALL_PANES ) return wxEvtHandler::ProcessEvent( event ); int mask = 0; switch ( evt.mpPane->mAlignment ) { case FL_ALIGN_TOP : mask = FL_ALIGN_TOP_PANE; break; case FL_ALIGN_BOTTOM : mask = FL_ALIGN_BOTTOM_PANE;break; case FL_ALIGN_LEFT : mask = FL_ALIGN_LEFT_PANE; break; case FL_ALIGN_RIGHT : mask = FL_ALIGN_RIGHT_PANE; break; } // if event's pane maks matches the plugin's mask if ( mPaneMask & mask ) return wxEvtHandler::ProcessEvent( event ); // otherwise pass to the next handler if present if ( GetNextHandler() && GetNextHandler()->ProcessEvent( event ) ) return true; else return false; }