1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/univ/toolbar.cpp 
   3 // Purpose:     implementation of wxToolBar for wxUniversal 
   4 // Author:      Robert Roebling, Vadim Zeitlin (universalization) 
   8 // Copyright:   (c) 2001 Robert Roebling, 
   9 //              (c) 2002 SciTech Software, Inc. (www.scitechsoft.com) 
  10 // Licence:     wxWindows licence 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  22     #pragma implementation "univtoolbar.h" 
  25 // For compilers that support precompilation, includes "wx.h". 
  26 #include "wx/wxprec.h" 
  39 #include "wx/univ/renderer.h" 
  41 #include "wx/toolbar.h" 
  45 // ---------------------------------------------------------------------------- 
  47 // ---------------------------------------------------------------------------- 
  49 // value meaning that m_widthSeparator is not initialized 
  50 static const wxCoord INVALID_WIDTH 
= -1; 
  52 // ---------------------------------------------------------------------------- 
  53 // wxToolBarTool: our implementation of wxToolBarToolBase 
  54 // ---------------------------------------------------------------------------- 
  56 class WXDLLEXPORT wxToolBarTool 
: public wxToolBarToolBase
 
  59     wxToolBarTool(wxToolBar 
*tbar
, 
  61                   const wxString
& label
, 
  62                   const wxBitmap
& bmpNormal
, 
  63                   const wxBitmap
& bmpDisabled
, 
  66                   const wxString
& shortHelp
, 
  67                   const wxString
& longHelp
) 
  68         : wxToolBarToolBase(tbar
, id
, label
, bmpNormal
, bmpDisabled
, kind
, 
  69                             clientData
, shortHelp
, longHelp
) 
  82     // is this tool pressed, even temporarily? (this is different from being 
  83     // permanently toggled which is what IsToggled() returns) 
  84     bool IsPressed() const 
  85         { return CanBeToggled() ? IsToggled() != m_isInverted 
: m_isInverted
; } 
  87     // are we temporarily pressed/unpressed? 
  88     bool IsInverted() const { return m_isInverted
; } 
  90     // press the tool temporarily by inverting its toggle state 
  91     void Invert() { m_isInverted 
= !m_isInverted
; } 
  94     void SetUnderMouse( bool under 
= TRUE 
) { m_underMouse 
= under
; } 
  95     bool IsUnderMouse() { return m_underMouse
; } 
  98     // the tool position (the size is known by the toolbar itself) 
 103     // TRUE if the tool is pressed 
 106     // TRUE if the tool is under the mouse 
 110 // ============================================================================ 
 111 // wxToolBar implementation 
 112 // ============================================================================ 
 114 IMPLEMENT_DYNAMIC_CLASS(wxToolBar
, wxControl
); 
 116 // ---------------------------------------------------------------------------- 
 117 // wxToolBar creation 
 118 // ---------------------------------------------------------------------------- 
 120 void wxToolBar::Init() 
 123     m_needsLayout 
= FALSE
; 
 125     // unknown widths for the tools and separators 
 126     m_widthSeparator 
= INVALID_WIDTH
; 
 131     wxRenderer 
*renderer 
= GetRenderer(); 
 133     SetToolBitmapSize(renderer
->GetToolBarButtonSize(&m_widthSeparator
)); 
 134     SetMargins(renderer
->GetToolBarMargin()); 
 137 bool wxToolBar::Create(wxWindow 
*parent
, 
 142                        const wxString
& name
) 
 144     if ( !wxToolBarBase::Create(parent
, id
, pos
, size
, style
, 
 145                                 wxDefaultValidator
, name
) ) 
 150     CreateInputHandler(wxINP_HANDLER_TOOLBAR
); 
 157 wxToolBar::~wxToolBar() 
 161 void wxToolBar::SetMargins(int x
, int y
) 
 163     // This required for similar visual effects under 
 164     // native platforms and wxUniv. 
 165     wxToolBarBase::SetMargins( x 
+ 3, y 
+ 3 ); 
 168 // ---------------------------------------------------------------------------- 
 169 // wxToolBar tool-related methods 
 170 // ---------------------------------------------------------------------------- 
 172 wxToolBarToolBase 
