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" 
  38     #include "wx/univ/renderer.h" 
  41 #include "wx/toolbar.h" 
  44 // ---------------------------------------------------------------------------- 
  46 // ---------------------------------------------------------------------------- 
  48 // value meaning that m_widthSeparator is not initialized 
  49 static const wxCoord INVALID_WIDTH 
= -1; 
  51 // ---------------------------------------------------------------------------- 
  52 // wxToolBarTool: our implementation of wxToolBarToolBase 
  53 // ---------------------------------------------------------------------------- 
  55 class WXDLLEXPORT wxToolBarTool 
: public wxToolBarToolBase
 
  58     wxToolBarTool( wxToolBarBase 
*tbar 
= (wxToolBarBase 
*)NULL
, 
  59                    int id 
= wxID_SEPARATOR
, 
  60                    const wxBitmap
& bitmap1 
= wxNullBitmap
, 
  61                    const wxBitmap
& bitmap2 
= wxNullBitmap
, 
  63                    wxObject 
*clientData 
= (wxObject 
*) NULL
, 
  64                    const wxString
& shortHelpString 
= wxEmptyString
, 
  65                    const wxString
& longHelpString 
= wxEmptyString 
) 
  66         : wxToolBarToolBase(tbar
, id
, bitmap1
, bitmap2
, toggle
, clientData
, 
  67                             shortHelpString
, longHelpString
) 
  77     // is this tool pressed, even temporarily? (this is different from being 
  78     // permanently toggled which is what IsToggled() returns) 
  79     bool IsPressed() const 
  80         { return CanBeToggled() ? IsToggled() != m_isInverted 
: m_isInverted
; } 
  82     // are we temporarily pressed/unpressed? 
  83     bool IsInverted() const { return m_isInverted
; } 
  85     // press the tool temporarily by inverting its toggle state 
  86     void Invert() { m_isInverted 
= !m_isInverted
; } 
  89     // the tool position (the size is known by the toolbar itself) 
  94     // TRUE if the tool is pressed 
  98 // ============================================================================ 
  99 // wxToolBar implementation 
 100 // ============================================================================ 
 102 IMPLEMENT_DYNAMIC_CLASS(wxToolBar
, wxControl
); 
 104 // ---------------------------------------------------------------------------- 
 105 // wxToolBar creation 
 106 // ---------------------------------------------------------------------------- 
 108 void wxToolBar::Init() 
 111     m_needsLayout 
= FALSE
; 
 113     // unknown widths for the tools and separators 
 114     m_widthSeparator 
= INVALID_WIDTH
; 
 119     m_toolPressed 
= NULL
; 
 120     m_toolCurrent 
= NULL
; 
 122     wxRenderer 
*renderer 
= GetRenderer(); 
 124     SetToolBitmapSize(renderer
->GetToolBarButtonSize(&m_widthSeparator
)); 
 125     SetMargins(renderer
->GetToolBarMargin()); 
 128 bool wxToolBar::Create(wxWindow 
*parent
, 
 133                        const wxString
& name
) 
 135     if ( !wxToolBarBase::Create(parent
, id
, pos
, size
, style
, 
 136                                 wxDefaultValidator
, name
) ) 
 141     CreateInputHandler(wxINP_HANDLER_TOOLBAR
); 
 148 wxToolBar::~wxToolBar() 
 152 // ---------------------------------------------------------------------------- 
 153 // wxToolBar tool-related methods 
 154 // ---------------------------------------------------------------------------- 
 156 wxToolBarToolBase 
*wxToolBar::FindToolForPosition(wxCoord x
, wxCoord y
) const 
 158     // check the "other" direction first: it must be inside the toolbar or we 
 159     // don't risk finding anything 
 162         if ( x 
< 0 || x 
> m_maxWidth 
) 
 165         // we always use x, even for a vertical toolbar, this makes the code 
 171         if ( y 
< 0 || y 
> m_maxHeight 
) 
 175     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 177           node 
= node
->GetNext() ) 
 179         wxToolBarToolBase 
*tool 
=  node
->GetData(); 
 180         wxRect rectTool 
= GetToolRect(tool
); 
 182         wxCoord startTool
