1/////////////////////////////////////////////////////////////////////////////
2// Name:        gcupdatesmgr.cpp
3// Purpose:     cbGCUpdatesMgr class, optimizing refresh using GarbageCollector
4// Author:      Aleksandras Gluchovas
5// Modified by:
6// Created:     19/10/98
7// RCS-ID:      $Id: gcupdatesmgr.cpp 35650 2005-09-23 12:56:45Z MR $
8// Copyright:   (c) Aleksandras Gluchovas
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#ifndef WX_PRECOMP
20#include "wx/wx.h"
21#endif
22
23#include "wx/fl/gcupdatesmgr.h"
24
25// helper function
26
27static inline bool rect_hits_rect( const wxRect& r1, const wxRect& r2 )
28{
29    if ( ( r2.x >= r1.x && r2.x <= r1.x + r1.width ) ||
30         ( r1.x >= r2.x && r1.x <= r2.x + r2.width ) )
31
32        if ( ( r2.y >= r1.y && r2.y <= r1.y + r1.height ) ||
33             ( r1.y >= r2.y && r1.y <= r2.y + r2.height ) )
34
35            return 1;
36
37    return 0;
38}
39
40// helper structure
41
42struct cbRectInfo
43{
44    cbBarInfo*  mpBar;
45    cbDockPane* mpPane;
46    wxRect*     mpCurBounds;
47    wxRect*     mpPrevBounds;
48};
49
50static inline cbRectInfo& node_to_rect_info( wxNode* pNode )
51{
52    return *( (cbRectInfo*) (pNode->GetData()) );
53}
54
55/***** Implementation for class cbSimpleUpdatesMgr *****/
56
57IMPLEMENT_DYNAMIC_CLASS( cbGCUpdatesMgr, cbSimpleUpdatesMgr )
58
59cbGCUpdatesMgr::cbGCUpdatesMgr( wxFrameLayout* pPanel )
60    : cbSimpleUpdatesMgr( pPanel )
61{}
62
63void cbGCUpdatesMgr::AddItem( wxList&     itemList,
64                              cbBarInfo*  pBar,
65                              cbDockPane* pPane,
66                              wxRect&     curBounds,
67                              wxRect&     prevBounds )
68{
69    cbRectInfo* pInfo = new cbRectInfo();
70
71    pInfo->mpBar     = pBar;
72    pInfo->mpPane       = pPane;
73    pInfo->mpCurBounds  = &curBounds;
74    pInfo->mpPrevBounds = &prevBounds;
75
76    itemList.Append( (wxObject*) pInfo );
77}
78
79void cbGCUpdatesMgr::OnStartChanges()
80{
81    // memorize states of ALL items in the layout -
82    // this is quite excessive, but OK for the decent
83    // implementation of updates manager
84
85    mpLayout->GetPrevClientRect() = mpLayout->GetClientRect();
86
87    cbDockPane** panes = mpLayout->GetPanesArray();
88
89    for( int n = 0; n != MAX_PANES; ++n )
90    {
91        cbDockPane& pane = *(panes[n]);
92
93        // store pane state
94        pane.mUMgrData.StoreItemState( pane.mBoundsInParent );
95        pane.mUMgrData.SetDirty( false );
96
97        cbRowInfo* pRow = pane.GetFirstRow();
98
99        while ( pRow )
100        {
101            cbBarInfo* pBar = pRow->GetFirstBar();
102
103            // store row state
104            pRow->mUMgrData.StoreItemState( pRow->mBoundsInParent );
105            pRow->mUMgrData.SetDirty( false );
106
107            while( pBar )
108            {
109                // store bar state
110                pBar->mUMgrData.StoreItemState( pBar->mBoundsInParent );
111                pBar->mUMgrData.SetDirty( false );
112
113                pBar = pBar->mpNext;
114            }
115
116            pRow = pRow->mpNext;
117        }
118    }
119}
120
121void cbGCUpdatesMgr::UpdateNow()
122{
123    cbDockPane** panes = mpLayout->GetPanesArray();
124
125    wxRect& r1 = mpLayout->GetClientRect();
126    wxRect& r2 = mpLayout->GetPrevClientRect();
127
128    // detect changes in client window's area
129
130    bool clientWindowChanged = ( r1.x      != r2.x     ||
131                                 r1.y      != r2.y     ||
132                                 r1.width  != r2.width ||
133                                 r1.height != r2.height );
134
135    // step #1 - detect changes in each row of each pane,
136    //           and repaint decorations around changed windows
137
138    wxList mBarsToResize;
139
140    int n;
141    for ( n = 0; n != MAX_PANES; ++n )
142    {
143        cbDockPane& pane = *(panes[n]);
144
145        bool paneChanged = WasChanged( pane.mUMgrData, pane.mBoundsInParent );
146
147        if ( paneChanged )
148        {
149            wxClientDC dc( &mpLayout->GetParentFrame() );
150            pane.PaintPaneBackground( dc );
151        }
152
153        wxRect realBounds;
154
155        cbRowInfo* pRow = pane.GetFirstRow();
156
157        while ( pRow )
158        {
159            wxDC* pDc = 0;
160
161            cbBarInfo* pBar = pRow->GetFirstBar();
162
163            bool rowChanged = false;
164//            bool rowBkPainted  = false;
165
166            // FIXME:: the below should not be fixed
167            cbBarInfo* barsToRepaint[128];
168            // number of bars, that were changed in the current row
169            int nBars = 0;
170
171            //wxRect r1 = pRow->mUMgrData.mPrevBounds;
172            //wxRect r2 = pRow->mBoundsInParent;
173
174            if ( WasChanged( pRow->mUMgrData, pRow->mBoundsInParent ) )
175
176                rowChanged = true;
177            else
178                while( pBar )
179                {
180                    if ( WasChanged( pBar->mUMgrData, pBar->mBoundsInParent ) )
181
182                        barsToRepaint[nBars++] = pBar;
183
184                    pBar = pBar->mpNext;
185                }
186
187            if ( nBars || rowChanged )
188            {
189                realBounds = pRow->mBoundsInParent;
190
191                // include 1-pixel thick shades around the row
192                realBounds.x -= 1;
193                realBounds.y -= 1;
194                realBounds.width  += 2;
195                realBounds.height += 2;
196
197                pDc = pane.StartDrawInArea( realBounds );
198            }
199
200            if ( rowChanged )
201            {
202                // postphone the resizement and refreshing the changed
203                // bar windows
204
205                cbBarInfo* pCurBar = pRow->GetFirstBar();
206
207                while ( pCurBar )
208                {
209                    if ( WasChanged( pCurBar->mUMgrData,
210                                     pCurBar->mBoundsInParent ) )
211
212                        AddItem( mBarsToResize, pCurBar, &pane,
213                                 pCurBar->mBoundsInParent,
214                                 pCurBar->mUMgrData.mPrevBounds );
215
216                    pCurBar = pCurBar->mpNext;
217                }
218
219                // draw only their decorations now
220
221                pane.PaintRow( pRow, *pDc );
222            }
223            else
224            if ( nBars != 0 )
225            {
226                for ( int i = 0; i != nBars; ++i )
227
228                    // postphone the resizement and refreshing the changed
229                    // bar windows
230
231                    AddItem( mBarsToResize,
232                             barsToRepaint[i],
233                             &pane,
234                             barsToRepaint[i]->mBoundsInParent,
235                             barsToRepaint[i]->mUMgrData.mPrevBounds );
236
237                // redraw decorations of entire row, regardless of how much
238                // of the bars were changed
239
240                pane.PaintRow( pRow, *pDc );
241            }
242
243            if ( pDc )
244
245                pane.FinishDrawInArea( realBounds );
246
247            pRow = pRow->mpNext;
248
249        } // end of while
250
251        if ( paneChanged )
252        {
253            wxClientDC dc( &mpLayout->GetParentFrame() );
254            pane.PaintPaneDecorations( dc );
255        }
256
257    } // end of for
258
259    if ( clientWindowChanged && !mpLayout->mClientWndRefreshPending )
260    {
261        // ptr to client-window object is "marked" as NULL
262
263        AddItem( mBarsToResize, NULL, NULL,
264                 mpLayout->GetClientRect(),
265                 mpLayout->GetPrevClientRect() );
266    }
267
268    // step #2 - do ordered refreshing and resizing of bar window objects now
269
270    DoRepositionItems( mBarsToResize );
271}
272
273void cbGCUpdatesMgr::DoRepositionItems( wxList& items )
274{
275    wxNode* pNode1 = items.GetFirst();
276
277    while( pNode1 )
278    {
279        cbRectInfo& info = node_to_rect_info( pNode1 );
280
281        wxNode* pNode2 = items.GetFirst();
282
283        // and node itself
284
285        mGC.AddObject( &info );
286
287        while( pNode2 )
288        {
289            if ( pNode2 != pNode1 ) // node should not depend on itself
290            {
291                // Add references to objects on which this object
292                // depends. Dependency here indicates intersection of current
293                // bounds of this object with the initial bounds of the
294                // other object.
295
296                cbRectInfo& otherInfo = node_to_rect_info( pNode2 );
297
298                if ( rect_hits_rect( *info.mpCurBounds, *otherInfo.mpPrevBounds ) )
299
300                                    // the node    depends on node
301                    mGC.AddDependency( &info,      &otherInfo      );
302            }
303
304            pNode2 = pNode2->GetNext();
305        }
306
307        pNode1 = pNode1->GetNext();
308    }
309
310    mGC.ArrangeCollection(); // order nodes according "least-dependency" rule,
311                             // and find out cycled chains
312
313    // Regular item nodes need to be resized, but not repainted (since
314    // they stand in linear (not cyclic) dependency with other
315    // regular nodes).
316
317    wxNode* pNode = mGC.GetRegularObjects().GetFirst();
318
319    while ( pNode )
320    {
321        cbRectInfo& info = *((cbRectInfo*)gc_node_to_obj(pNode));
322
323        if ( info.mpBar == NULL )
324
325            mpLayout->PositionClientWindow();
326        else
327            info.mpPane->SizeBar( info.mpBar );
328
329        pNode = pNode->GetNext();
330    }
331
332    // cycled item nodes, need to be both resized and repainted
333
334    pNode = mGC.GetCycledObjects().GetFirst();
335
336    while ( pNode )
337    {
338        cbRectInfo& info = *((cbRectInfo*)gc_node_to_obj(pNode));
339
340        if ( info.mpBar == NULL )
341        {
342            wxWindow* pClntWnd = mpLayout->GetFrameClient();
343
344            mpLayout->PositionClientWindow();
345
346            // FIXME FIXME:: excessive!
347
348            pClntWnd->Show( false );
349            pClntWnd->Show( true  );
350
351            // OLD STUFF:: mpLayout->PositionClientWindow();
352        }
353        else
354        if ( info.mpBar->mpBarWnd )
355        {
356            wxWindow* pWnd = info.mpBar->mpBarWnd;
357
358            // resize
359            info.mpPane->SizeBar( info.mpBar );
360
361            // repaint
362
363            /* OLD STUFF:: bool isChoice = info.mpBar->IsKindOf( CLASSINFO( wxChoice ) );
364
365            //#ifdef __WINDOWS__
366            //int result = ::SendMessage( (HWND)pWnd->m_hWnd, WM_NCPAINT, 0, 0 );
367            //#endif
368            */
369
370            // FIXME FIXME:: there's no other way to repaint non-client area of the wxWindow!!
371            //                 so we do *excessive* "hide 'n show"
372
373            pWnd->Show(false);
374            pWnd->Show(true);
375
376            pWnd->Refresh();
377        }
378
379        pNode = pNode->GetNext();
380    }
381
382    // release data prepared for GC alg.
383
384    pNode = items.GetFirst();
385
386    while( pNode )
387    {
388        cbRectInfo* pInfo = (cbRectInfo*)(pNode->GetData());
389
390        delete pInfo;
391
392        pNode = pNode->GetNext();
393    }
394
395    mGC.Reset(); // reinit GC
396
397    // FIXME:: this is a dirty-workaround for messy client-area,
398    //         as a result of docking bar out of floated-container window
399
400    if ( mpLayout->mClientWndRefreshPending )
401    {
402        mpLayout->PositionClientWindow();
403        mpLayout->GetFrameClient()->Refresh();
404    }
405}
406
407