1/////////////////////////////////////////////////////////////////////////////
2// Name:        barhintspl.h
3// Purpose:     Implementation for cbBarHintsPlugin
4// Author:      Aleksandras Gluchovas
5// Modified by:
6// Created:     30/11/98 (my 22th birthday :-)
7// RCS-ID:      $Id: barhintspl.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/utils.h"
24#include "wx/fl/barhintspl.h"
25
26// fixed settings
27
28#define GROOVE_WIDTH         3  // left shade + middle line + right shade
29#define GROOVE_TO_GROOVE_GAP 1
30#define BOX_T_BOX_GAP        2
31#define BOX_TO_GROOVE_GAP    3
32
33#define BOXES_IN_HINT        2
34#define CLOSE_BOX_IDX        0
35#define COLLAPSE_BOX_IDX     1
36
37// used interally
38
39#define CLOSE_BOX_HITTED    1
40#define COLLAPSE_BOX_HITTED 2
41
42/***** Implementation fro class cbBarHintsPlugin *****/
43
44IMPLEMENT_DYNAMIC_CLASS( cbBarHintsPlugin, cbPluginBase )
45
46BEGIN_EVENT_TABLE( cbBarHintsPlugin, cbPluginBase )
47
48    EVT_PL_SIZE_BAR_WND  ( cbBarHintsPlugin::OnSizeBarWindow )
49    EVT_PL_DRAW_BAR_DECOR( cbBarHintsPlugin::OnDrawBarDecorations )
50
51    EVT_PL_LEFT_DOWN( cbBarHintsPlugin::OnLeftDown )
52    EVT_PL_LEFT_UP  ( cbBarHintsPlugin::OnLeftUp   )
53    EVT_PL_MOTION   ( cbBarHintsPlugin::OnMotion   )
54
55END_EVENT_TABLE()
56
57cbBarHintsPlugin::cbBarHintsPlugin(void)
58
59    : mpPane( 0 ),
60      mBtnPressed   ( false ),
61      mCloseBoxOn   ( true ),
62      mCollapseBoxOn( true ),
63      mGrooveCount  ( 2 ),
64      mHintGap      ( 4 ),
65      mXWeight      ( 2 )
66{
67    mBoxes[CLOSE_BOX_IDX]    = NULL;
68    mBoxes[COLLAPSE_BOX_IDX] = NULL;
69}
70
71cbBarHintsPlugin::cbBarHintsPlugin( wxFrameLayout* pLayout, int paneMask )
72
73    : cbPluginBase( pLayout, paneMask ),
74      mpPane( 0 ),
75      mBtnPressed   ( false ),
76      mCloseBoxOn   ( true ),
77      mCollapseBoxOn( true ),
78      mGrooveCount  ( 2 ),
79      mHintGap      ( 5 ),
80      mXWeight      ( 2 )
81{
82    mBoxes[CLOSE_BOX_IDX]    = NULL;
83    mBoxes[COLLAPSE_BOX_IDX] = NULL;
84}
85
86
87cbBarHintsPlugin::~cbBarHintsPlugin()
88{
89    if (mBoxes[CLOSE_BOX_IDX])
90        delete mBoxes[CLOSE_BOX_IDX];
91
92    if (mBoxes[COLLAPSE_BOX_IDX])
93        delete mBoxes[COLLAPSE_BOX_IDX];
94}  // cbBarHintsPlugin destructor
95
96
97void cbBarHintsPlugin::SetGrooveCount( int nGrooves )
98{
99    mGrooveCount = nGrooves;
100}
101
102void cbBarHintsPlugin::CreateBoxes()
103{
104    cbCloseBox*    box1 = new cbCloseBox();
105    cbCollapseBox* box2 = new cbCollapseBox();
106
107    mBoxes[CLOSE_BOX_IDX]    = box1;
108    mBoxes[COLLAPSE_BOX_IDX] = box2;
109
110    int i;
111    for ( i = 0; i != BOXES_IN_HINT; ++i )
112    {
113        mBoxes[i]->mpLayout = mpLayout;
114        mBoxes[i]->mpPlugin = this;
115        mBoxes[i]->mpWnd    = NULL;
116    }
117}
118
119
120void cbBarHintsPlugin::Draw3DBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos), bool WXUNUSED(pressed) )
121{
122}
123
124void cbBarHintsPlugin::DrawCloseBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos), bool WXUNUSED(pressed) )
125{
126}
127
128void cbBarHintsPlugin::DrawCollapseBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos),
129                                        bool WXUNUSED(atLeft), bool WXUNUSED(disabled), bool WXUNUSED(pressed) )
130{
131}
132
133void cbBarHintsPlugin::DrawGrooves( wxDC& dc, const wxPoint& pos, int length )
134{
135    int ofs = 0;
136
137    int i;
138    for ( i = 0; i != mGrooveCount; ++i, ofs += ( GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP ) )
139    {
140        if ( mpPane->IsHorizontal() )
141        {
142            dc.SetPen( mpLayout->mLightPen );
143            dc.DrawLine( pos.x + ofs, pos.y, pos.x + ofs, pos.y + length - 1 );
144            dc.DrawPoint( pos.x + ofs + 1, pos.y );
145
146            dc.SetPen( mpLayout->mDarkPen );
147            dc.DrawLine( pos.x + ofs + 2, pos.y, pos.x + ofs + 2, pos.y + length );
148            dc.DrawPoint( pos.x + ofs + 1, pos.y + length - 1 );
149            dc.DrawPoint( pos.x + ofs,     pos.y + length - 1 );
150        }
151        else
152        {
153            dc.SetPen( mpLayout->mLightPen );
154            dc.DrawLine( pos.x, pos.y + ofs, pos.x + length - 1, pos.y + ofs );
155            dc.DrawPoint( pos.x, pos.y + ofs + 1 );
156
157            dc.SetPen( mpLayout->mDarkPen );
158            dc.DrawLine( pos.x, pos.y + ofs + 2, pos.x + length, pos.y + ofs + 2 );
159            dc.DrawPoint( pos.x + length - 1, pos.y + ofs + 1 );
160            dc.DrawPoint( pos.x + length - 1, pos.y + ofs );
161        }
162    }
163}
164
165void cbBarHintsPlugin::ExcludeHints( wxRect& rect, cbBarInfo& info )
166{
167    int boxHeight = BTN_BOX_HEIGHT;
168
169    // collapse and close box are not placed on fixed bars
170
171    if ( info.IsFixed() || ( !mCloseBoxOn && !mCollapseBoxOn ) )
172
173        boxHeight = 0;
174
175    int height = wxMax( mGrooveCount*(GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP)
176                        - GROOVE_TO_GROOVE_GAP,
177                        boxHeight
178                      );
179
180    if ( mpPane->IsHorizontal() )
181    {
182        rect.x     += ( mHintGap*2 + height );
183        rect.width -= (height + 2*mHintGap);
184
185        rect.x     -= info.mDimInfo.mHorizGap + 2;
186        rect.width += info.mDimInfo.mHorizGap + 2;
187    }
188    else
189    {
190        rect.y      += (mHintGap*2 + height);
191        rect.height -= (height + 2*mHintGap);
192
193        rect.y      -= info.mDimInfo.mVertGap + 2;
194        rect.height += info.mDimInfo.mVertGap + 2;
195    }
196}
197
198void cbBarHintsPlugin::DoDrawHint( wxDC& dc, wxRect& rect,
199                                   int pos, int WXUNUSED(boxOfs), int grooveOfs,
200                                   bool isFixed )
201{
202    if ( !isFixed )
203    {
204        if ( mpPane->IsHorizontal() )
205        {
206            if ( mCloseBoxOn )
207
208                mBoxes[CLOSE_BOX_IDX]->Draw( dc );
209
210            if ( mCollapseBoxOn )
211
212                mBoxes[COLLAPSE_BOX_IDX]->Draw( dc );
213        }
214        else
215        {
216            if ( mCloseBoxOn )
217
218                mBoxes[CLOSE_BOX_IDX]->Draw( dc );
219
220            if ( mCollapseBoxOn )
221
222                mBoxes[COLLAPSE_BOX_IDX]->Draw( dc );
223        }
224    }
225
226    if ( mpPane->IsHorizontal() )
227
228        DrawGrooves( dc, wxPoint( rect.x + mHintGap + grooveOfs, pos ),
229                     rect.height - (pos - rect.y) - mHintGap );
230    else
231        DrawGrooves( dc, wxPoint( rect.x + mHintGap, rect.y + mHintGap + grooveOfs ),
232                     (pos - rect.x) - mHintGap );
233}
234
235void cbBarHintsPlugin::GetHintsLayout( wxRect& rect, cbBarInfo& info,
236                                       int& boxOfs, int& grooveOfs, int& pos )
237{
238    int boxHeight = BTN_BOX_HEIGHT;
239//  int boxWidth  = BTN_BOX_WIDTH + BOX_TO_GROOVE_GAP + BTN_BOX_WIDTH;
240
241    // collapse and close box are not placed on fixed bars
242
243    if ( info.IsFixed() || ( !mCloseBoxOn && !mCollapseBoxOn ) )
244    {
245        boxHeight = 0;
246//      boxWidth = 0;
247    }
248/*
249    else
250    if ( !mCloseBoxOn || !mCollapseBoxOn )
251
252        boxWidth = BTN_BOX_WIDTH;
253*/
254    int grooveHeight = mGrooveCount*(GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP)
255                       - GROOVE_TO_GROOVE_GAP;
256
257    int height = wxMax( grooveHeight, boxHeight );
258
259    // center boxs and groves with respect to each other
260
261    boxOfs    = ( height - boxHeight    ) / 2;
262    grooveOfs = ( height - grooveHeight ) / 2;
263
264    pos = ( mpPane->IsHorizontal() ) ? rect.y + mHintGap
265                                     : rect.x + rect.width - mHintGap;
266
267    // setup positions for boxes
268
269    if ( !info.IsFixed() )
270    {
271        // what direction "collapse-triangle" should look at?
272
273        bool& isAtLeft = ((cbCollapseBox*)(mBoxes[COLLAPSE_BOX_IDX]))->mIsAtLeft;
274
275        isAtLeft= info.mBounds.x <= mpPane->mPaneWidth - ( info.mBounds.x + info.mBounds.width );
276
277        if ( info.IsExpanded() )
278        {
279            isAtLeft = false;
280
281            cbBarInfo* pCur = info.mpPrev;
282
283            while( pCur )
284            {
285                if ( !pCur->IsFixed() )
286                {
287                    isAtLeft = true; break;
288                }
289
290                pCur = pCur->mpPrev;
291            }
292        }
293
294        // collapse/expand works only when more not-fixed bars are present in the same row
295
296        mBoxes[COLLAPSE_BOX_IDX]->Enable( info.mpRow->mNotFixedBarsCnt > 1 );
297
298        int i;
299        for ( i = 0; i != BOXES_IN_HINT; ++i )
300        {
301            mBoxes[i]->mpPane = mpPane;
302        }
303
304        if ( mpPane->IsHorizontal() )
305        {
306            if ( mCloseBoxOn )
307            {
308                mBoxes[CLOSE_BOX_IDX]->mPos = wxPoint( rect.x + mHintGap + boxOfs, pos );
309
310                pos += BTN_BOX_HEIGHT;
311            }
312
313            if ( mCollapseBoxOn )
314            {
315                if ( mCloseBoxOn ) pos += BOX_T_BOX_GAP;
316
317                mBoxes[COLLAPSE_BOX_IDX]->mPos = wxPoint( rect.x + mHintGap + boxOfs, pos );
318
319                pos += BTN_BOX_HEIGHT;
320
321                pos += BOX_TO_GROOVE_GAP;
322            }
323        }
324        else
325        {
326            if ( mCloseBoxOn )
327            {
328                pos -= BTN_BOX_WIDTH;
329
330                mBoxes[CLOSE_BOX_IDX]->mPos = wxPoint( pos , rect.y + mHintGap + boxOfs );
331            }
332
333            if ( mCollapseBoxOn )
334            {
335                if ( mCloseBoxOn ) pos -= BOX_T_BOX_GAP;
336
337                pos -= BTN_BOX_WIDTH;
338
339                mBoxes[COLLAPSE_BOX_IDX]->mPos = wxPoint( pos, rect.y + mHintGap + boxOfs );
340
341                pos -= BOX_TO_GROOVE_GAP;
342            }
343        }
344    }
345}
346
347static inline bool is_in_box( const wxPoint& rectPos, const wxPoint& mousePos )
348{
349    return ( mousePos.x >= rectPos.x &&
350             mousePos.y >= rectPos.y &&
351             mousePos.x < rectPos.x + BTN_BOX_WIDTH &&
352             mousePos.y < rectPos.y + BTN_BOX_HEIGHT );
353}
354
355int cbBarHintsPlugin::HitTestHints( cbBarInfo& info, const wxPoint& pos )
356{
357    wxPoint inPane = pos;
358    mpPane->PaneToFrame( &inPane.x, &inPane.y );
359
360    wxRect& rect = info.mBoundsInParent;
361
362    if ( info.IsFixed() ) return false;
363
364    int boxOfs, grooveOfs, coord;
365
366    GetHintsLayout( rect, info, boxOfs, grooveOfs, coord );
367
368    if ( mpPane->IsHorizontal() )
369    {
370        if ( mCloseBoxOn )
371        {
372            if ( is_in_box( wxPoint( rect.x + mHintGap + boxOfs, coord ), inPane ) )
373
374                return CLOSE_BOX_HITTED;
375
376            coord += BTN_BOX_HEIGHT;
377        }
378
379        if ( mCollapseBoxOn )
380        {
381            if ( mCloseBoxOn ) coord += BOX_T_BOX_GAP;
382
383            if ( is_in_box( wxPoint( rect.x + mHintGap + boxOfs, coord ), inPane ) )
384
385                return COLLAPSE_BOX_HITTED;
386
387            coord += BTN_BOX_HEIGHT;
388        }
389    }
390    else
391    {
392        if ( mCloseBoxOn )
393        {
394            coord -= BTN_BOX_WIDTH;
395
396            if ( is_in_box( wxPoint( coord , rect.y + mHintGap + boxOfs ), inPane ) )
397
398                return CLOSE_BOX_HITTED;
399        }
400
401        if ( mCollapseBoxOn )
402        {
403            if ( mCloseBoxOn ) coord -= BOX_T_BOX_GAP;
404            coord -= BTN_BOX_WIDTH;
405
406            if ( is_in_box( wxPoint( coord, rect.y + mHintGap + boxOfs ), inPane ) )
407
408                return COLLAPSE_BOX_HITTED;
409        }
410    }
411
412    return false;
413}
414
415// handlers for plugin-events
416
417void cbBarHintsPlugin::OnSizeBarWindow( cbSizeBarWndEvent& event )
418{
419    wxRect& rect     = event.mBoundsInParent;
420    mpPane           = event.mpPane;
421
422    ExcludeHints( rect, *event.mpBar );
423
424    event.Skip(); // pass event to the next plugin in the chain
425}
426
427void cbBarHintsPlugin::OnDrawBarDecorations( cbDrawBarDecorEvent& event )
428{
429    wxRect& rect     = event.mBoundsInParent;
430    mpPane           = event.mpPane;
431
432    int boxOfs, grooveOfs, pos;
433
434    GetHintsLayout( rect, *event.mpBar, boxOfs, grooveOfs, pos );
435
436    DoDrawHint( *event.mpDc, rect, pos, boxOfs, grooveOfs, event.mpBar->IsFixed() );
437
438    // let other plugins add on their decorations
439
440    event.Skip();
441}
442
443void cbBarHintsPlugin::OnLeftDown( cbLeftDownEvent& event )
444{
445    mpPane           = event.mpPane;
446    wxPoint inFrame = event.mPos;
447
448    mpPane->PaneToFrame( &inFrame.x, &inFrame.y );
449
450    wxBarIterator iter( mpPane->GetRowList() );
451
452    mpClickedBar = NULL;
453
454    while ( iter.Next() )
455    {
456        cbBarInfo& bar = iter.BarInfo();
457
458        int boxOfs, grooveOfs, pos;
459
460        GetHintsLayout( bar.mBoundsInParent, bar, boxOfs, grooveOfs, pos );
461
462        if ( !bar.IsFixed() )
463        {
464            int i;
465            for ( i = 0; i != BOXES_IN_HINT; ++i )
466            {
467                mBoxes[i]->mPressed = false;
468                mBoxes[i]->mWasClicked = false;
469            }
470            for ( i = 0; i != BOXES_IN_HINT; ++i )
471            {
472                mBoxes[i]->OnLeftDown( inFrame );
473
474                if ( mBoxes[i]->mPressed )
475                {
476                    mBtnPressed = true;
477                    mpClickedBar = &bar;
478
479                    return; // event handled
480                }
481            }
482        }
483    }
484
485    event.Skip();
486}
487
488void cbBarHintsPlugin::OnLeftUp( cbLeftUpEvent&   event )
489{
490    if ( mBtnPressed )
491    {
492        wxPoint inFrame = event.mPos;
493        mpPane->PaneToFrame( &inFrame.x, &inFrame.y );
494
495        int boxOfs, grooveOfs, pos;
496
497        GetHintsLayout( mpClickedBar->mBoundsInParent, *mpClickedBar, boxOfs, grooveOfs, pos );
498
499        HitTestHints( *mpClickedBar, event.mPos );
500
501        int i;
502        for ( i = 0; i != BOXES_IN_HINT; ++i )
503        {
504            mBoxes[i]->OnLeftUp( inFrame );
505
506            if ( mBoxes[i]->WasClicked() )
507            {
508                if ( i == 0 )
509                {
510                    mpLayout->SetBarState( mpClickedBar, wxCBAR_HIDDEN, true );
511                    // Notify bar child window of close event:
512                    if(mpClickedBar->mpBarWnd!=NULL)
513                        mpClickedBar->mpBarWnd->Close();
514                }
515                else
516                {
517                    if ( mpClickedBar->IsExpanded() )
518                        mpPane->ContractBar( mpClickedBar );
519                    else
520                        mpPane->ExpandBar( mpClickedBar );
521                }
522            }
523        }
524
525        mBtnPressed = false;
526        return;
527    }
528    else
529        event.Skip();
530}
531
532void cbBarHintsPlugin::OnMotion( cbMotionEvent&   event )
533{
534    if ( mBtnPressed )
535    {
536        wxPoint inFrame = event.mPos;
537        mpPane->PaneToFrame( &inFrame.x, &inFrame.y );
538
539        mpPane = event.mpPane;
540
541        int i;
542        for ( i = 0; i != BOXES_IN_HINT; ++i )
543        {
544            mBoxes[i]->OnMotion( inFrame );
545        }
546    }
547    else
548        event.Skip();
549}
550
551void cbBarHintsPlugin::OnInitPlugin()
552{
553    cbPluginBase::OnInitPlugin();
554
555    cbDockPane** panes = mpLayout->GetPanesArray();
556
557    int i;
558    for ( i = 0; i != MAX_PANES; ++i )
559    {
560        if ( panes[i]->MatchesMask( mPaneMask ) )
561        {
562            panes[i]->mProps.mMinCBarDim.x = 25;
563            panes[i]->mProps.mMinCBarDim.y = 16;
564        }
565    }
566    CreateBoxes();
567}
568