*wxToolBar::FindToolForPosition(wxCoord x
, wxCoord y
) const 
 174     // check the "other" direction first: it must be inside the toolbar or we 
 175     // don't risk finding anything 
 178         if ( x 
< 0 || x 
> m_maxWidth 
) 
 181         // we always use x, even for a vertical toolbar, this makes the code 
 187         if ( y 
< 0 || y 
> m_maxHeight 
) 
 191     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 193           node 
= node
->GetNext() ) 
 195         wxToolBarToolBase 
*tool 
=  node
->GetData(); 
 196         wxRect rectTool 
= GetToolRect(tool
); 
 198         wxCoord startTool
, endTool
; 
 199         GetRectLimits(rectTool
, &startTool
, &endTool
); 
 201         if ( x 
>= startTool 
&& x 
<= endTool 
) 
 203             // don't return the separators from here, they don't accept any 
 205             return tool
->IsSeparator() ? NULL 
: tool
; 
 212 void wxToolBar::SetToolShortHelp(int id
, const wxString
& help
) 
 214     wxToolBarToolBase 
*tool 
= FindById(id
); 
 216     wxCHECK_RET( tool
, _T("SetToolShortHelp: no such tool") ); 
 218     tool
->SetShortHelp(help
); 
 221 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos
), 
 222                              wxToolBarToolBase 
* WXUNUSED(tool
)) 
 224     // recalculate the toolbar geometry before redrawing it the next time 
 225     m_needsLayout 
= TRUE
; 
 227     // and ensure that we indeed are going to redraw 
 233 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos
), 
 234                              wxToolBarToolBase 
* WXUNUSED(tool
)) 
 237     m_needsLayout 
= TRUE
; 
 244 void wxToolBar::DoEnableTool(wxToolBarToolBase 
*tool
, bool enable
) 
 246     // created disabled-state bitmap on demand 
 247     if ( !enable 
&& !tool
->GetDisabledBitmap().Ok() ) 
 249         wxImage 
image( tool
->GetNormalBitmap().ConvertToImage() ); 
 251         // TODO: don't hardcode 180 
 252         unsigned char bg_red 
= 180; 
 253         unsigned char bg_green 
= 180; 
 254         unsigned char bg_blue 
= 180; 
 256         unsigned char mask_red 
= image
.GetMaskRed(); 
 257         unsigned char mask_green 
= image
.GetMaskGreen(); 
 258         unsigned char mask_blue 
= image
.GetMaskBlue(); 
 260         bool has_mask 
= image
.HasMask(); 
 263         for (y 
= 0; y 
< image
.GetHeight(); y
++) 
 265             for (x 
= 0; x 
< image
.GetWidth(); x
++) 
 267                 unsigned char red 
= image
.GetRed(x
,y
); 
 268                 unsigned char green 
= image
.GetGreen(x
,y
); 
 269                 unsigned char blue 
= image
.GetBlue(x
,y
); 
 270                 if (!has_mask 
|| red 
!= mask_red 
|| green 
!= mask_green 
|| blue 
!= mask_blue
) 
 272                     red 
= (((wxInt32
) red  
- bg_red
) >> 1) + bg_red
; 
 273                     green 
= (((wxInt32
) green  
- bg_green
) >> 1) + bg_green
; 
 274                     blue 
= (((wxInt32
) blue  
- bg_blue
) >> 1) + bg_blue
; 
 275                     image
.SetRGB( x
, y
, red
, green
, blue 
); 
 280         for (y 
= 0; y 
< image
.GetHeight(); y
++) 
 282             for (x 
= y 
% 2; x 
< image
.GetWidth(); x 
+= 2) 
 284                 unsigned char red 
= image
.GetRed(x
,y
); 
 285                 unsigned char green 
= image
.GetGreen(x
,y
); 
 286                 unsigned char blue 
= image
.GetBlue(x
,y
); 
 287                 if (!has_mask 
|| red 
!= mask_red 
|| green 
!= mask_green 
|| blue 
!= mask_blue
) 
 289                     red 
= (((wxInt32
) red  
- bg_red
) >> 1) + bg_red
; 
 290                     green 
= (((wxInt32
) green  
- bg_green
) >> 1) + bg_green
; 
 291                     blue 
= (((wxInt32
) blue  
- bg_blue
) >> 1) + bg_blue
; 
 292                     image
.SetRGB( x
, y
, red
, green
, blue 
); 
 297         tool