, endTool
; 
 183         GetRectLimits(rectTool
, &startTool
, &endTool
); 
 185         if ( x 
>= startTool 
&& x 
<= endTool 
) 
 187             // don't return the separators from here, they don't accept any 
 189             return tool
->IsSeparator() ? NULL 
: tool
; 
 196 void wxToolBar::SetToolShortHelp(int id
, const wxString
& help
) 
 198     wxToolBarToolBase 
*tool 
= FindById(id
); 
 200     wxCHECK_RET( tool
, _T("SetToolShortHelp: no such tool") ); 
 202     tool
->SetShortHelp(help
); 
 205 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos
), 
 206                              wxToolBarToolBase 
* WXUNUSED(tool
)) 
 208     // recalculate the toolbar geometry before redrawing it the next time 
 209     m_needsLayout 
= TRUE
; 
 211     // and ensure that we indeed are going to redraw 
 217 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos
), 
 218                              wxToolBarToolBase 
* WXUNUSED(tool
)) 
 221     m_needsLayout 
= TRUE
; 
 228 void wxToolBar::DoEnableTool(wxToolBarToolBase 
*tool
, bool enable
) 
 230     // created disabled-state bitmap on demand 
 231     if ( !enable 
&& !tool
->GetDisabledBitmap().Ok() ) 
 233         wxImage 
image( tool
->GetNormalBitmap() ); 
 235         // TODO: don't hardcode 180 
 236         unsigned char bg_red 
= 180; 
 237         unsigned char bg_green 
= 180; 
 238         unsigned char bg_blue 
= 180; 
 240         unsigned char mask_red 
= image
.GetMaskRed(); 
 241         unsigned char mask_green 
= image
.GetMaskGreen(); 
 242         unsigned char mask_blue 
= image
.GetMaskBlue(); 
 244         bool has_mask 
= image
.HasMask(); 
 247         for (y 
= 0; y 
< image
.GetHeight(); y
++) 
 249             for (x 
= 0; x 
< image
.GetWidth(); x
++) 
 251                 unsigned char red 
= image
.GetRed(x
,y
); 
 252                 unsigned char green 
= image
.GetGreen(x
,y
); 
 253                 unsigned char blue 
= image
.GetBlue(x
,y
); 
 254                 if (!has_mask 
|| red 
!= mask_red 
|| green 
!= mask_green 
|| blue 
!= mask_blue
) 
 256                     red 
= (((wxInt32
) red  
- bg_red
) >> 1) + bg_red
; 
 257                     green 
= (((wxInt32
) green  
- bg_green
) >> 1) + bg_green
; 
 258                     blue 
= (((wxInt32
) blue  
- bg_blue
) >> 1) + bg_blue
; 
 259                     image
.SetRGB( x
, y
, red
, green
, blue 
); 
 264         for (y 
= 0; y 
< image
.GetHeight(); y
++) 
 266             for (x 
= y 
% 2; x 
< image
.GetWidth(); x 
+= 2) 
 268                 unsigned char red 
= image
.GetRed(x
,y
); 
 269                 unsigned char green 
= image
.GetGreen(x
,y
); 
 270                 unsigned char blue 
= image
.GetBlue(x
,y
); 
 271                 if (!has_mask 
|| red 
!= mask_red 
|| green 
!= mask_green 
|| blue 
!= mask_blue
) 
 273                     red 
= (((wxInt32
) red  
- bg_red
) >> 1) + bg_red
; 
 274                     green 
= (((wxInt32
) green  
- bg_green
) >> 1) + bg_green
; 
 275                     blue 
= (((wxInt32
) blue  
- bg_blue
) >> 1) + bg_blue
; 
 276                     image
.SetRGB( x
, y
, red
, green
, blue 
); 
 281         tool
->SetDisabledBitmap( image
.ConvertToBitmap() ); 
 287 void wxToolBar::DoToggleTool(wxToolBarToolBase 
*tool
, bool WXUNUSED(toggle
)) 
 289     // note that if we're called the tool did change state (the base class 
 290     // checks for it), so it's not necessary to check for this again here 
 294 void wxToolBar::DoSetToggle(wxToolBarToolBase 
*tool
, bool WXUNUSED(toggle
)) 
 299 wxToolBarToolBase 
