1/////////////////////////////////////////////////////////////////////////////
2// Name:        toolwnd.cpp
3// Purpose:     wxToolWindow implementation.
4// Author:      Aleksandras Gluchovas
5// Modified by:
6// Created:     06/09/98
7// RCS-ID:      $Id: toolwnd.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/toolwnd.h"
24
25#define _IMG_A  0xAA    // Note: modified from _A to _IMG_A, _A was already defined (cygwin)
26#define _IMG_B  0x00    // Note: modified from _B to _IMG_A, _B was already defined (cygwin)
27#define _IMG_C  0x55    // Note: modified from _C to _IMG_C, for consistency reasons.
28#define _IMG_D  0x00    // Note: modified from _D to _IMG_D, for consistency reasons.
29
30// FOR NOW:: static
31
32static const unsigned char _gCheckerImg[16] = { _IMG_A,_IMG_B,_IMG_C,_IMG_D,
33                                                _IMG_A,_IMG_B,_IMG_C,_IMG_D,
34                                                _IMG_A,_IMG_B,_IMG_C,_IMG_D,
35                                                _IMG_A,_IMG_B,_IMG_C,_IMG_D
36                                              };
37
38/***** Implementation for class wxToolWindow *****/
39
40IMPLEMENT_DYNAMIC_CLASS( wxToolWindow, wxFrame)
41
42BEGIN_EVENT_TABLE( wxToolWindow, wxFrame )
43
44    EVT_PAINT    ( wxToolWindow::OnPaint )
45    EVT_MOTION   ( wxToolWindow::OnMotion )
46    EVT_LEFT_DOWN( wxToolWindow::OnLeftDown )
47    EVT_LEFT_UP  ( wxToolWindow::OnLeftUp )
48    EVT_SIZE     ( wxToolWindow::OnSize )
49
50
51    EVT_ERASE_BACKGROUND( wxToolWindow::OnEraseBackground )
52
53END_EVENT_TABLE()
54
55enum INTERNAL_HIT_CODES
56{
57    HITS_WND_NOTHING,
58    HITS_WND_CLIENT,
59    HITS_WND_TITLE,
60
61    HITS_WND_LEFT_EDGE,
62    HITS_WND_RIGHT_EDGE,
63    HITS_WND_TOP_EDGE,
64    HITS_WND_BOTTOM_EDGE,
65
66    HITS_WND_TOP_LEFT_CORNER,
67    HITS_WND_BOTTOM_RIGHT_CORNER,
68    HITS_WND_TOP_RIGHT_CORNER,
69    HITS_WND_BOTTOM_LEFT_CORNER
70};
71
72wxToolWindow::wxToolWindow()
73
74    : mpClientWnd   ( NULL ),
75
76#ifndef __WXMSW__
77    mTitleFont( 8, wxSWISS,  wxNORMAL, wxNORMAL ),
78#else
79    // just to simulate MS-Dev style
80    mTitleFont( 8, wxSWISS,  wxNORMAL, wxNORMAL, false, wxT("MS Sans Serif") ),
81#endif
82
83    mTitleHeight  ( 16 ),
84    mClntHorizGap ( 2 ),
85    mClntVertGap  ( 2 ),
86    mWndVertGap   ( 4 ),
87    mWndHorizGap  ( 4 ),
88
89    mButtonGap    ( 2 ),
90    mInTitleMargin( 4 ),
91    mHintBorder   ( 4 ),
92
93    mResizeStarted( false ),
94    mRealTimeUpdatesOn( true ),
95
96    mMTolerance   ( 5 ), // mouse-resizing tollerance
97
98    mCursorType( HITS_WND_NOTHING ),
99    mMouseCaptured( false ),
100
101    mpScrDc( NULL )
102
103{
104}
105
106wxToolWindow::~wxToolWindow()
107{
108    if ( mpScrDc ) delete mpScrDc;
109
110    for( size_t i = 0; i != mButtons.Count(); ++i )
111        delete mButtons[i];
112}
113
114void wxToolWindow::LayoutMiniButtons()
115{
116    int w,h;
117
118    GetSize( &w, &h );
119
120    int x = w - mWndHorizGap - mInTitleMargin - BTN_BOX_WIDTH;
121    int y = mWndVertGap + 2;
122
123    for( size_t i = 0; i != mButtons.Count(); ++i )
124    {
125        mButtons[i]->SetPos( wxPoint( x,y ) );
126        x-= BTN_BOX_WIDTH + mButtonGap;
127    }
128}
129
130void wxToolWindow::SetClient( wxWindow* pWnd )
131{
132    mpClientWnd = pWnd;
133}
134
135wxWindow* wxToolWindow::GetClient()
136{
137    return mpClientWnd;
138}
139
140void wxToolWindow::SetTitleFont( wxFont& font )
141{
142    mTitleFont = font;
143}
144
145void wxToolWindow::AddMiniButton( cbMiniButton* pBtn )
146{
147    pBtn->mpWnd = this;
148
149    mButtons.Add( pBtn );
150
151    // not necesserely now..
152    //LayoutMiniButtons();
153}
154
155void wxToolWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
156{
157    wxPaintDC pdc( this );
158    wxWindowDC dc( this );
159
160    int w,h;
161    GetSize( &w, &h );
162
163    wxBrush backGround( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxSOLID );
164    //dc.SetBrush( *wxLIGHT_GREY_BRUSH );
165    dc.SetBrush( backGround );
166    dc.SetPen( *wxTRANSPARENT_PEN );
167
168    int y = mWndVertGap + mTitleHeight + mClntVertGap;
169
170    dc.DrawRectangle( 0,0, w, y );                                      // Top grey part.
171    dc.DrawRectangle( 0,y-1, mWndHorizGap + mClntHorizGap, h - y );     // Left grey part.
172    dc.DrawRectangle( w - ( mWndHorizGap + mClntHorizGap ), y-1,
173                      mWndHorizGap + mClntHorizGap, h - y );            // Right grey part.
174    dc.DrawRectangle( 0, h - mWndVertGap - mClntVertGap, w, mWndVertGap + mClntVertGap ); // Bottom grey part.
175
176    // draw shades
177    dc.SetPen( *wxLIGHT_GREY_PEN );
178
179    dc.DrawLine( 0,0, w, 0 );
180    dc.DrawLine( 0,0, 0, h );
181
182    dc.SetPen( *wxWHITE_PEN );
183
184    dc.DrawLine( 1,1, w, 1 );
185    dc.DrawLine( 1,2, 1, h );
186
187    dc.SetPen( *wxGREY_PEN );
188
189    dc.DrawLine( w - 2, 1, w - 2, h - 1 );
190    dc.DrawLine( 1, h - 2, w - 2, h - 2 );
191
192    dc.SetPen( *wxBLACK_PEN );
193
194    dc.DrawLine( 0, h - 1, w, h - 1 );
195    dc.DrawLine( w-1, 0, w-1, h );
196
197    // fill inner area
198
199    dc.SetBrush( *wxTheBrushList->FindOrCreateBrush( wxColour( 0,0,128 ), wxSOLID ) );
200
201    dc.DrawRectangle( mWndHorizGap, mWndVertGap, w - mWndHorizGap*2, mTitleHeight );
202
203    dc.SetFont( mTitleFont );
204
205    for( size_t i = 0; i != mButtons.Count(); ++i )
206        mButtons[i]->Draw( dc );
207
208    int x1 = mWndHorizGap + mClntHorizGap;
209    int x2 = mButtons[ mButtons.GetCount() - 1 ]->mPos.x - mClntHorizGap*2;
210
211    dc.SetClippingRegion( x1, mWndVertGap + mClntVertGap, x2 - x1, mTitleHeight );
212
213    dc.SetTextForeground( *wxWHITE );
214    dc.SetBackgroundMode(  wxTRANSPARENT );
215    dc.DrawText( GetTitle(), mWndHorizGap + 2, mWndVertGap + 1 );
216}
217
218void wxToolWindow::GetScrWindowRect( wxRect& r )
219{
220    int x,y;
221    GetPosition(&x,&y);
222    int w,h;
223    GetSize( &w, &h );
224
225    r.x = x; r.y = y;
226    r.width = w; r.height = h;
227}
228
229void wxToolWindow::GetScrMousePos( wxMouseEvent& event, wxPoint& pos )
230{
231    int x = event.m_x, y = event.m_y;
232
233    ClientToScreen( &x, &y );
234
235    pos.x = x; pos.y = y;
236}
237
238int wxToolWindow::HitTestWindow( wxMouseEvent& event )
239{
240    wxPoint pos;
241    wxRect r;
242
243    GetScrMousePos( event, pos );
244    GetScrWindowRect( r );
245
246    int k = mMTolerance;
247
248    if ( !( pos.x >= r.x && pos.y >= r.y &&
249            pos.x < r.x + r.width &&
250            pos.y < r.y + r.height )
251       )
252        return HITS_WND_NOTHING;
253
254    if ( pos.y <= r.y + k )
255    {
256        if ( pos.x < r.x + k*2 )
257            return HITS_WND_TOP_LEFT_CORNER;
258        else
259        {
260            if ( pos.x >= r.x + r.width - k*2 )
261                return HITS_WND_TOP_RIGHT_CORNER;
262            else
263                return HITS_WND_TOP_EDGE;
264        }
265    }
266    else
267    {
268        if ( pos.y >= r.y + r.height - k )
269        {
270            if ( pos.x < r.x + k*2 )
271                return HITS_WND_BOTTOM_LEFT_CORNER;
272            else
273            {
274                if ( pos.x > r.x + r.width - k*2 )
275                    return HITS_WND_BOTTOM_RIGHT_CORNER;
276                else
277                    return HITS_WND_BOTTOM_EDGE;
278            }
279        }
280        else
281        {
282            if ( pos.x <= r.x + k )
283                return HITS_WND_LEFT_EDGE;
284            else
285            {
286                if ( pos.x >= r.x + r.width - k )
287                    return HITS_WND_RIGHT_EDGE;
288                else
289                {
290                    if ( pos.y <= r.y + mWndVertGap + mTitleHeight + mClntVertGap )
291                        return HITS_WND_TITLE;
292                    else
293                        return HITS_WND_CLIENT;
294                }
295            }
296        }
297    }
298}
299
300void wxToolWindow::DrawHintRect( const wxRect& r )
301{
302    // BUG BUG BUG (wx):: somehow stippled brush works only
303    //                    when the bitmap created on stack, not
304    //                    as a member of the class
305
306    int prevLF = mpScrDc->GetLogicalFunction();
307
308    mpScrDc->SetLogicalFunction( wxXOR );
309
310    wxBitmap checker( (const char*)_gCheckerImg, 8,8 );
311
312    wxBrush checkerBrush( checker );
313
314    mpScrDc->SetPen( *wxTRANSPARENT_PEN );
315    mpScrDc->SetBrush( checkerBrush );
316
317    int half = mHintBorder / 2;
318
319    mpScrDc->DrawRectangle( r.x - half, r.y - half,
320                            r.width + 2*half, mHintBorder );
321
322    mpScrDc->DrawRectangle( r.x - half, r.y + r.height - half,
323                            r.width + 2*half, mHintBorder );
324
325    mpScrDc->DrawRectangle( r.x - half, r.y + half - 1,
326                            mHintBorder, r.height - 2*half + 2);
327
328    mpScrDc->DrawRectangle( r.x + r.width - half,
329                            r.y + half - 1,
330                            mHintBorder, r.height - 2*half + 2);
331
332    mpScrDc->SetBrush( wxNullBrush );
333
334    mpScrDc->SetLogicalFunction( prevLF );
335}
336
337void wxToolWindow::SetHintCursor( int type )
338{
339    if ( mResizeStarted )
340        return;
341
342    if ( type == HITS_WND_NOTHING || type == HITS_WND_CLIENT )
343    {
344        // the cursor is out of window - reset to arrow
345
346        if ( mMouseCaptured )
347        {
348            ReleaseMouse();
349            mMouseCaptured = false;
350        }
351
352        SetCursor( wxCURSOR_ARROW );
353
354        mCursorType = type;
355
356        return;
357    }
358
359    // did the cursor actually changed?
360
361    if ( type != mCursorType )
362    {
363        mCursorType = type;
364
365        switch ( type )
366        {
367            case HITS_WND_LEFT_EDGE   : SetCursor( wxCURSOR_SIZEWE ); break;
368            case HITS_WND_RIGHT_EDGE  : SetCursor( wxCURSOR_SIZEWE ); break;
369            case HITS_WND_TOP_EDGE    : SetCursor( wxCURSOR_SIZENS ); break;
370            case HITS_WND_BOTTOM_EDGE : SetCursor( wxCURSOR_SIZENS ); break;
371
372            case HITS_WND_TOP_LEFT_CORNER     : SetCursor( wxCURSOR_SIZENWSE ); break;
373            case HITS_WND_BOTTOM_RIGHT_CORNER : SetCursor( wxCURSOR_SIZENWSE ); break;
374            case HITS_WND_TOP_RIGHT_CORNER    : SetCursor( wxCURSOR_SIZENESW ); break;
375            case HITS_WND_BOTTOM_LEFT_CORNER  : SetCursor( wxCURSOR_SIZENESW ); break;
376
377            case HITS_WND_TITLE  : SetCursor( wxCURSOR_ARROW ); break;
378            case HITS_WND_CLIENT : SetCursor( wxCURSOR_ARROW ); break;
379
380            default: break;
381        }
382
383        if (mMouseCaptured)
384        {
385            mMouseCaptured = false;
386            ReleaseMouse();
387        }
388    }
389
390    if ( !mMouseCaptured )
391    {
392        mMouseCaptured = true;
393        CaptureMouse();
394    }
395}
396
397#define FL_INFINITY 32768
398
399static inline void clip_to( int& value, long from, long till )
400{
401    if ( value < from )
402        value = from;
403
404    if ( value > till )
405        value = till;
406}
407
408void wxToolWindow::AdjustRectPos( const wxRect& original, const wxSize& newDim, wxRect& newRect )
409{
410    if ( mCursorType == HITS_WND_TOP_EDGE ||
411         mCursorType == HITS_WND_TOP_LEFT_CORNER )
412    {
413        newRect.x = original.x + original.width  - newDim.x;
414        newRect.y = original.y + original.height - newDim.y;
415    }
416    else
417    if ( mCursorType == HITS_WND_LEFT_EDGE ||
418         mCursorType == HITS_WND_BOTTOM_LEFT_CORNER )
419    {
420        newRect.x = original.x + original.width  - newDim.x;
421        newRect.y = original.y;
422    }
423    else
424    if ( mCursorType == HITS_WND_RIGHT_EDGE ||
425         mCursorType == HITS_WND_TOP_RIGHT_CORNER )
426    {
427        newRect.x = original.x;
428        newRect.y = original.y + original.height - newDim.y;
429    }
430    else
431    if ( mCursorType == HITS_WND_BOTTOM_EDGE ||
432         mCursorType == HITS_WND_BOTTOM_RIGHT_CORNER )
433    {
434        newRect.x = original.x;
435        newRect.y = original.y;
436    }
437
438    newRect.width  = newDim.x;
439    newRect.height = newDim.y;
440}
441
442void wxToolWindow::CalcResizedRect( wxRect& rect, wxPoint& delta, const wxSize& minDim )
443{
444    // Microsoft's rect-coordinates are best suited
445    // for the case of corner-clipping
446
447    int left   = mInitialRect.x;
448    int top    = mInitialRect.y;
449    int right  = mInitialRect.x + mInitialRect.width;
450    int bottom = mInitialRect.y + mInitialRect.height;
451
452    // constraint delta edge is dragged
453
454    switch ( mCursorType )
455    {
456        case HITS_WND_LEFT_EDGE   : delta.y = 0; break;
457        case HITS_WND_RIGHT_EDGE  : delta.y = 0; break;
458        case HITS_WND_TOP_EDGE    : delta.x = 0; break;
459        case HITS_WND_BOTTOM_EDGE : delta.x = 0; break;
460        default: break;
461    }
462
463    if ( mCursorType == HITS_WND_TOP_EDGE ||
464         mCursorType == HITS_WND_TOP_LEFT_CORNER )
465    {
466        left += delta.x;
467        top  += delta.y;
468
469        clip_to( left, -FL_INFINITY, mInitialRect.x + mInitialRect.width  - minDim.x  );
470        clip_to( top,  -FL_INFINITY, mInitialRect.y + mInitialRect.height - minDim.y );
471    }
472    else
473    if ( mCursorType == HITS_WND_LEFT_EDGE ||
474         mCursorType == HITS_WND_BOTTOM_LEFT_CORNER )
475    {
476        left   += delta.x;
477        bottom += delta.y;
478
479        clip_to( left,    -FL_INFINITY, mInitialRect.x + mInitialRect.width  - minDim.x  );
480        clip_to( bottom,  mInitialRect.y + minDim.y, FL_INFINITY );
481    }
482    else
483    if ( mCursorType == HITS_WND_RIGHT_EDGE ||
484        mCursorType == HITS_WND_TOP_RIGHT_CORNER )
485    {
486        right += delta.x;
487        top   += delta.y;
488
489        clip_to( right, mInitialRect.x + minDim.x, FL_INFINITY );
490        clip_to( top,   -FL_INFINITY, mInitialRect.y + mInitialRect.height - minDim.y );
491    }
492    else
493    if ( mCursorType == HITS_WND_BOTTOM_EDGE ||
494         mCursorType == HITS_WND_BOTTOM_RIGHT_CORNER )
495    {
496        right  += delta.x;
497        bottom += delta.y;
498
499        clip_to( right,  mInitialRect.x + minDim.x,  FL_INFINITY );
500        clip_to( bottom, mInitialRect.y + minDim.y, FL_INFINITY );
501    }
502    else
503    {
504        wxFAIL_MSG( _T("what did the cursor hit?") );
505    }
506
507    rect.x = left;
508    rect.y = top;
509    rect.width  = right - left;
510    rect.height = bottom - top;
511}
512
513wxSize wxToolWindow::GetMinimalWndDim()
514{
515    return wxSize( (mWndHorizGap + mClntHorizGap)*2 + BTN_BOX_WIDTH*4,
516                   (mWndVertGap  + mClntVertGap )*2 + mTitleHeight );
517}
518
519void wxToolWindow::OnMotion( wxMouseEvent& event )
520{
521    if ( !mResizeStarted )
522    {
523        for( size_t i = 0; i != mButtons.Count(); ++i )
524            mButtons[i]->OnMotion( wxPoint( event.m_x, event.m_y ) );
525
526        SetHintCursor( HitTestWindow( event ) );
527        return;
528    }
529
530    wxPoint pos;
531    GetScrMousePos( event, pos );
532
533    if ( mCursorType == HITS_WND_TITLE )
534    {
535        int w,h;
536        GetSize( &w, &h );
537
538        SetSize( mInitialRect.x + pos.x - mDragOrigin.x,
539                 mInitialRect.y + pos.y - mDragOrigin.y,
540                 w,h, 0 );
541    }
542
543    else
544    {
545        wxPoint delta( pos.x - mDragOrigin.x, pos.y - mDragOrigin.y );
546
547        wxRect newRect;
548
549        wxSize minDim = GetMinimalWndDim();
550
551        CalcResizedRect( newRect, delta, minDim );
552
553        wxSize borderDim( ( mWndHorizGap + mClntHorizGap )*2,
554                          ( mWndVertGap  + mClntVertGap  )*2 + mTitleHeight );
555
556        wxSize preferred = GetPreferredSize( wxSize( newRect.width  - borderDim.x,
557                                             newRect.height - borderDim.y ) );
558
559        preferred.x += borderDim.x;
560        preferred.y += borderDim.y;
561
562        //CalcResizedRect( newRect, delta, preferred );
563
564        wxRect finalRect = newRect;
565
566        AdjustRectPos( newRect, preferred, finalRect );
567
568        if ( mRealTimeUpdatesOn )
569        {
570            SetSize( finalRect.x, finalRect.y,
571                     finalRect.width, finalRect.height, 0 );
572        }
573        else
574        {
575            DrawHintRect( mPrevHintRect );
576            DrawHintRect( finalRect );
577
578            ::wxLogTrace(wxT("wxToolWindow"),wxT("%d,%d / %d,%d\n"), finalRect.x, finalRect.y, finalRect.width, finalRect.height);
579        }
580
581        mPrevHintRect = finalRect;
582    }
583}
584
585void wxToolWindow::OnLeftDown( wxMouseEvent& event )
586{
587    int result = HitTestWindow( event );
588
589    for( size_t i = 0; i != mButtons.Count(); ++i )
590    {
591        mButtons[i]->OnLeftDown( wxPoint( event.m_x, event.m_y ) );
592
593        if ( mButtons[i]->IsPressed() )
594            return; // button hitted,
595    }
596
597    if ( result >= HITS_WND_LEFT_EDGE || result == HITS_WND_TITLE )
598    {
599        GetScrMousePos( event, mDragOrigin );
600
601        /*
602        if ( mMouseCaptured `)
603        {
604            ReleaseMouse();
605            mMouseCaptured = false;
606        }*/
607
608        if ( result == HITS_WND_TITLE &&
609             HandleTitleClick( event ) )
610            return;
611
612        mResizeStarted = true;
613
614        int x,y;
615        GetPosition( &x, &y );
616
617        mInitialRect.x = x;
618        mInitialRect.y = y;
619
620        GetSize( &x, &y );
621        mInitialRect.width  = x;
622        mInitialRect.height = y;
623
624        mPrevHintRect = mInitialRect;
625
626        if ( mCursorType != HITS_WND_TITLE && !mRealTimeUpdatesOn )
627        {
628            mpScrDc = new wxScreenDC();
629
630            wxScreenDC::StartDrawingOnTop( (wxRect*)NULL );
631
632            DrawHintRect( mInitialRect );
633        }
634    }
635}
636
637void wxToolWindow::OnLeftUp( wxMouseEvent& event )
638{
639    for( size_t i = 0; i != mButtons.Count(); ++i )
640    {
641        mButtons[i]->OnLeftUp( wxPoint( event.m_x, event.m_y ) );
642
643        if ( mButtons[i]->WasClicked() )
644        {
645            OnMiniButtonClicked( i ); // notify derived classes
646            mButtons[i]->Reset();
647        }
648    }
649
650    if ( mResizeStarted )
651    {
652        mResizeStarted = false;
653
654        if ( mCursorType != HITS_WND_TITLE )
655        {
656            if ( !mRealTimeUpdatesOn )
657            {
658                DrawHintRect( mPrevHintRect );
659
660                wxScreenDC::EndDrawingOnTop();
661
662                delete mpScrDc;
663
664                mpScrDc = NULL;
665
666                SetSize( mPrevHintRect.x, mPrevHintRect.y,
667                         mPrevHintRect.width, mPrevHintRect.height, 0 );
668            }
669        }
670    }
671}
672
673void wxToolWindow::OnSize( wxSizeEvent& WXUNUSED(event) )
674{
675    if ( mpClientWnd )
676    {
677        int w,h;
678        GetSize( &w, &h );
679
680        int x = mWndHorizGap + mClntHorizGap;
681        int y = mWndVertGap  + mTitleHeight + mClntVertGap;
682
683        mpClientWnd->SetSize( x-1, y-1,
684                              w - 2*(mWndHorizGap + mClntHorizGap),
685                              h - y - mClntVertGap - mWndVertGap,
686                              0
687                            );
688    }
689
690    LayoutMiniButtons();
691}
692
693wxSize wxToolWindow::GetPreferredSize( const wxSize& given )
694{
695    return given;
696}
697
698void wxToolWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) )
699{
700    // nothing
701}
702
703/***** Implementation for class cbMiniButton *****/
704
705cbMiniButton::cbMiniButton()
706
707    : mVisible( true ),
708      mEnabled( true ),
709
710      mpLayout( NULL ),
711      mpPane  ( NULL ),
712      mpPlugin( NULL ),
713      mpWnd   ( NULL ),
714
715      mWasClicked( false ),
716      mDragStarted( false ),
717      mPressed( false )
718{}
719
720void cbMiniButton::SetPos( const wxPoint& pos )
721{
722    mPos = pos;
723}
724
725bool cbMiniButton::HitTest( const wxPoint& pos )
726{
727    if ( !mVisible ) return false;
728
729    return ( pos.x >= mPos.x && pos.y >= mPos.y &&
730             pos.x < mPos.x + BTN_BOX_WIDTH     &&
731             pos.y < mPos.y + BTN_BOX_HEIGHT );
732}
733
734void cbMiniButton::OnLeftDown( const wxPoint& pos )
735{
736    if ( !mVisible || mDragStarted ) return;
737
738    if ( HitTest( pos ) && mEnabled )
739    {
740        if ( mpPlugin )
741        {
742            mpLayout->CaptureEventsForPane( mpPane );
743            mpLayout->CaptureEventsForPlugin( mpPlugin );
744        }
745        else
746            mpWnd->CaptureMouse();
747
748        mDragStarted = true;
749        mPressed     = true;
750        mWasClicked  = false;
751
752        Refresh();
753    }
754}
755
756void cbMiniButton::OnLeftUp( const wxPoint& WXUNUSED(pos) )
757{
758    if ( !mVisible || !mDragStarted ) return;
759
760    if ( mpPlugin )
761    {
762        mpLayout->ReleaseEventsFromPane( mpPane );
763        mpLayout->ReleaseEventsFromPlugin( mpPlugin );
764    }
765    else
766        mpWnd->ReleaseMouse();
767
768    mWasClicked  = mPressed;
769    mDragStarted = false;
770
771    mPressed = false;
772    Refresh();
773}
774
775void cbMiniButton::OnMotion( const wxPoint& pos )
776{
777    if ( !mVisible ) return;
778
779    if ( mDragStarted )
780    {
781        mPressed = HitTest( pos );
782
783        Refresh();
784    }
785}
786
787void cbMiniButton::Refresh()
788{
789    if ( mpLayout )
790    {
791         wxClientDC dc( &mpLayout->GetParentFrame() );
792
793         Draw( dc );
794    }
795    else
796    {
797        wxWindowDC dc( mpWnd );
798
799        Draw( dc );
800    }
801}
802
803void cbMiniButton::Draw( wxDC& dc )
804{
805    if ( !mVisible ) return;
806
807    dc.SetPen( *wxTRANSPARENT_PEN );
808
809    dc.SetBrush( *wxLIGHT_GREY_BRUSH );
810
811    dc.DrawRectangle( mPos.x + 1, mPos.y + 1, BTN_BOX_WIDTH - 2, BTN_BOX_HEIGHT - 2 );
812
813    // "hard-code" metafile
814
815    if ( !mPressed )
816        dc.SetPen( *wxWHITE_PEN );
817    else
818        dc.SetPen( *wxBLACK_PEN );
819
820    dc.DrawLine( mPos.x, mPos.y, mPos.x + BTN_BOX_WIDTH, mPos.y );
821    dc.DrawLine( mPos.x, mPos.y, mPos.x, mPos.y + BTN_BOX_HEIGHT );
822
823    dc.SetPen( *wxGREY_PEN );
824
825    if ( !mPressed )
826    {
827        dc.DrawLine( mPos.x + 1, mPos.y + BTN_BOX_HEIGHT - 2,
828                     mPos.x + BTN_BOX_WIDTH - 1, mPos.y + BTN_BOX_HEIGHT - 2 );
829
830        dc.DrawLine( mPos.x + BTN_BOX_WIDTH - 2, mPos.y + 1,
831                     mPos.x + BTN_BOX_WIDTH - 2, mPos.y + BTN_BOX_HEIGHT - 1 );
832    }
833    else
834    {
835        dc.DrawLine( mPos.x + 1, mPos.y + 1,
836                     mPos.x + BTN_BOX_WIDTH - 2, mPos.y + 1 );
837
838        dc.DrawLine( mPos.x + 1, mPos.y + 1,
839                     mPos.x + 1, mPos.y + BTN_BOX_HEIGHT - 2 );
840    }
841
842    if ( !mPressed )
843        dc.SetPen( *wxBLACK_PEN );
844    else
845        dc.SetPen( *wxWHITE_PEN );
846
847    dc.DrawLine( mPos.x, mPos.y + BTN_BOX_HEIGHT - 1,
848                 mPos.x + BTN_BOX_WIDTH, mPos.y + BTN_BOX_HEIGHT - 1 );
849
850    dc.DrawLine( mPos.x + BTN_BOX_WIDTH - 1, mPos.y ,
851                 mPos.x + BTN_BOX_WIDTH - 1, mPos.y + BTN_BOX_HEIGHT );
852}
853
854bool cbMiniButton::WasClicked()
855{
856    return mWasClicked;
857}
858
859void cbMiniButton::Reset()
860{
861    mWasClicked = false;
862}
863
864/***** Implementation fro class cbCloseBox *****/
865
866void cbCloseBox::Draw( wxDC& dc )
867{
868    cbMiniButton::Draw( dc );
869
870    dc.SetPen( *wxBLACK_PEN );
871
872    int width = BTN_BOX_WIDTH - 7;
873
874    int xOfs = (mPressed) ? 4 : 3;
875    int yOfs = (mPressed) ? 4 : 3;
876
877    for( int i = 0; i != BTN_X_WEIGHT; ++i )
878    {
879        dc.DrawLine( mPos.x + xOfs + i,
880                     mPos.y + yOfs,
881                     mPos.x + xOfs + i + width,
882                     mPos.y + yOfs + width );
883
884        dc.DrawLine( mPos.x + xOfs + i + width - 1,
885                     mPos.y + yOfs,
886                     mPos.x + xOfs + i - 1,
887                     mPos.y + yOfs + width );
888    }
889}
890
891/***** Implementation fro class cbCollapseBox *****/
892
893inline static void my_swap( int& a, int& b )
894{
895    long tmp = a;
896    a = b;
897    b = tmp;
898}
899
900void cbCollapseBox::Draw( wxDC& dc )
901{
902    cbMiniButton::Draw( dc );
903
904    dc.SetPen( *wxTRANSPARENT_PEN );
905
906    wxPoint arr[3];
907
908    int yOfs  = (mPressed) ? 3 : 2;
909    int xOfs  = (mPressed) ? 5 : 4;
910    int width = BTN_BOX_WIDTH - 8;
911
912    // rotating/shifting triangle inside collapse box
913
914    arr[0].x = xOfs;
915    arr[0].y = yOfs-1;
916    arr[2].x = xOfs;
917    arr[2].y = BTN_BOX_HEIGHT - yOfs - 1;
918    arr[1].x = xOfs + width;
919    arr[1].y = (arr[2].y + arr[0].y)/2;
920
921    if ( !mIsAtLeft )
922    {
923        arr[0].x = BTN_BOX_WIDTH - arr[0].x;
924        arr[1].x = BTN_BOX_WIDTH - arr[1].x;
925        arr[2].x = BTN_BOX_WIDTH - arr[2].x;
926    }
927
928    if ( !mpPane->IsHorizontal() )
929    {
930        my_swap( arr[0].y, arr[0].x );
931        my_swap( arr[1].y, arr[1].x );
932        my_swap( arr[2].y, arr[2].x );
933
934        arr[0].x += 1;
935        arr[1].x += 1;
936        arr[2].x += 1;
937
938        //arr[1].y -= 1;
939    }
940
941    arr[0].x += mPos.x;
942    arr[0].y += mPos.y;
943    arr[1].x += mPos.x;
944    arr[1].y += mPos.y;
945    arr[2].x += mPos.x;
946    arr[2].y += mPos.y;
947
948    if ( !mEnabled ) dc.SetBrush( *wxGREY_BRUSH );
949    else dc.SetBrush( *wxBLACK_BRUSH );
950
951    dc.DrawPolygon( 3, arr );
952    dc.SetBrush( wxNullBrush );
953}
954
955/***** Implementation for class cbDockBoxBox *****/
956
957void cbDockBox::Draw( wxDC& dc )
958{
959    cbMiniButton::Draw( dc );
960
961    int width = BTN_BOX_WIDTH - 7;
962
963    int xOfs = (mPressed) ? 4 : 3;
964    int yOfs = (mPressed) ? 4 : 3;
965
966    dc.SetPen( *wxBLACK_PEN );
967    dc.SetBrush( *wxBLACK_BRUSH );
968
969    dc.DrawRectangle( mPos.x + xOfs, mPos.y + yOfs, width, width );
970
971    xOfs += 1;
972    yOfs += 1;
973
974    dc.SetBrush( *wxWHITE_BRUSH );
975
976    dc.DrawRectangle( mPos.x + xOfs, mPos.y + yOfs, width-2, width-2 );
977}
978
979/***** Implementation for class wxToolWindow *****/
980
981IMPLEMENT_DYNAMIC_CLASS( cbFloatedBarWindow, wxToolWindow )
982
983BEGIN_EVENT_TABLE( cbFloatedBarWindow, wxToolWindow )
984
985    EVT_LEFT_DCLICK( cbFloatedBarWindow::OnDblClick )
986
987END_EVENT_TABLE()
988
989cbFloatedBarWindow::cbFloatedBarWindow()
990
991    : mpBar( NULL )
992{
993    AddMiniButton( new cbCloseBox() );
994    AddMiniButton( new cbDockBox()  );
995}
996
997void cbFloatedBarWindow::SetBar( cbBarInfo* pBar )
998{
999    mpBar = pBar;
1000}
1001
1002cbBarInfo* cbFloatedBarWindow::GetBar()
1003{
1004    return mpBar;
1005}
1006
1007void cbFloatedBarWindow::SetLayout( wxFrameLayout* pLayout )
1008{
1009    mpLayout = pLayout;
1010}
1011
1012void cbFloatedBarWindow::PositionFloatedWnd( int scrX,  int scrY,
1013                                             int width, int height )
1014{
1015    wxSize minDim = GetMinimalWndDim();
1016
1017    SetSize( scrX - mWndHorizGap - mClntHorizGap,
1018             scrY - mClntVertGap - mTitleHeight - mWndVertGap,
1019             width + minDim.x, height + minDim.y, 0 );
1020}
1021
1022wxSize cbFloatedBarWindow::GetPreferredSize( const wxSize& given )
1023{
1024    if ( mpBar->mDimInfo.GetDimHandler() )
1025    {
1026        cbBarDimHandlerBase* pHandler = mpBar->mDimInfo.GetDimHandler();
1027
1028        wxSize prefDim;
1029
1030        // int vtad = *((int*)pHandler);
1031
1032        pHandler->OnResizeBar( mpBar, given, prefDim );
1033
1034        return prefDim;
1035    }
1036    else
1037    {
1038        if ( mpBar->IsFixed() )
1039            return mpBar->mDimInfo.mSizes[ wxCBAR_FLOATING ];
1040        else
1041            return given; // not-fixed bars are resized exactly the way user wants
1042    }
1043}
1044
1045void cbFloatedBarWindow::OnMiniButtonClicked( int btnIdx )
1046{
1047    // #1 - close mini-button
1048    // #0 - dock mini-button
1049
1050    if ( btnIdx == 0 )
1051    {
1052        mpBar->mAlignment = -1; // sepcial "marking" for hidden bars out of floated state
1053        mpLayout->SetBarState( mpBar, wxCBAR_HIDDEN, true );
1054    }
1055    else
1056        mpLayout->SetBarState( mpBar, wxCBAR_DOCKED_HORIZONTALLY, true );
1057}
1058
1059bool cbFloatedBarWindow::HandleTitleClick( wxMouseEvent& event )
1060{
1061    ReleaseMouse();
1062    mMouseCaptured = false;
1063
1064    wxPoint scrPos;
1065    GetScrMousePos( event, scrPos );
1066
1067    int msX = scrPos.x,
1068        msY = scrPos.y;
1069
1070    mpLayout->GetParentFrame().ScreenToClient( &msX, &msY );
1071
1072    int x,y;
1073    GetPosition(&x,&y);
1074    int w,h;
1075    GetSize( &w, &h );
1076
1077    wxSize minDim = GetMinimalWndDim();
1078
1079    w -= minDim.x;
1080    h -= minDim.y;
1081
1082    x += mWndHorizGap + mClntHorizGap;
1083    y += mWndVertGap  + mTitleHeight + mClntVertGap;
1084
1085    mpLayout->GetParentFrame().ScreenToClient( &x, &y );
1086
1087    wxRect& bounds = mpBar->mDimInfo.mBounds[ wxCBAR_FLOATING ];
1088
1089    bounds.x = x;
1090    bounds.y = y;
1091    bounds.width  = w;
1092    bounds.height = h;
1093
1094    cbStartBarDraggingEvent dragEvt( mpBar, wxPoint(msX,msY),
1095                                     mpLayout->GetPanesArray()[FL_ALIGN_TOP] );
1096
1097    mpLayout->FirePluginEvent( dragEvt );
1098
1099    return true;
1100}
1101
1102void cbFloatedBarWindow::OnDblClick( wxMouseEvent& WXUNUSED(event) )
1103{
1104    mpLayout->SetBarState( mpBar, wxCBAR_DOCKED_HORIZONTALLY, true );
1105
1106    //wxMessageBox("toolWnd - dblClick!");
1107}
1108
1109