1/////////////////////////////////////////////////////////////////////////////
2// Name:        rowlayoutpl.cpp
3// Purpose:     cbRowLayoutPlugin implementation.
4// Author:      Aleksandras Gluchovas
5// Modified by:
6// Created:     09/09/98
7// RCS-ID:      $Id: rowlayoutpl.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/rowlayoutpl.h"
24
25// exerimental "features" are still buggy
26#undef __EXPERIMENTAL
27
28/***** Implementation for class cbRowLayoutPlugin *****/
29
30IMPLEMENT_DYNAMIC_CLASS( cbRowLayoutPlugin, cbPluginBase )
31
32BEGIN_EVENT_TABLE( cbRowLayoutPlugin, cbPluginBase )
33
34    EVT_PL_LAYOUT_ROW ( cbRowLayoutPlugin::OnLayoutRow  )
35    EVT_PL_LAYOUT_ROWS( cbRowLayoutPlugin::OnLayoutRows )
36    EVT_PL_RESIZE_ROW ( cbRowLayoutPlugin::OnResizeRow  )
37
38    EVT_PL_INSERT_BAR ( cbRowLayoutPlugin::OnInsertBar  )
39    EVT_PL_REMOVE_BAR ( cbRowLayoutPlugin::OnRemoveBar  )
40
41END_EVENT_TABLE()
42
43cbRowLayoutPlugin::cbRowLayoutPlugin(void)
44    : mpPane( 0 )
45{}
46
47cbRowLayoutPlugin::cbRowLayoutPlugin( wxFrameLayout* pPanel, int paneMask )
48
49    : cbPluginBase( pPanel, paneMask ),
50      mpPane( 0 )
51{}
52
53void cbRowLayoutPlugin::CheckIfAtTheBoundary( cbBarInfo* pTheBar, cbRowInfo& rowInfo )
54{
55    // this method handles situation, when fixed bar is inserted
56    // into the row, where among fixed bars not-fixed ones are present.
57    // In this case we need to check if the pBarNode appears to be inserted
58    // chain of fixed-bars on the very right or left side of the row,
59    // then all the white-space, such chain should be eliminated,
60    // and the resulting chain justified to the right or the left
61    // side of the row
62
63    if ( !pTheBar->IsFixed() || rowInfo.mHasOnlyFixedBars )
64
65        return;
66
67    cbBarInfo* pBar = rowInfo.mBars[ rowInfo.mBars.Count() - 1 ];
68
69    // slide fixed bars to the right on the right side relative to the pBarNode
70
71    int prevX = mpPane->mPaneWidth;
72
73    do
74    {
75        if ( !pBar->IsFixed() )
76            break;
77
78        wxRect& bounds = pBar->mBounds;
79
80        bounds.x = prevX - bounds.width;
81
82        prevX = bounds.x;
83
84        if ( pBar == pTheBar ) break;
85
86        pBar = pBar->mpPrev;
87    }
88    while( 1 );
89
90    // slide fixed bars to the left on the left side relative to the pBarNode
91
92    pBar = rowInfo.mBars[0];
93
94    prevX = 0;
95
96    do
97    {
98        if ( pBar->IsFixed() )
99
100            break;
101
102        wxRect& bounds = pBar->mBounds;
103
104        bounds.x = prevX;
105
106        prevX = bounds.x + bounds.width;
107
108        if ( pBar == pTheBar ) break;
109
110        pBar = pBar->mpNext;
111    }
112    while( 1 );
113}
114
115void cbRowLayoutPlugin::ExpandNotFixedBars( cbRowInfo* pRow )
116{
117    ApplyLengthRatios( pRow );
118
119    #if 1
120
121   // FIXME:: something's wrong?
122   return;
123
124    #else
125
126    double freeSpc = (double)GetRowFreeSpace( pRow );
127
128    // calculate sum of precents
129
130    double pcntSum = 0.0;
131
132    size_t i;
133    for ( i = 0; i != pRow->mBars.Count(); ++i )
134    {
135        if ( !pRow->mBars[i]->IsFixed() )
136            pcntSum += pRow->mBars[i]->mLenRatio;
137    }
138
139    // setup bar lengths
140
141    int curX = 0;
142
143    for ( i = 0; i != pRow->mBars.Count(); ++i )
144    {
145        cbBarInfo& bar = *pRow->mBars[i];
146
147        if ( !bar.IsFixed() )
148        {
149            bar.mLenRatio = bar.mLenRatio/(pcntSum);
150
151            bar.mBounds.width =
152
153                wxMax( mpPane->mProps.mMinCBarDim.x, int( freeSpc*bar.mLenRatio ) );
154        }
155
156        bar.mBounds.x = curX;
157        curX = bar.mBounds.x + bar.mBounds.width;
158    }
159    #endif
160}
161
162void cbRowLayoutPlugin::AdjustLengthOfInserted( cbRowInfo* WXUNUSED(pRow), cbBarInfo* WXUNUSED(pTheBar) )
163{
164    return;
165
166#if 0
167
168    // TBD: Makes following code unreachable
169
170    // pTheBar is not-fixed
171
172    // FIXME:: what is this for??
173
174#if 1
175
176    int totalLen = 0;
177
178    size_t i;
179    for ( i = 0; i != pRow->mBars.Count(); ++i )
180    {
181        if ( !pRow->mBars[i]->IsFixed() )
182            totalLen += pRow->mBars[i]->mBounds.width;
183    }
184
185    double curWidth = pTheBar->mBounds.width;
186
187    if ( pRow->mBars.Count() )
188
189        pTheBar->mBounds.width = int( mpPane->mPaneWidth * (curWidth / double(totalLen)) );
190#else
191
192    double freeSpc = (double)GetRowFreeSpace( pRow );
193
194    double pcntSum = 0.0;
195
196   size_t i;
197    for ( i = 0; i != pRow->mBars.Count(); ++i )
198   {
199        if ( !pRow->mBars[i]->IsFixed() )
200            pcntSum += pRow->mBars[i]->mLenRatio;
201   }
202
203    // if no longer "balanced", assume that `pTheBar' was previously
204    // removed from this row (kind of AI...)
205
206    if ( pcntSum < 0.98 )
207
208        pTheBar->mBounds.width = freeSpc * (1.0 - pcntSum);
209#endif
210
211#endif
212
213}
214
215void cbRowLayoutPlugin::FitBarsToRange( int from, int till,
216                                        cbBarInfo* pTheBar, cbRowInfo* pRow )
217{
218    cbBarInfo* pFromBar;
219    cbBarInfo* pTillBar;
220
221    if ( pTheBar->mBounds.x > from )
222    {
223        // it's range from the left
224        pFromBar = pRow->mBars[0];
225        pTillBar = pTheBar;
226    }
227    else
228    {
229        pFromBar = pTheBar->mpNext;
230        pTillBar = NULL;
231    }
232
233    // calc free space in the range
234
235    cbBarInfo* pBar = pFromBar;
236    int     freeSpc = till-from;
237    double  pcntSum = 0;
238
239    while( pBar != pTillBar )
240    {
241        if ( pBar->IsFixed() )
242            freeSpc -= pBar->mBounds.width;
243        else
244            pcntSum += pBar->mLenRatio;
245
246        pBar = pBar->mpNext;
247    }
248
249    // adjust not-fixed bar sizes in the range
250
251    pBar = pFromBar;
252
253    while ( pBar != pTillBar )
254    {
255        if ( !pBar->IsFixed() )
256      {
257            pBar->mBounds.width =
258                wxMax( mpPane->mProps.mMinCBarDim.x,
259                       (int)( ((double)freeSpc) * (pBar->mLenRatio/pcntSum) )
260                     );
261      }
262        pBar = pBar->mpNext;
263    }
264
265    // layout range, starting from the left-most bar
266
267    pBar      = pFromBar;
268    int prevX = from;
269    bool hasNotFixedBars = false;
270
271    while ( pBar != pTillBar )
272    {
273        wxRect& bounds = pBar->mBounds;
274
275        if ( !pBar->IsFixed() )
276        {
277            hasNotFixedBars = true;
278
279            freeSpc -= bounds.width;
280        }
281
282        bounds.x = prevX;
283
284        prevX = bounds.x + bounds.width;
285
286        pBar = pBar->mpNext;
287    }
288
289    // make width adjustment for the right-most bar in the range, due to
290    // lost precision when seting widths using f.p. length-ratios
291
292    if ( hasNotFixedBars )
293    {
294        if ( pTheBar->mBounds.x > from )
295        {
296            if ( pTillBar->mpPrev )
297            {
298                wxRect& tillBar = pTillBar->mpPrev->mBounds;
299
300                //tillBar.width = bar.mBounds.x - tillBar.x;
301                tillBar.width += freeSpc;
302            }
303        }
304        else
305        {
306            cbBarInfo* pLast = pRow->mBars[ pRow->mBars.Count() - 1 ];
307
308            if ( pLast != pTheBar )
309            {
310                pTheBar->mBounds.width += freeSpc;
311
312                SlideRightSideBars( pTheBar );
313            }
314        }
315    }
316}
317
318void cbRowLayoutPlugin::MinimzeNotFixedBars( cbRowInfo* pRow, cbBarInfo* pBarToPreserve )
319{
320    size_t i;
321    for ( i = 0; i != pRow->mBars.Count(); ++i )
322    {
323        if ( !pRow->mBars[i]->IsFixed() && pRow->mBars[i] != pBarToPreserve )
324            pRow->mBars[i]->mBounds.width = mpPane->mProps.mMinCBarDim.x;
325    }
326}
327
328int cbRowLayoutPlugin::GetRowFreeSpace( cbRowInfo* pRow )
329{
330    int freeSpc = mpPane->mPaneWidth;
331
332    size_t i;
333    for ( i = 0; i != pRow->mBars.Count(); ++i )
334    {
335        // not-fixed bars variable length, thus their
336        // dimensions are ignored
337        if ( pRow->mBars[i]->IsFixed() )
338            freeSpc -= pRow->mBars[i]->mBounds.width;
339    }
340
341    return freeSpc;
342}
343
344void cbRowLayoutPlugin::RecalcLengthRatios( cbRowInfo* pRow )
345{
346    double freeSpc = double( GetRowFreeSpace( pRow ) );
347
348    cbBarInfo* pBar          = pRow->mBars[0];
349    cbBarInfo* pLastNotFixed = NULL;
350
351    double pcntLeft = 1.0; // (100%)
352
353#ifdef __EXPERIMENTAL
354
355    int totalLen = 0;
356
357    size_t i;
358    for ( i = 0; i != pRow->mBars.Count(); ++i )
359    {
360        if ( !pRow->mBars[i]->IsFixed() )
361            totalLen += pRow->mBars[i]->mBounds.width;
362    }
363#endif
364
365    size_t i;
366    for ( i = 0; i != pRow->mBars.Count(); ++i )
367    {
368        cbBarInfo& bar = *pRow->mBars[i];
369
370        if ( !bar.IsFixed() )
371        {
372
373#ifdef __EXPERIMENTAL
374
375            bar.mLenRatio = double(bar.mBounds.width)/double(totalLen);
376#else
377            bar.mLenRatio = double(bar.mBounds.width)/freeSpc;
378#endif
379
380            pcntLeft      -= bar.mLenRatio;
381            pLastNotFixed  = pBar;
382        }
383    }
384
385    // attach remainder (the result of lost precision) to the
386    // last not-fixed bar
387
388#if !defined(__EXPERIMENTAL)
389
390    if ( pLastNotFixed )
391
392        pLastNotFixed->mLenRatio += pcntLeft;
393#endif
394
395}
396
397void cbRowLayoutPlugin::ApplyLengthRatios( cbRowInfo* pRow )
398{
399    size_t i;
400    double pcntSum = 0;
401
402    // FOR NOW:: all-in-one
403
404    for ( i = 0; i != pRow->mBars.Count(); ++i )
405    {
406        if ( !pRow->mBars[i]->IsFixed() )
407            pcntSum += pRow->mBars[i]->mLenRatio;
408    }
409
410    /*
411    pBar = node_to_first_bar_node( pRow );
412
413    while( pBar )
414    {
415        cbBarInfo& bar = node_to_bar( pBar );
416
417        if ( !bar.IsFixed() )
418
419            bar.mLenRatio = pcntSum / bar.mLenRatio;
420
421        pBar = pBar->Next();
422    }
423    */
424
425    int    prevX   = 0;
426    double freeSpc = GetRowFreeSpace( pRow );
427
428    // tricky stuff (improtant!):
429    // when not-fixed bar is removed from the row and there are
430    // still some other not-fixed ones left in that row, then
431    // the sum of mLenRatio's is no longer 1.0 - this is left
432    // intintionally to handle the case when the removed bar
433    // is returned right back to the row - so that it would retain
434    // it's original dimensions in this row (this is kind of AI...)
435    //
436    // The problem is - when it's remvoed, the sum of
437    // mLenRatio's is not in "balance", i.e. is < 1.0,
438    // it's possible to restore balance, but instead of that
439    // we artifically ajdust freeSpc value in a way that it would
440    // look like total of mLetRatio's is 1.0, thus original
441    // len. ratios are _preserved_:
442
443    if (pcntSum == 0.0)
444        pcntSum = 1.0;
445
446    double unit = freeSpc / pcntSum;
447
448    bool haveSquished = false;
449
450    for ( i = 0; i != pRow->mBars.Count(); ++i )
451    {
452        if ( !pRow->mBars[i]->IsFixed() )
453        {
454            cbBarInfo& bar = *pRow->mBars[i];
455
456            if ( int( unit * bar.mLenRatio ) < mpPane->mProps.mMinCBarDim.x )
457            {
458                haveSquished = true;
459
460                bar.mBounds.width = -1; // mark as "squished"
461
462                pcntSum -= bar.mLenRatio;
463
464                freeSpc -= mpPane->mProps.mMinCBarDim.x;
465            }
466        }
467    }  // for
468
469    if ( haveSquished )
470        unit = freeSpc / pcntSum;
471
472    for ( i = 0; i != pRow->mBars.Count(); ++i )
473    {
474        cbBarInfo& bar = *pRow->mBars[i];
475
476        bar.mBounds.x = prevX;
477
478        if ( !bar.IsFixed() )
479        {
480            if ( bar.mBounds.width == -1 )
481
482                bar.mBounds.width = mpPane->mProps.mMinCBarDim.x;
483            else
484                bar.mBounds.width = int( unit * bar.mLenRatio );
485
486            // a little bit of AI:
487            // memorize bar's height and width, when docked in
488            // the current orientation - by making the current
489            // dimensions to be "preffered" ones for this docking state
490
491            if ( !bar.IsFixed() )
492            {
493                bar.mDimInfo.mSizes[ bar.mState ].x = bar.mBounds.width;
494                bar.mDimInfo.mSizes[ bar.mState ].y = bar.mBounds.height;
495            }
496        }
497
498        prevX = bar.mBounds.x + bar.mBounds.width;
499    }
500}
501
502void cbRowLayoutPlugin::DetectBarHandles( cbRowInfo* pRow )
503{
504    // first pass from left to right (detect left-side handles)
505
506    bool foundNotFixed = false;
507
508    size_t i;
509    for ( i = 0; i != pRow->mBars.Count(); ++i )
510    {
511        cbBarInfo& bar = *pRow->mBars[i];
512
513        bar.mHasLeftHandle = false;
514
515        if ( !bar.IsFixed() )
516        {
517            if ( foundNotFixed )
518
519                if ( bar.mpPrev &&
520                     bar.mpPrev->IsFixed() )
521
522                    bar.mHasLeftHandle = true;
523
524            foundNotFixed = true;
525        }
526    }
527
528    // pass from right to left (detect right-side handles)
529
530    foundNotFixed = false;
531
532    cbBarInfo* pBar = pRow->mBars[ pRow->mBars.Count() - 1 ];
533
534    while( pBar )
535    {
536        pBar->mHasRightHandle = false;
537
538        if ( !pBar->IsFixed() )
539        {
540            if ( foundNotFixed )
541
542                if ( pBar->mpNext )
543
544                     pBar->mHasRightHandle = true;
545
546            foundNotFixed = true;
547        }
548
549        pBar = pBar->mpPrev;
550    }
551}
552
553void cbRowLayoutPlugin::RelayoutNotFixedBarsAround( cbBarInfo* pTheBar, cbRowInfo* pRow )
554{
555    if ( !pTheBar->mpPrev )
556    {
557        if (  !pTheBar->IsFixed() )
558        {
559            // this bar the first in the row, move it's
560            // left edge to the very left
561            pTheBar->mBounds.width += pTheBar->mBounds.x;
562            pTheBar->mBounds.x      = 0;
563        }
564    }
565    else
566        FitBarsToRange( 0, pTheBar->mBounds.x, pTheBar, pRow );
567
568    if ( !pTheBar->mpNext )
569    {
570        if ( !pTheBar->IsFixed() )
571        {
572            // this bar is the last one, move it's
573            // right edge to the very right
574
575            pTheBar->mBounds.width = mpPane->mPaneWidth - pTheBar->mBounds.x;
576        }
577    }
578    else
579        FitBarsToRange( pTheBar->mBounds.x + pTheBar->mBounds.width, mpPane->mPaneWidth,
580                        pTheBar, pRow
581                      );
582}
583
584void cbRowLayoutPlugin::LayoutItemsVertically( cbRowInfo& row )
585{
586    size_t i;
587    for ( i = 0; i != row.mBars.Count(); ++i )
588    {
589        cbBarInfo& bar = *row.mBars[i];
590
591        bar.mBounds.y = row.mRowY;
592
593        if ( !bar.IsFixed() )
594
595            // make all not-fixed bars of equal height
596            bar.mBounds.height = row.mRowHeight;
597
598        if ( row.mHasUpperHandle )
599
600            bar.mBounds.y += mpPane->mProps.mResizeHandleSize;
601    }
602}
603
604int cbRowLayoutPlugin::CalcRowHeight( cbRowInfo& row )
605{
606    int maxHeight = 0;
607
608    size_t i;
609    for ( i = 0; i != row.mBars.Count(); ++i )
610
611        maxHeight = wxMax( maxHeight, row.mBars[i]->mBounds.height );
612
613    return maxHeight;
614}
615
616void cbRowLayoutPlugin::StickRightSideBars( cbBarInfo* pToBar )
617{
618    cbBarInfo* pBar  = pToBar->mpNext;
619    cbBarInfo* pPrev = pToBar;
620
621    while( pBar )
622    {
623        wxRect& cur  = pBar->mBounds;
624        wxRect& prev = pPrev->mBounds;
625
626        cur.x = prev.x + prev.width;
627
628        pPrev = pBar;
629        pBar  = pBar->mpNext;
630    }
631}
632
633void cbRowLayoutPlugin::SlideLeftSideBars( cbBarInfo* pTheBar )
634{
635    // shift left-side-bars to the left (with respect to "theBar"),
636    // so that they would not obscured by each other
637
638    cbBarInfo* pBar  = pTheBar->mpPrev;
639    cbBarInfo* pPrev = pTheBar;
640
641    while( pBar )
642    {
643        wxRect& cur  = pBar->mBounds;
644        wxRect& prev = pPrev->mBounds;
645
646        if ( cur.x + cur.width > prev.x )
647
648            cur.x = prev.x - cur.width;
649
650        pPrev = pBar;
651        pBar  = pBar->mpPrev;
652    }
653}
654
655void cbRowLayoutPlugin::SlideRightSideBars( cbBarInfo* pTheBar )
656{
657    // shift right-side-bars to the right (with respect to "theBar"),
658    // so that they would not be obscured by each other
659
660    cbBarInfo* pBar  = pTheBar->mpNext;
661    cbBarInfo* pPrev = pTheBar;
662
663    while( pBar )
664    {
665        wxRect& cur  = pBar->mBounds;
666        wxRect& prev = pPrev->mBounds;
667
668        if ( cur.x < prev.x + prev.width )
669
670            cur.x = prev.x + prev.width;
671
672        pPrev = pBar;
673        pBar  = pBar->mpNext;
674    }
675}
676
677void cbRowLayoutPlugin::ShiftLeftTrashold( cbBarInfo* WXUNUSED(pTheBar), cbRowInfo& row )
678{
679    wxRect& first = row.mBars[0]->mBounds;
680
681    if ( first.x < 0 )
682    {
683        row.mBars[0]->mBounds.x = 0;
684
685        SlideRightSideBars( row.mBars[0] );
686    }
687}
688
689void cbRowLayoutPlugin::ShiftRightTrashold( cbBarInfo* pTheBar, cbRowInfo& row )
690{
691    wxRect& theBar = pTheBar->mBounds;
692
693    do
694    {
695        cbBarInfo* pBar = pTheBar;
696
697        // calculate free spece on the left side
698
699        int leftFreeSpc = 0;
700
701        while( pBar )
702        {
703            wxRect& cur = pBar->mBounds;
704
705            if ( pBar->mpPrev )
706            {
707                wxRect& prev = pBar->mpPrev->mBounds;
708
709                leftFreeSpc += cur.x - prev.x - prev.width;
710            }
711            else
712                leftFreeSpc += cur.x;
713
714            if ( cur.x < 0 )
715            {
716                leftFreeSpc = 0;
717                break;
718            }
719
720            pBar = pBar->mpPrev;
721        }
722
723        pBar = pTheBar;
724
725        int rightOverflow = 0;
726
727        if ( pTheBar->IsFixed() )
728
729            while( pBar )
730            {
731                if ( !pBar->mpNext )
732                {
733                    wxRect& cur = pBar->mBounds;
734
735                    if ( cur.x + cur.width > mpPane->mPaneWidth )
736
737                        rightOverflow = cur.x + cur.width - mpPane->mPaneWidth;
738                }
739
740                pBar = pBar->mpNext;
741            }
742
743        if ( rightOverflow > 0 )
744        {
745            if ( leftFreeSpc <= 0 ) return;
746
747            if ( pTheBar->mpNext )
748            {
749                wxRect& next = pTheBar->mpNext->mBounds;
750
751                // if there's enough space on the left, move over one half-obscured
752                // bar from the right to the left side with respect to "theBar"
753
754                if ( next.width < leftFreeSpc )
755                {
756                    cbBarInfo* pNext = pTheBar->mpNext;
757
758                    row.mBars.Remove( pNext );
759
760                    row.mBars.Insert( pNext, row.mBars.Index( pTheBar ) );
761
762                    next.x = theBar.x - next.width;
763
764                    // re-setup mpPrev/mpNext references after insertion
765
766                    mpPane->InitLinksForRow( &row );
767
768                    // tighten things
769
770                    StickRightSideBars( pTheBar );
771                    SlideLeftSideBars ( pTheBar );
772
773                    continue;
774                }
775            }
776
777            int leftShift = ( rightOverflow > leftFreeSpc )
778                            ? leftFreeSpc
779                            : rightOverflow;
780
781            theBar.x -= leftShift;
782
783            StickRightSideBars( pTheBar );
784            SlideLeftSideBars ( pTheBar );
785
786            break;
787
788        } // end of if ( rightOverflow )
789        else
790            break;
791
792    } while(1);
793}
794
795void cbRowLayoutPlugin::InsertBefore( cbBarInfo* pBeforeBar,
796                                      cbBarInfo* pTheBar,
797                                      cbRowInfo& row        )
798{
799    if ( pBeforeBar )
800
801        row.mBars.Insert( pTheBar, row.mBars.Index( pBeforeBar ) );
802    else
803        row.mBars.Add( pTheBar );
804
805    pTheBar->mpRow = &row;
806}
807
808void cbRowLayoutPlugin::DoInsertBar( cbBarInfo* pTheBar, cbRowInfo& row )
809{
810    wxRect& theBar = pTheBar->mBounds;
811
812    /* OLD STUFF::
813    if ( theBar.x < 0 && !node_to_bar( pTheBar ).IsFixed() )
814    {
815        // AI::
816        theBar.width += theBar.x;
817        theBar.x = 0;
818    } */
819
820    size_t i;
821    for ( i = 0; i != row.mBars.Count(); ++i )
822    {
823        cbBarInfo& bar = *row.mBars[i];
824
825        wxRect& cur = bar.mBounds;
826
827        // if bar hits the left edge
828        if ( theBar.x <= cur.x )
829        {
830            InsertBefore( &bar, pTheBar, row );
831            return;
832        }
833
834        else
835        // if bar hits the right edge
836        if ( theBar.x <= cur.x + cur.width )
837        {
838            if ( theBar.x + theBar.width > cur.x + cur.width )
839            {
840                InsertBefore( bar.mpNext, pTheBar, row );
841                return;
842            }
843
844            // otherwise the bar lies within the bounds of current bar
845
846            int leftDist  = theBar.x - cur.x;
847            int rightDist = cur.x + cur.width - (theBar.x + theBar.width);
848
849            if ( leftDist < rightDist )
850
851                InsertBefore( &bar, pTheBar, row );
852            else
853                InsertBefore( bar.mpNext, pTheBar, row );
854
855            return;
856        }
857    }
858
859    InsertBefore( NULL, pTheBar, row ); // insert at the end
860}
861
862// evnet handlers
863
864void cbRowLayoutPlugin::OnInsertBar( cbInsertBarEvent& event )
865{
866    cbBarInfo* pBarToInsert = event.mpBar;
867    cbRowInfo* pIntoRow     = event.mpRow;
868    mpPane                  = event.mpPane;
869
870    if ( !pBarToInsert->IsFixed() )
871
872        AdjustLengthOfInserted( pIntoRow, pBarToInsert );
873
874    DoInsertBar( pBarToInsert, *pIntoRow );
875
876    mpPane->InitLinksForRow( pIntoRow ); // relink "mpNext/mpPrev"s
877
878    // perform relayouting of the bars after insertion
879
880    // init bar location info
881    pBarToInsert->mAlignment = event.mpPane->mAlignment;
882    pBarToInsert->mRowNo     = event.mpPane->GetRowIndex( pIntoRow );
883
884#ifdef __EXPERIMENTAL
885
886    if ( !pIntoRow->mHasOnlyFixedBars || !pBarToInsert->IsFixed() )
887
888        RecalcLengthRatios( pIntoRow );
889
890#endif
891
892    MinimzeNotFixedBars( pIntoRow, pBarToInsert );
893
894    SlideLeftSideBars ( pBarToInsert );
895    SlideRightSideBars( pBarToInsert );
896
897    ShiftLeftTrashold ( pBarToInsert, *pIntoRow );
898    ShiftRightTrashold( pBarToInsert, *pIntoRow );
899
900    mpPane->SyncRowFlags( pIntoRow );
901
902    CheckIfAtTheBoundary( pBarToInsert, *pIntoRow );
903
904    if ( event.mpPane->IsHorizontal() )
905
906        pBarToInsert->mState = wxCBAR_DOCKED_HORIZONTALLY;
907    else
908        pBarToInsert->mState = wxCBAR_DOCKED_VERTICALLY;
909
910    if ( !pIntoRow->mHasOnlyFixedBars )
911    {
912
913#ifdef __EXPERIMENTAL
914
915        ExpandNotFixedBars( pIntoRow );
916#else
917
918        RelayoutNotFixedBarsAround( pBarToInsert, pIntoRow );
919        RecalcLengthRatios( pIntoRow );
920
921#endif
922
923        DetectBarHandles( pIntoRow );
924
925        // do proportional resizing of not-fixed bars
926        ApplyLengthRatios( pIntoRow );
927    }
928
929    // adjust the bar's docking state
930
931    // a little bit of AI:
932    // memorize bar's height and width, when docked in
933    // the current orientation - by making the current
934    // dimensions to be "preferred" ones for this docking state
935
936    if ( !pBarToInsert->IsFixed() )
937    {
938        cbBarInfo& bar = *pBarToInsert;
939
940        bar.mDimInfo.mSizes[ bar.mState ].x = bar.mBounds.width;
941        bar.mDimInfo.mSizes[ bar.mState ].y = bar.mBounds.height;
942    }
943}
944
945void cbRowLayoutPlugin::OnRemoveBar ( cbRemoveBarEvent& event )
946{
947    cbBarInfo* pBar = event.mpBar;
948    mpPane          = event.mpPane;
949
950    cbRowInfo* pRow = pBar->mpRow;
951
952    mpLayout->GetUpdatesManager().OnBarWillChange( pBar, pRow, event.mpPane );
953
954    // invalidate the whole row
955    //pFirst->mpRowInfo->mMgrData.mPrevBounds.x = -1;
956
957    pRow->mBars.Remove( pBar );
958
959    // rest bar information after removing it from the row
960    pBar->mpRow           = NULL;
961    pBar->mHasLeftHandle  = false;
962    pBar->mHasRightHandle = false;
963
964    mpPane->InitLinksForRow( pRow ); // relink "mpNext/mpPrev"s
965
966    if ( pRow->mBars.Count() == 0 )
967    {
968        // empty rows should not exist
969
970        event.mpPane->GetRowList().Remove( pRow );
971
972        delete pRow;
973
974        mpPane->InitLinksForRows();
975    }
976    else
977    {
978        // force repainting of bars, in the row, from which the bar was removed
979
980        // FIXME:: really needed?
981        pRow->mBars[0]->mUMgrData.SetDirty(true);
982
983        // re-setup mHasOnlyFixedBars flag for the row information
984        event.mpPane->SyncRowFlags( pRow );
985
986        DetectBarHandles( pRow );
987
988        if ( !pRow->mHasOnlyFixedBars )
989
990            ExpandNotFixedBars( pRow );
991    }
992}
993
994void cbRowLayoutPlugin::OnLayoutRow( cbLayoutRowEvent& event )
995{
996    cbRowInfo* pRow = event.mpRow;
997    mpPane          = event.mpPane;
998
999    MinimzeNotFixedBars( pRow, NULL );
1000
1001    if ( !pRow->mHasOnlyFixedBars )
1002    {
1003        // do proportional resizing of not-fixed bars
1004        ApplyLengthRatios( pRow );
1005    }
1006
1007    cbBarInfo& lastBar  = *pRow->mBars[ pRow->mBars.Count() - 1 ];
1008    cbBarInfo& firstBar = *pRow->mBars[ 0 ];
1009
1010    // FIXME:: Next line not used
1011    // wxRect& bounds = lastBar.mBounds;
1012
1013    if ( lastBar.mBounds.x + lastBar.mBounds.width > mpPane->mPaneWidth )
1014    {
1015        lastBar.mBounds.x = mpPane->mPaneWidth - lastBar.mBounds.width;
1016
1017        // first simulate left-row-edge friction
1018
1019        SlideLeftSideBars( &lastBar );
1020
1021        if ( firstBar.mBounds.x < 0 )
1022            firstBar.mBounds.x = 0;
1023
1024        // then left-row-edge function, though this
1025        // may cause some of the right-side bars going
1026        // out of row bounds, but left-side always
1027        // has the highest "priority"
1028
1029        SlideRightSideBars( &firstBar );
1030    }
1031
1032    event.Skip(); // pass event to the next handler
1033}
1034
1035void cbRowLayoutPlugin::OnLayoutRows( cbLayoutRowsEvent& event )
1036{
1037    mpPane       = event.mpPane;
1038
1039    int curY = 0;
1040
1041    // FIXME:: Next line not used.
1042    // RowArrayT& arr = mpPane->GetRowList();
1043
1044    size_t i;
1045    for ( i = 0; i != mpPane->GetRowList().Count(); ++i )
1046    {
1047        cbRowInfo& row = *mpPane->GetRowList()[ i ];
1048        //mpPane->CalcLengthRatios(& row);
1049
1050        // setup "has-handle" flags for rows, which depend on the existence
1051        // of not-fixed bars in the row
1052
1053        if ( !row.mHasOnlyFixedBars )
1054        {
1055            if ( mpPane->mAlignment == FL_ALIGN_TOP ||
1056                 mpPane->mAlignment == FL_ALIGN_LEFT   )
1057            {
1058                row.mHasLowerHandle = true;
1059
1060                row.mHasUpperHandle = false;
1061            }
1062            else
1063            {
1064                row.mHasUpperHandle = true;
1065
1066                row.mHasLowerHandle = false;
1067            }
1068        }
1069        else
1070        {
1071            // otherwise, rows with fixed-bars only, have no height-resizing handles
1072            row.mHasUpperHandle = false;
1073            row.mHasLowerHandle = false;
1074        }
1075
1076        // setup vertical positions for items in the row
1077
1078        row.mRowY = curY;
1079
1080        row.mRowWidth  = mpPane->mPaneWidth;
1081        row.mRowHeight = CalcRowHeight( row );
1082
1083        LayoutItemsVertically( row );
1084
1085        if ( row.mHasUpperHandle )
1086            row.mRowHeight += mpPane->mProps.mResizeHandleSize;
1087        if ( row.mHasLowerHandle )
1088            row.mRowHeight += mpPane->mProps.mResizeHandleSize;
1089
1090        curY += row.mRowHeight;
1091    }
1092
1093    event.Skip(); // pass event to the next handler - other hookeds plugin
1094                  // may also add some "refinements" to the layout now
1095}
1096
1097void cbRowLayoutPlugin::OnResizeRow( cbResizeRowEvent& event )
1098{
1099    // extract resize-event info
1100    int     ofs            = event.mHandleOfs;
1101    bool    forUpperHandle = event.mForUpperHandle;
1102    cbRowInfo* pTheRow     = event.mpRow;
1103            mpPane         = event.mpPane;
1104
1105    // FIXME:: Next line not used.
1106    //int     newHeight      = pTheRow->mRowHeight;
1107
1108    if ( forUpperHandle )
1109    {
1110        // calculate available free space from above,
1111        // which can be obtained by squeezing not-fixed height rows
1112
1113        cbRowInfo* pRow = pTheRow->mpPrev;
1114
1115        while( pRow )
1116        {
1117            pRow = pRow->mpPrev;
1118        }
1119    }
1120    else
1121    {
1122        // calculate available free space from below,
1123        // which can be obtained by squeezing not-fixed height rows
1124
1125        cbRowInfo* pRow = pTheRow->mpNext;
1126
1127        while( pRow )
1128        {
1129            pRow = pRow->mpNext;
1130        }
1131    }
1132
1133    mpLayout->GetUpdatesManager().OnStartChanges();
1134
1135    int clientSize;
1136
1137    // allow user adjusting pane vs. client-area space, for upper-handle
1138
1139    if ( mpPane->IsHorizontal() )
1140
1141        clientSize = mpLayout->GetClientHeight();
1142    else
1143        clientSize = mpLayout->GetClientWidth();
1144
1145    if ( forUpperHandle && ofs < -clientSize )
1146    {
1147        int needed = -(ofs + clientSize);
1148
1149        cbRowInfo* pRow = mpPane->GetRowList()[ 0 ];
1150
1151        // start squeezing rows from the top row towards bottom
1152
1153        while( pRow != pTheRow && needed )
1154        {
1155            // only not-fixed rows can be squeezed
1156
1157            if ( !pRow->mHasOnlyFixedBars )
1158            {
1159                int prevHeight = pRow->mRowHeight;
1160
1161                int newHeight  = wxMax( event.mpPane->GetMinimalRowHeight( pRow ),
1162                                        prevHeight - needed );
1163
1164                if ( newHeight != prevHeight )
1165                {
1166                    event.mpPane->SetRowHeight( pRow, newHeight );
1167
1168                    needed -= prevHeight - pRow->mRowHeight;
1169                }
1170            }
1171
1172            pRow = pRow->mpNext;
1173        }
1174    }
1175
1176    // allow user adjusting pane vs. client-area space, for lower-handle
1177
1178    if ( !forUpperHandle && ofs > clientSize )
1179    {
1180        int needed = ofs - clientSize;
1181
1182        cbRowInfo* pRow = mpPane->GetRowList()[ mpPane->GetRowList().Count() - 1 ];
1183
1184        // start squeezing rows from the bottom towards the top row
1185
1186        while( pRow && needed )
1187        {
1188            // only not-fixed rows can be squeezed
1189
1190            if ( !pRow->mHasOnlyFixedBars )
1191            {
1192                int prevHeight = pRow->mRowHeight;
1193
1194                int newHeight  = wxMax( event.mpPane->GetMinimalRowHeight( pRow ),
1195                                        prevHeight - needed );
1196
1197                if ( newHeight != prevHeight )
1198                {
1199                    event.mpPane->SetRowHeight( pRow, newHeight );
1200
1201                    needed -= prevHeight - pRow->mRowHeight;
1202                }
1203            }
1204
1205            pRow = pRow->mpPrev;
1206        }
1207    }
1208
1209    if ( forUpperHandle )
1210
1211        event.mpPane->SetRowHeight( pTheRow, pTheRow->mRowHeight + (-ofs) );
1212    else
1213        event.mpPane->SetRowHeight( pTheRow, pTheRow->mRowHeight +   ofs  );
1214
1215    mpLayout->RecalcLayout(false);
1216
1217    mpLayout->GetUpdatesManager().OnFinishChanges();
1218    mpLayout->GetUpdatesManager().UpdateNow();
1219}
1220
1221