*wxToolBar::CreateTool(int id
, 
 300                                          const wxBitmap
& bitmap1
, 
 301                                          const wxBitmap
& bitmap2
, 
 303                                          wxObject 
*clientData
, 
 304                                          const wxString
& shortHelpString
, 
 305                                          const wxString
& longHelpString
) 
 307     return new wxToolBarTool( this, id
, bitmap1
, bitmap2
, toggle
, 
 308                               clientData
, shortHelpString
, longHelpString
); 
 311 wxToolBarToolBase 
*wxToolBar::CreateTool(wxControl 
*control
) 
 313     wxFAIL_MSG( wxT("Toolbar doesn't support controls yet (TODO)") ); 
 318 // ---------------------------------------------------------------------------- 
 319 // wxToolBar geometry 
 320 // ---------------------------------------------------------------------------- 
 322 wxRect 
wxToolBar::GetToolRect(wxToolBarToolBase 
*toolBase
) const 
 324     const wxToolBarTool 
*tool 
= (wxToolBarTool 
*)toolBase
; 
 328     wxCHECK_MSG( tool
, rect
, _T("GetToolRect: NULL tool") ); 
 330     // ensure that we always have the valid tool position 
 333         wxConstCast(this, wxToolBar
)->DoLayout(); 
 336     rect
.x 
= tool
->m_x 
- m_xMargin
; 
 337     rect
.y 
= tool
->m_y 
- m_yMargin
; 
 341         rect
.width 
= m_defaultWidth
; 
 342         rect
.height 
= tool
->IsSeparator() ? m_widthSeparator 
: m_defaultHeight
; 
 346         rect
.width 
= tool
->IsSeparator() ? m_widthSeparator 
: m_defaultWidth
; 
 347         rect
.height 
= m_defaultHeight
; 
 350     rect
.width 
+= 2*m_xMargin
; 
 351     rect
.height 
+= 2*m_yMargin
; 
 356 bool wxToolBar::Realize() 
 358     if ( !wxToolBarBase::Realize() ) 
 361     m_needsLayout 
= TRUE
; 
 364     SetBestSize(wxDefaultSize
); 
 369 void wxToolBar::DoLayout() 
 371     wxASSERT_MSG( m_needsLayout
, _T("why are we called?") ); 
 373     m_needsLayout 
= FALSE
; 
 375     wxCoord x 
= m_xMargin
, 
 378     const wxCoord widthTool 
= IsVertical() ? m_defaultHeight 
: m_defaultWidth
; 
 379     wxCoord margin 
= IsVertical() ? m_xMargin 
: m_yMargin
, 
 380            *pCur 
= IsVertical() ? &y 
: &x
; 
 382     // calculate the positions of all elements 
 383     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 385           node 
= node
->GetNext() ) 
 387         wxToolBarTool 
*tool 
= (wxToolBarTool 
*) node
->GetData(); 
 392         *pCur 
+= (tool
->IsSeparator() ? m_widthSeparator 
: widthTool
) + margin
; 
 395     // calculate the total toolbar size 
 396     wxCoord xMin 
= m_defaultWidth 
+ 2*m_xMargin
, 
 397             yMin 
= m_defaultHeight 
+ 2*m_yMargin
; 
 399     m_maxWidth 
= x 
< xMin 
? xMin 
: x
; 
 400     m_maxHeight 
= y 
< yMin 
? yMin 
: y
; 
 403 wxSize 