->SetDisabledBitmap(image
); 
 303 void wxToolBar::DoToggleTool(wxToolBarToolBase 
*tool
, bool WXUNUSED(toggle
)) 
 305     // note that if we're called the tool did change state (the base class 
 306     // checks for it), so it's not necessary to check for this again here 
 310 void wxToolBar::DoSetToggle(wxToolBarToolBase 
*tool
, bool WXUNUSED(toggle
)) 
 315 wxToolBarToolBase 
*wxToolBar::CreateTool(int id
, 
 316                                          const wxString
& label
, 
 317                                          const wxBitmap
& bmpNormal
, 
 318                                          const wxBitmap
& bmpDisabled
, 
 320                                          wxObject 
*clientData
, 
 321                                          const wxString
& shortHelp
, 
 322                                          const wxString
& longHelp
) 
 324     return new wxToolBarTool(this, id
, label
, bmpNormal
, bmpDisabled
, kind
, 
 325                              clientData
, shortHelp
, longHelp
); 
 328 wxToolBarToolBase 
*wxToolBar::CreateTool(wxControl 
*control
) 
 330     wxFAIL_MSG( wxT("Toolbar doesn't support controls yet (TODO)") ); 
 335 // ---------------------------------------------------------------------------- 
 336 // wxToolBar geometry 
 337 // ---------------------------------------------------------------------------- 
 339 wxRect 
wxToolBar::GetToolRect(wxToolBarToolBase 
*toolBase
) const 
 341     const wxToolBarTool 
*tool 
= (wxToolBarTool 
*)toolBase
; 
 345     wxCHECK_MSG( tool
, rect
, _T("GetToolRect: NULL tool") ); 
 347     // ensure that we always have the valid tool position 
 350         wxConstCast(this, wxToolBar
)->DoLayout(); 
 353     rect
.x 
= tool
->m_x 
- m_xMargin
; 
 354     rect
.y 
= tool
->m_y 
- m_yMargin
; 
 358         rect
.width 
= m_defaultWidth
; 
 359         rect
.height 
= tool
->IsSeparator() ? m_widthSeparator 
: m_defaultHeight
; 
 363         rect
.width 
= tool
->IsSeparator() ? m_widthSeparator 
: m_defaultWidth
; 
 364         rect
.height 
= m_defaultHeight
; 
 367     rect
.width 
+= 2*m_xMargin
; 
 368     rect
.height 
+= 2*m_yMargin
; 
 373 bool wxToolBar::Realize() 
 375     if ( !wxToolBarBase::Realize() ) 
 378     m_needsLayout 
= TRUE
; 
 381     SetBestSize(wxDefaultSize
); 
 386 void wxToolBar::DoLayout() 
 388     wxASSERT_MSG( m_needsLayout
, _T("why are we called?") ); 
 390     m_needsLayout 
= FALSE
; 
 392     wxCoord x 
= m_xMargin
, 
 395     const wxCoord widthTool 
= IsVertical() ? m_defaultHeight 
: m_defaultWidth
; 
 396     wxCoord margin 
= IsVertical() ? m_xMargin 
: m_yMargin
, 
 397            *pCur 
= IsVertical() ? &y 
: &x
; 
 399     // calculate the positions of all elements 
 400     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 402           node 
= node
->GetNext() ) 
 404         wxToolBarTool 
*tool 
= (wxToolBarTool 
*) node
->GetData(); 
 409         // TODO ugly number fiddling 
 410         *pCur 
+= ( tool
->IsSeparator() ? m_widthSeparator 
: (widthTool
+2) ) + margin
; 
 413     // calculate the total toolbar size 
 414     wxCoord xMin 
= m_defaultWidth 
+ 2*m_xMargin
, 
 415             yMin 
= m_defaultHeight 
+ 2*m_yMargin
; 
 417     m_maxWidth 
= x 
< xMin 
? xMin 
: x
; 
 418     m_maxHeight 
= y 
< yMin 
? yMin 
: y
; 
 421 wxSize 
wxToolBar::DoGetBestClientSize() const 
 423     return wxSize(m_maxWidth
, m_maxHeight
); 
 426 // ---------------------------------------------------------------------------- 
 428 // ---------------------------------------------------------------------------- 
 430 void wxToolBar::RefreshTool(wxToolBarToolBase 
*tool
) 
 432     RefreshRect(GetToolRect(tool
)); 
 435 void wxToolBar::GetRectLimits(const wxRect
& rect
, 
 439     wxCHECK_RET( start 
&& end
, _T("NULL pointer in GetRectLimits") ); 
 443         *start 
= rect
.GetTop(); 
 444         *end 
= rect
.GetBottom(); 
 448         *start 
= rect
.GetLeft(); 
 449         *end 
= rect
.GetRight(); 
 453 void wxToolBar::DoDraw(wxControlRenderer 
*renderer
) 
 455     // prepare the variables used below 
 456     wxDC
& dc 
= renderer
->GetDC(); 
 457     wxRenderer 
*rend 
= renderer
->GetRenderer(); 
 458     // dc.SetFont(GetFont()); -- uncomment when we support labels 
 460     // draw the border separating us from the menubar (if there is no menubar 
 461     // we probably shouldn't draw it?) 
 464         rend
->DrawHorizontalLine(dc
, 0, 0, GetClientSize().x
); 
 467     // get the update rect and its limits depending on the orientation 
 468     wxRect rectUpdate 
= GetUpdateClientRect(); 
 470     GetRectLimits(rectUpdate
, &start
, &end
); 
 472     // and redraw all the tools intersecting it 
 473     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 475           node 
= node
->GetNext() ) 
 477         wxToolBarTool 
*tool 
= (wxToolBarTool
*) node
->GetData(); 
 478         wxRect rectTool 
= GetToolRect(tool
); 
 479         wxCoord startTool
, endTool
; 
 480         GetRectLimits(rectTool
, &startTool
, &endTool
); 
 482         if ( endTool 
< start 
) 
 484             // we're still to the left of the area to redraw 
 488         if ( startTool 
> end 
) 
 490             // we're beyond the area to redraw, nothing left to do 
 494         if (tool
->IsSeparator() && !HasFlag(wxTB_FLAT
)) 
 496             // Draw seperators only in flat mode 
 500         // deal with the flags 
 503         if ( tool
->IsEnabled() ) 
 505             // The toolbars without wxTB_FLAT don't react to the mouse hovering 
 506             if ( !HasFlag(wxTB_FLAT
) || tool
->IsUnderMouse() ) 
 507                 flags 
|= wxCONTROL_CURRENT
; 
 509         else // disabled tool 
 511             flags 
|= wxCONTROL_DISABLED
; 
 514         //if ( tool == m_toolCaptured ) 
 515         //    flags |= wxCONTROL_FOCUSED; 
 517         if ( tool
->IsPressed() ) 
 518             flags 
= wxCONTROL_PRESSED
; 
 522         if ( !tool
->IsSeparator() ) 
 524             // label = tool->GetLabel(); 
 525             bitmap 
= tool
->GetBitmap(); 
 527         //else: leave both the label and the bitmap invalid to draw a separator 
 529         rend
->DrawToolBarButton(dc
, label
, bitmap
, rectTool
, flags
); 
 533 // ---------------------------------------------------------------------------- 
 535 // ---------------------------------------------------------------------------- 
 537 bool wxToolBar::PerformAction(const wxControlAction
& action
, 
 539                               const wxString
& strArg
) 
 541     wxToolBarTool 
*tool 
= (wxToolBarTool
*) FindById(numArg
); 
 543     if ( action 
== wxACTION_TOOLBAR_TOGGLE 
) 
 545         PerformAction( wxACTION_BUTTON_RELEASE
, numArg 
); 
 547         PerformAction( wxACTION_BUTTON_CLICK
, numArg 
); 
 549     else if ( action 
== wxACTION_TOOLBAR_PRESS 
) 
 551         wxLogTrace(_T("toolbar"), _T("Button '%s' pressed."), tool
->GetShortHelp().c_str()); 
 557     else if ( action 
== wxACTION_TOOLBAR_RELEASE 
) 
 559         wxLogTrace(_T("toolbar"), _T("Button '%s' released."), tool
->GetShortHelp().c_str()); 
 561         wxASSERT_MSG( tool
->IsInverted(), _T("release unpressed button?") ); 
 567     else if ( action 
== wxACTION_TOOLBAR_CLICK 
) 
 570         if ( tool
->CanBeToggled() ) 
 576             isToggled 