wxToolBar::DoGetBestClientSize() const 
 405     return wxSize(m_maxWidth
, m_maxHeight
); 
 408 // ---------------------------------------------------------------------------- 
 410 // ---------------------------------------------------------------------------- 
 412 void wxToolBar::RefreshTool(wxToolBarToolBase 
*tool
) 
 414     RefreshRect(GetToolRect(tool
)); 
 417 void wxToolBar::GetRectLimits(const wxRect
& rect
, 
 421     wxCHECK_RET( start 
&& end
, _T("NULL pointer in GetRectLimits") ); 
 425         *start 
= rect
.GetTop(); 
 426         *end 
= rect
.GetBottom(); 
 430         *start 
= rect
.GetLeft(); 
 431         *end 
= rect
.GetRight(); 
 435 void wxToolBar::DoDraw(wxControlRenderer 
*renderer
) 
 437     // prepare the variables used below 
 438     wxDC
& dc 
= renderer
->GetDC(); 
 439     wxRenderer 
*rend 
= renderer
->GetRenderer(); 
 440     // dc.SetFont(GetFont()); -- uncomment when we support labels 
 442     // draw the border separating us from the menubar (if there is no menubar 
 443     // we probably shouldn't draw it?) 
 446         rend
->DrawHorizontalLine(dc
, 0, 0, GetClientSize().x
); 
 449     // get the update rect and its limits depending on the orientation 
 450     wxRect rectUpdate 
= GetUpdateClientRect(); 
 452     GetRectLimits(rectUpdate
, &start
, &end
); 
 454     // and redraw all the tools intersecting it 
 455     for ( wxToolBarToolsList::Node 
*node 
= m_tools
.GetFirst(); 
 457           node 
= node
->GetNext() ) 
 459         wxToolBarToolBase 
*tool 
= node
->GetData(); 
 460         wxRect rectTool 
= GetToolRect(tool
); 
 461         wxCoord startTool
, endTool
; 
 462         GetRectLimits(rectTool
, &startTool
, &endTool
); 
 464         if ( endTool 
< start 
) 
 466             // we're still to the left of the area to redraw 
 470         if ( startTool 
> end 
) 
 472             // we're beyond the area to redraw, nothing left to do 
 476         // deal with the flags 
 479         if ( tool
->IsEnabled() ) 
 481             // the toolbars without wxTB_FLAT don't react to the mouse hovering 
 482             if ( HasFlag(wxTB_FLAT
) && (tool 
== m_toolCurrent
) ) 
 483                 flags 
|= wxCONTROL_CURRENT
; 
 485         else // disabled tool 
 487             flags 
|= wxCONTROL_DISABLED
; 
 490         if ( tool 
== m_toolPressed 
) 
 491             flags 
|= wxCONTROL_FOCUSED
; 
 493         if ( ((wxToolBarTool 
*)tool
)->IsPressed() ) 
 494             flags 
|= wxCONTROL_PRESSED
; 
 498         if ( !tool
->IsSeparator() ) 
 500             label 
= tool
->GetLabel(); 
 501             bitmap 
= tool
->GetBitmap(); 
 503         //else: leave both the label and the bitmap invalid to draw a separator 
 505         rend
->DrawToolBarButton(dc
, label
, bitmap
, rectTool
, flags
); 
 509 // ---------------------------------------------------------------------------- 
 511 // ---------------------------------------------------------------------------- 
 513 void wxToolBar::Press() 
 515     wxCHECK_RET( m_toolCurrent
, _T("no tool to press?") ); 
 517     wxLogTrace(_T("toolbar"), 
 518                _T("Button '%s' pressed."), 
 519                m_toolCurrent
->GetShortHelp().c_str()); 
 521     // this is the tool whose state is going to change 
 522     m_toolPressed 
= (wxToolBarTool 
*)m_toolCurrent
; 
 524     // we must toggle it regardless of whether it is a checkable tool or not, 
 525     // so use Invert() and not Toggle() here 
 526     m_toolPressed
->Invert(); 
 528     RefreshTool(m_toolPressed
); 
 531 void wxToolBar::Release() 
 533     wxCHECK_RET( m_toolPressed
, _T("no tool to release?") ); 
 535     wxLogTrace(_T("toolbar"), 
 536                _T("Button '%s' released."), 
 537                m_toolCurrent
->GetShortHelp().c_str()); 
 539     wxASSERT_MSG( m_toolPressed
->IsInverted(), _T("release unpressed button?") ); 
 541     m_toolPressed
->Invert(); 
 543     RefreshTool(m_toolPressed
); 
 546 void wxToolBar::Toggle() 
 548     m_toolCurrent 
= m_toolPressed
; 
 555 void wxToolBar::Click() 
 557     wxCHECK_RET( m_toolCurrent
, _T("no tool to click?") ); 
 560     if ( m_toolCurrent
->CanBeToggled() ) 
 562         m_toolCurrent
->Toggle(); 
 564         RefreshTool(m_toolCurrent
); 
 566         isToggled 
= m_toolCurrent
->IsToggled(); 
 568     else // simple non-checkable tool 
 573     OnLeftClick(m_toolCurrent
->GetId(), isToggled
); 
 576 bool wxToolBar::PerformAction(const wxControlAction
& action
, 
 578                               const wxString
& strArg
) 
 580     if ( action 
== wxACTION_TOOLBAR_TOGGLE 
) 
 582     else if ( action 
== wxACTION_TOOLBAR_PRESS 
) 
 584     else if ( action 
== wxACTION_TOOLBAR_RELEASE 
) 
 586     else if ( action 
== wxACTION_TOOLBAR_CLICK 
) 
 588     else if ( action 
== wxACTION_TOOLBAR_ENTER 
) 
 590         wxToolBarToolBase 
*toolCurrentOld 
= m_toolCurrent
; 
 591         m_toolCurrent 
= FindById((int)numArg
); 
 593         if ( m_toolCurrent 
!= toolCurrentOld 
) 
 595             // the appearance of the current tool only changes for the flat 
 597             if ( HasFlag(wxTB_FLAT
) ) 
 599                 // and only if the tool was/is enabled 
 600                 if ( toolCurrentOld 
&& toolCurrentOld
->IsEnabled() ) 
 601                     RefreshTool(toolCurrentOld
); 
 605                     if ( m_toolCurrent
->IsEnabled() ) 
 606                         RefreshTool(m_toolCurrent
); 
 610                     wxFAIL_MSG( _T("no current tool in wxACTION_TOOLBAR_ENTER?") ); 
 615     else if ( action 
== wxACTION_TOOLBAR_LEAVE 
) 
 619             wxToolBarToolBase 
*toolCurrentOld 
= m_toolCurrent
; 
 620             m_toolCurrent 
= NULL
; 
 622             RefreshTool(toolCurrentOld
); 
 626         return wxControl::PerformAction(action
, numArg
, strArg
); 
 631 // ============================================================================ 
 632 // wxStdToolbarInputHandler implementation 
 633 // ============================================================================ 
 635 wxStdToolbarInputHandler::wxStdToolbarInputHandler(wxInputHandler 
*handler
) 
 636                         : wxStdButtonInputHandler(handler
) 
 640 bool wxStdToolbarInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
 641                                          const wxKeyEvent
& event
, 
 644     // TODO: when we have a current button we should allow the arrow 
 646     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
 649 bool wxStdToolbarInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
 650                                            const wxMouseEvent
& event
) 
 652     // don't let the base class press the disabled buttons but simply ignore 
 653     // all events on them 
 654     wxToolBar 