= tool
->IsToggled(); 
 578         else // simple non-checkable tool 
 582         OnLeftClick( tool
->GetId(), isToggled 
); 
 584     else if ( action 
== wxACTION_TOOLBAR_ENTER 
) 
 586         wxCHECK_MSG( tool
, FALSE
, _T("no tool to enter?") ); 
 588         if ( HasFlag(wxTB_FLAT
) && tool
->IsEnabled() ) 
 590             tool
->SetUnderMouse( TRUE 
); 
 592             if ( !tool
->IsToggled() ) 
 596     else if ( action 
== wxACTION_TOOLBAR_LEAVE 
) 
 598         wxCHECK_MSG( tool
, FALSE
, _T("no tool to leave?") ); 
 600         if ( HasFlag(wxTB_FLAT
) && tool
->IsEnabled() ) 
 602             tool
->SetUnderMouse( FALSE 
); 
 604             if ( !tool
->IsToggled() ) 
 609         return wxControl::PerformAction(action
, numArg
, strArg
); 
 614 // ============================================================================ 
 615 // wxStdToolbarInputHandler implementation 
 616 // ============================================================================ 
 618 wxStdToolbarInputHandler::wxStdToolbarInputHandler(wxInputHandler 
*handler
) 
 619                         : wxStdInputHandler(handler
) 
 622     m_toolCapture 
= NULL
; 
 626 bool wxStdToolbarInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
 627                                          const wxKeyEvent
& event
, 
 630     // TODO: when we have a current button we should allow the arrow 
 632     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
 635 bool wxStdToolbarInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
 636                                            const wxMouseEvent
& event
) 
 638     wxToolBar 
*tbar 
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
); 
 639     wxToolBarToolBase 
*tool 
= tbar
->FindToolForPosition(event
.GetX(), event
.GetY()); 
 641     if ( event
.Button(1) ) 
 644         if ( event
.LeftDown() || event
.LeftDClick() ) 
 646             if ( !tool 
|| !tool
->IsEnabled() ) 
 650             m_winCapture
->CaptureMouse(); 
 652             m_toolCapture 
= tool
; 
 654             consumer
->PerformAction( wxACTION_BUTTON_PRESS
, tool
->GetId() ); 
 658         else if ( event
.LeftUp() ) 
 662                 m_winCapture
->ReleaseMouse(); 
 666             if ( tool 
== m_toolCapture 
) 
 667                 consumer
->PerformAction( wxACTION_BUTTON_TOGGLE
, m_toolCapture
->GetId() ); 
 669                 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() ); 
 671             m_toolCapture 
= NULL
; 
 675         //else: don't do anything special about the double click 
 678     return wxStdInputHandler::HandleMouse(consumer
, event
); 
 681 bool wxStdToolbarInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
 682                                                const wxMouseEvent
& event
) 
 684     if ( !wxStdInputHandler::HandleMouseMove(consumer
, event
) ) 
 686         wxToolBar 
*tbar 
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
); 
 689         if ( event
.Leaving() ) 
 691             // We cannot possibly be over a tool when 
 692             // leaving the toolbar 
 697             tool 
= (wxToolBarTool
*) tbar
->FindToolForPosition( event
.GetX(), event
.GetY() ); 
 702             // During capture we only care of the captured tool 
 703             if (tool 
&& (tool 
!= m_toolCapture
)) 
 706             if (tool 
== m_toolLast
) 
 710                 consumer
->PerformAction( wxACTION_BUTTON_PRESS
, m_toolCapture
->GetId() ); 
 712                 consumer
->PerformAction( wxACTION_BUTTON_RELEASE
, m_toolCapture
->GetId() ); 
 718             if (tool 
== m_toolLast
) 
 723                 // Leave old tool if any 
 724                 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolLast
->GetId() ); 
 729                 // Enter new tool if any 
 730                 consumer
->PerformAction( wxACTION_TOOLBAR_ENTER
, tool
->GetId() ); 
 742 bool wxStdToolbarInputHandler::HandleFocus(wxInputConsumer 
*consumer
, 
 743                                            const wxFocusEvent
& event
) 
 747         // We shouldn't be left with a highlighted button 
 748         consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() ); 
 754 bool wxStdToolbarInputHandler::HandleActivation(wxInputConsumer 
*consumer
, 
 757     if (m_toolCapture 
&& !activated
) 
 759         // We shouldn't be left with a highlighted button 
 760         consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() ); 
 766 #endif // wxUSE_TOOLBAR