*tbar 
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
); 
 655     wxToolBarToolBase 
*tool 
= tbar
->FindToolForPosition(event
.GetX(), event
.GetY()); 
 657     if ( !tool 
|| !tool
->IsEnabled() ) 
 660     return wxStdButtonInputHandler::HandleMouse(consumer
, event
); 
 663 bool wxStdToolbarInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
 664                                                const wxMouseEvent
& event
) 
 666     if ( !wxStdButtonInputHandler::HandleMouseMove(consumer
, event
) ) 
 668         wxToolBarToolBase 
*tool
; 
 670         if ( event
.Leaving() ) 
 676             wxToolBar 
*tbar 
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
); 
 677             tool 
= tbar
->FindToolForPosition(event
.GetX(), event
.GetY()); 
 681             consumer
->PerformAction(wxACTION_TOOLBAR_ENTER
, tool
->GetId()); 
 683             consumer
->PerformAction(wxACTION_TOOLBAR_LEAVE
); 
 691 bool wxStdToolbarInputHandler::HandleFocus(wxInputConsumer 
*consumer
, 
 692                                            const wxFocusEvent
& event
) 
 694     // we shouldn't be left with a highlighted button 
 695     consumer
->PerformAction(wxACTION_TOOLBAR_LEAVE
); 
 700 bool wxStdToolbarInputHandler::HandleActivation(wxInputConsumer 
*consumer
, 
 705         consumer
->PerformAction(wxACTION_TOOLBAR_LEAVE
); 
 710 #endif // wxUSE_TOOLBAR