1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/toolbar.cpp
3 // Purpose: implementation of wxToolBar for wxUniversal
4 // Author: Robert Roebling, Vadim Zeitlin (universalization)
7 // Copyright: (c) 2001 Robert Roebling,
8 // (c) 2002 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/toolbar.h"
40 #include "wx/univ/renderer.h"
42 // ----------------------------------------------------------------------------
43 // wxStdToolbarInputHandler: translates SPACE and ENTER keys and the left mouse
44 // click into button press/release actions
45 // ----------------------------------------------------------------------------
47 class WXDLLEXPORT wxStdToolbarInputHandler
: public wxStdInputHandler
50 wxStdToolbarInputHandler(wxInputHandler
*inphand
);
52 virtual bool HandleKey(wxInputConsumer
*consumer
,
53 const wxKeyEvent
& event
,
55 virtual bool HandleMouse(wxInputConsumer
*consumer
,
56 const wxMouseEvent
& event
);
57 virtual bool HandleMouseMove(wxInputConsumer
*consumer
, const wxMouseEvent
& event
);
58 virtual bool HandleFocus(wxInputConsumer
*consumer
, const wxFocusEvent
& event
);
59 virtual bool HandleActivation(wxInputConsumer
*consumer
, bool activated
);
62 wxWindow
*m_winCapture
;
63 wxToolBarToolBase
*m_toolCapture
;
64 wxToolBarToolBase
*m_toolLast
;
67 // ----------------------------------------------------------------------------
69 // ----------------------------------------------------------------------------
71 // value meaning that m_widthSeparator is not initialized
72 static const wxCoord INVALID_WIDTH
= wxDefaultCoord
;
74 // ----------------------------------------------------------------------------
75 // wxToolBarTool: our implementation of wxToolBarToolBase
76 // ----------------------------------------------------------------------------
78 class WXDLLEXPORT wxToolBarTool
: public wxToolBarToolBase
81 wxToolBarTool(wxToolBar
*tbar
,
83 const wxString
& label
,
84 const wxBitmap
& bmpNormal
,
85 const wxBitmap
& bmpDisabled
,
88 const wxString
& shortHelp
,
89 const wxString
& longHelp
)
90 : wxToolBarToolBase(tbar
, id
, label
, bmpNormal
, bmpDisabled
, kind
,
91 clientData
, shortHelp
, longHelp
)
100 m_isInverted
= false;
102 // mouse not here yet
103 m_underMouse
= false;
106 wxToolBarTool(wxToolBar
*tbar
, wxControl
*control
, const wxString
& label
)
107 : wxToolBarToolBase(tbar
, control
, label
)
111 m_y
= wxDefaultCoord
;
116 m_isInverted
= false;
118 // mouse not here yet
119 m_underMouse
= false;
122 // is this tool pressed, even temporarily? (this is different from being
123 // permanently toggled which is what IsToggled() returns)
124 bool IsPressed() const
125 { return CanBeToggled() ? IsToggled() != m_isInverted
: m_isInverted
; }
127 // are we temporarily pressed/unpressed?
128 bool IsInverted() const { return m_isInverted
; }
130 // press the tool temporarily by inverting its toggle state
131 void Invert() { m_isInverted
= !m_isInverted
; }
134 void SetUnderMouse( bool under
= true ) { m_underMouse
= under
; }
135 bool IsUnderMouse() { return m_underMouse
; }
138 // the tool position (for controls)
145 // true if the tool is pressed
148 // true if the tool is under the mouse
152 // ============================================================================
153 // wxToolBar implementation
154 // ============================================================================
156 IMPLEMENT_DYNAMIC_CLASS(wxToolBar
, wxControl
)
158 // ----------------------------------------------------------------------------
159 // wxToolBar creation
160 // ----------------------------------------------------------------------------
162 void wxToolBar::Init()
165 m_needsLayout
= false;
167 // unknown widths for the tools and separators
168 m_widthSeparator
= INVALID_WIDTH
;
173 wxRenderer
*renderer
= GetRenderer();
175 SetToolBitmapSize(renderer
->GetToolBarButtonSize(&m_widthSeparator
));
176 SetMargins(renderer
->GetToolBarMargin());
179 bool wxToolBar::Create(wxWindow
*parent
,
184 const wxString
& name
)
186 if ( !wxToolBarBase::Create(parent
, id
, pos
, size
, style
,
187 wxDefaultValidator
, name
) )
194 CreateInputHandler(wxINP_HANDLER_TOOLBAR
);
196 SetInitialSize(size
);
201 wxToolBar::~wxToolBar()
203 // Make sure the toolbar is removed from the parent.
207 void wxToolBar::SetMargins(int x
, int y
)
209 // This required for similar visual effects under
210 // native platforms and wxUniv.
211 wxToolBarBase::SetMargins( x
+ 3, y
+ 3 );
214 // ----------------------------------------------------------------------------
215 // wxToolBar tool-related methods
216 // ----------------------------------------------------------------------------
218 wxToolBarToolBase
*wxToolBar::FindToolForPosition(wxCoord x
, wxCoord y
) const
220 // check the "other" direction first: it must be inside the toolbar or we
221 // don't risk finding anything
224 if ( x
< 0 || x
> m_maxWidth
)
227 // we always use x, even for a vertical toolbar, this makes the code
233 if ( y
< 0 || y
> m_maxHeight
)
237 for ( wxToolBarToolsList::compatibility_iterator node
= m_tools
.GetFirst();
239 node
= node
->GetNext() )
241 wxToolBarToolBase
*tool
= node
->GetData();
242 wxRect rectTool
= GetToolRect(tool
);
244 wxCoord startTool
, endTool
;
245 GetRectLimits(rectTool
, &startTool
, &endTool
);
247 if ( x
>= startTool
&& x
<= endTool
)
249 // don't return the separators from here, they don't accept any
251 return tool
->IsSeparator() ? NULL
: tool
;
258 void wxToolBar::SetToolShortHelp(int id
, const wxString
& help
)
260 wxToolBarToolBase
*tool
= FindById(id
);
262 wxCHECK_RET( tool
, wxT("SetToolShortHelp: no such tool") );
264 tool
->SetShortHelp(help
);
267 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos
),
268 wxToolBarToolBase
* WXUNUSED(tool
))
270 // recalculate the toolbar geometry before redrawing it the next time
271 m_needsLayout
= true;
273 // and ensure that we indeed are going to redraw
279 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos
),
280 wxToolBarToolBase
* WXUNUSED(tool
))
283 m_needsLayout
= true;
290 void wxToolBar::DoEnableTool(wxToolBarToolBase
*tool
, bool enable
)
293 // created disabled-state bitmap on demand
294 if ( !enable
&& !tool
->GetDisabledBitmap().IsOk() )
296 wxImage
image(tool
->GetNormalBitmap().ConvertToImage());
298 tool
->SetDisabledBitmap(image
.ConvertToGreyscale());
300 #endif // wxUSE_IMAGE
305 void wxToolBar::DoToggleTool(wxToolBarToolBase
*tool
, bool WXUNUSED(toggle
))
307 // note that if we're called the tool did change state (the base class
308 // checks for it), so it's not necessary to check for this again here
312 void wxToolBar::DoSetToggle(wxToolBarToolBase
*tool
, bool WXUNUSED(toggle
))
317 wxToolBarToolBase
*wxToolBar::CreateTool(int id
,
318 const wxString
& label
,
319 const wxBitmap
& bmpNormal
,
320 const wxBitmap
& bmpDisabled
,
322 wxObject
*clientData
,
323 const wxString
& shortHelp
,
324 const wxString
& longHelp
)
326 return new wxToolBarTool(this, id
, label
, bmpNormal
, bmpDisabled
, kind
,
327 clientData
, shortHelp
, longHelp
);
331 wxToolBar::CreateTool(wxControl
*control
, const wxString
& label
)
333 return new wxToolBarTool(this, control
, label
);
336 // ----------------------------------------------------------------------------
337 // wxToolBar geometry
338 // ----------------------------------------------------------------------------
340 wxRect
wxToolBar::GetToolRect(wxToolBarToolBase
*toolBase
) const
342 const wxToolBarTool
*tool
= (wxToolBarTool
*)toolBase
;
346 wxCHECK_MSG( tool
, rect
, wxT("GetToolRect: NULL tool") );
348 // ensure that we always have the valid tool position
351 wxConstCast(this, wxToolBar
)->DoLayout();
354 rect
.x
= tool
->m_x
- m_xMargin
;
355 rect
.y
= tool
->m_y
- m_yMargin
;
359 if (tool
->IsButton())
361 if(!HasFlag(wxTB_TEXT
))
363 rect
.width
= m_defaultWidth
;
364 rect
.height
= m_defaultHeight
;
368 rect
.width
= m_defaultWidth
+
369 GetFont().GetPointSize() * tool
->GetLabel().length();
370 rect
.height
= m_defaultHeight
;
373 else if (tool
->IsSeparator())
375 rect
.width
= m_defaultWidth
;
376 rect
.height
= m_widthSeparator
;
380 rect
.width
= tool
->m_width
;
381 rect
.height
= tool
->m_height
;
386 if (tool
->IsButton())
388 if(!HasFlag(wxTB_TEXT
))
390 rect
.width
= m_defaultWidth
;
391 rect
.height
= m_defaultHeight
;
395 rect
.width
= m_defaultWidth
+
396 GetFont().GetPointSize() * tool
->GetLabel().length();
397 rect
.height
= m_defaultHeight
;
400 else if (tool
->IsSeparator())
402 rect
.width
= m_widthSeparator
;
403 rect
.height
= m_defaultHeight
;
407 rect
.width
= tool
->m_width
;
408 rect
.height
= tool
->m_height
;
412 rect
.width
+= 2*m_xMargin
;
413 rect
.height
+= 2*m_yMargin
;
418 bool wxToolBar::Realize()
420 if ( !wxToolBarBase::Realize() )
423 m_needsLayout
= true;
426 // the first item in the radio group is checked by default to be consistent
427 // with wxGTK and the menu radio items
428 int radioGroupCount
= 0;
430 for ( wxToolBarToolsList::compatibility_iterator node
= m_tools
.GetFirst();
432 node
= node
->GetNext() )
434 wxToolBarTool
*tool
= (wxToolBarTool
*) node
->GetData();
436 if ( !tool
->IsButton() || tool
->GetKind() != wxITEM_RADIO
)
442 bool toggle
= !radioGroupCount
++;
443 if ( tool
->Toggle(toggle
) )
445 DoToggleTool(tool
, toggle
);
449 SetInitialSize(wxDefaultSize
);
454 void wxToolBar::SetWindowStyleFlag( long style
)
456 wxToolBarBase::SetWindowStyleFlag(style
);
458 m_needsLayout
= true;
463 void wxToolBar::DoLayout()
465 wxASSERT_MSG( m_needsLayout
, wxT("why are we called?") );
467 m_needsLayout
= false;
469 wxCoord x
= m_xMargin
,
472 wxCoord widthTool
= 0, maxWidthTool
= 0;
473 wxCoord heightTool
= 0, maxHeightTool
= 0;
474 wxCoord margin
= IsVertical() ? m_xMargin
: m_yMargin
;
475 wxCoord
*pCur
= IsVertical() ? &y
: &x
;
477 // calculate the positions of all elements
478 for ( wxToolBarToolsList::compatibility_iterator node
= m_tools
.GetFirst();
480 node
= node
->GetNext() )
482 wxToolBarTool
*tool
= (wxToolBarTool
*) node
->GetData();
487 // TODO ugly number fiddling
488 if (tool
->IsButton())
492 widthTool
= m_defaultHeight
;
493 heightTool
= m_defaultWidth
;
494 if(HasFlag(wxTB_TEXT
))
495 heightTool
+= GetFont().GetPointSize() * tool
->GetLabel().length();
499 widthTool
= m_defaultWidth
;
500 if(HasFlag(wxTB_TEXT
))
501 widthTool
+= GetFont().GetPointSize() * tool
->GetLabel().length();
503 heightTool
= m_defaultHeight
;
506 if(widthTool
> maxWidthTool
) // Record max width of tool
508 maxWidthTool
= widthTool
;
511 if(heightTool
> maxHeightTool
) // Record max width of tool
513 maxHeightTool
= heightTool
;
518 else if (tool
->IsSeparator())
520 *pCur
+= m_widthSeparator
;
522 else if (!IsVertical()) // horizontal control
524 wxControl
*control
= tool
->GetControl();
525 wxSize size
= control
->GetSize();
526 tool
->m_y
+= (m_defaultHeight
- size
.y
)/2;
527 tool
->m_width
= size
.x
;
528 tool
->m_height
= size
.y
;
530 *pCur
+= tool
->m_width
;
535 // calculate the total toolbar size
538 if(!HasFlag(wxTB_TEXT
))
540 xMin
= m_defaultWidth
+ 2*m_xMargin
;
541 yMin
= m_defaultHeight
+ 2*m_yMargin
;
547 xMin
= heightTool
+ 2*m_xMargin
;
548 yMin
= widthTool
+ 2*m_xMargin
;
552 xMin
= maxWidthTool
+ 2*m_xMargin
;
553 yMin
= heightTool
+ 2*m_xMargin
;
557 m_maxWidth
= x
< xMin
? xMin
: x
;
558 m_maxHeight
= y
< yMin
? yMin
: y
;
561 wxSize
wxToolBar::DoGetBestClientSize() const
563 return wxSize(m_maxWidth
, m_maxHeight
);
566 void wxToolBar::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
568 int old_width
, old_height
;
569 GetSize(&old_width
, &old_height
);
571 wxToolBarBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
573 // Correct width and height if needed.
574 if ( width
== wxDefaultCoord
|| height
== wxDefaultCoord
)
576 int tmp_width
, tmp_height
;
577 GetSize(&tmp_width
, &tmp_height
);
579 if ( width
== wxDefaultCoord
)
581 if ( height
== wxDefaultCoord
)
585 // We must refresh the frame size when the toolbar changes size
586 // otherwise the toolbar can be shown incorrectly
587 if ( old_width
!= width
|| old_height
!= height
)
589 SendSizeEventToParent();
593 // ----------------------------------------------------------------------------
595 // ----------------------------------------------------------------------------
597 void wxToolBar::RefreshTool(wxToolBarToolBase
*tool
)
599 RefreshRect(GetToolRect(tool
));
602 void wxToolBar::GetRectLimits(const wxRect
& rect
,
606 wxCHECK_RET( start
&& end
, wxT("NULL pointer in GetRectLimits") );
610 *start
= rect
.GetTop();
611 *end
= rect
.GetBottom();
615 *start
= rect
.GetLeft();
616 *end
= rect
.GetRight();
620 void wxToolBar::DoDraw(wxControlRenderer
*renderer
)
622 // prepare the variables used below
623 wxDC
& dc
= renderer
->GetDC();
624 wxRenderer
*rend
= renderer
->GetRenderer();
625 dc
.SetFont(GetFont());
627 // draw the border separating us from the menubar (if there is no menubar
628 // we probably shouldn't draw it?)
631 rend
->DrawHorizontalLine(dc
, 0, 0, GetClientSize().x
);
634 // get the update rect and its limits depending on the orientation
635 wxRect rectUpdate
= GetUpdateClientRect();
637 GetRectLimits(rectUpdate
, &start
, &end
);
639 // and redraw all the tools intersecting it
640 for ( wxToolBarToolsList::compatibility_iterator node
= m_tools
.GetFirst();
642 node
= node
->GetNext() )
644 wxToolBarTool
*tool
= (wxToolBarTool
*) node
->GetData();
645 wxRect rectTool
= GetToolRect(tool
);
646 wxCoord startTool
, endTool
;
647 GetRectLimits(rectTool
, &startTool
, &endTool
);
649 if ( endTool
< start
)
651 // we're still to the left of the area to redraw
655 if ( startTool
> end
)
657 // we're beyond the area to redraw, nothing left to do
661 if (tool
->IsSeparator() && !HasFlag(wxTB_FLAT
))
663 // Draw separators only in flat mode
667 // deal with the flags
670 if ( tool
->IsEnabled() )
672 // The toolbars without wxTB_FLAT don't react to the mouse hovering
673 if ( !HasFlag(wxTB_FLAT
) || tool
->IsUnderMouse() )
674 flags
|= wxCONTROL_CURRENT
;
676 else // disabled tool
678 flags
|= wxCONTROL_DISABLED
;
681 //if ( tool == m_toolCaptured )
682 // flags |= wxCONTROL_FOCUSED;
684 if ( tool
->IsPressed() )
685 flags
= wxCONTROL_PRESSED
;
689 if ( !tool
->IsSeparator() )
691 label
= tool
->GetLabel();
692 bitmap
= tool
->GetBitmap();
694 if ( !bitmap
.IsOk() )
696 // it's better not to draw anything than to assert inside
697 // drawing code as this results in an almost guaranteed crash
698 // as we're likely to be called by a paint event handler and so
699 // the assert is going to be triggered again and again and ...
703 //else: leave both the label and the bitmap invalid to draw a separator
705 if ( !tool
->IsControl() )
707 int tbStyle
= HasFlag(wxTB_VERTICAL
) ? wxTB_VERTICAL
: wxTB_HORIZONTAL
;
708 if ( HasFlag(wxTB_TEXT
) )
709 tbStyle
|= wxTB_TEXT
;
711 rend
->DrawToolBarButton(dc
, label
, bitmap
, rectTool
, flags
,
712 tool
->GetStyle(), tbStyle
);
716 wxControl
*control
= tool
->GetControl();
717 control
->Move(tool
->m_x
, tool
->m_y
);
722 // ----------------------------------------------------------------------------
724 // ----------------------------------------------------------------------------
726 bool wxToolBar::PerformAction(const wxControlAction
& action
,
728 const wxString
& strArg
)
730 wxToolBarTool
*tool
= (wxToolBarTool
*) FindById(numArg
);
734 if ( action
== wxACTION_TOOLBAR_TOGGLE
)
736 PerformAction( wxACTION_BUTTON_RELEASE
, numArg
);
738 PerformAction( wxACTION_BUTTON_CLICK
, numArg
);
740 // Set mouse leave toolbar button range (If still in the range,
741 // toolbar button would get focus again
742 PerformAction( wxACTION_TOOLBAR_LEAVE
, numArg
);
744 else if ( action
== wxACTION_TOOLBAR_PRESS
)
746 wxLogTrace(wxT("toolbar"), wxT("Button '%s' pressed."), tool
->GetShortHelp().c_str());
752 else if ( action
== wxACTION_TOOLBAR_RELEASE
)
754 wxLogTrace(wxT("toolbar"), wxT("Button '%s' released."), tool
->GetShortHelp().c_str());
756 wxASSERT_MSG( tool
->IsInverted(), wxT("release unpressed button?") );
758 if(tool
->IsInverted())
765 else if ( action
== wxACTION_TOOLBAR_CLICK
)
768 if ( tool
->CanBeToggled() )
770 if ( tool
->IsButton() && tool
->GetKind() == wxITEM_RADIO
)
772 UnToggleRadioGroup(tool
);
782 isToggled
= tool
->IsToggled();
784 else // simple non-checkable tool
788 OnLeftClick( tool
->GetId(), isToggled
);
790 else if ( action
== wxACTION_TOOLBAR_ENTER
)
792 wxCHECK_MSG( tool
, false, wxT("no tool to enter?") );
794 if ( HasFlag(wxTB_FLAT
) && tool
->IsEnabled() )
796 tool
->SetUnderMouse( true );
798 if ( !tool
->IsToggled() )
802 else if ( action
== wxACTION_TOOLBAR_LEAVE
)
804 wxCHECK_MSG( tool
, false, wxT("no tool to leave?") );
806 if ( HasFlag(wxTB_FLAT
) && tool
->IsEnabled() )
808 tool
->SetUnderMouse( false );
810 if ( !tool
->IsToggled() )
815 return wxControl::PerformAction(action
, numArg
, strArg
);
821 wxInputHandler
*wxToolBar::GetStdInputHandler(wxInputHandler
*handlerDef
)
823 static wxStdToolbarInputHandler
s_handler(handlerDef
);
828 // ============================================================================
829 // wxStdToolbarInputHandler implementation
830 // ============================================================================
832 wxStdToolbarInputHandler::wxStdToolbarInputHandler(wxInputHandler
*handler
)
833 : wxStdInputHandler(handler
)
836 m_toolCapture
= NULL
;
840 bool wxStdToolbarInputHandler::HandleKey(wxInputConsumer
*consumer
,
841 const wxKeyEvent
& event
,
844 // TODO: when we have a current button we should allow the arrow
846 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
849 bool wxStdToolbarInputHandler::HandleMouse(wxInputConsumer
*consumer
,
850 const wxMouseEvent
& event
)
852 wxToolBar
*tbar
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
);
853 wxToolBarToolBase
*tool
= tbar
->FindToolForPosition(event
.GetX(), event
.GetY());
855 if ( event
.Button(1) )
858 if ( event
.LeftDown() || event
.LeftDClick() )
860 if ( !tool
|| !tool
->IsEnabled() )
864 m_winCapture
->CaptureMouse();
866 m_toolCapture
= tool
;
868 consumer
->PerformAction( wxACTION_BUTTON_PRESS
, tool
->GetId() );
872 else if ( event
.LeftUp() )
876 m_winCapture
->ReleaseMouse();
882 if ( tool
== m_toolCapture
)
883 consumer
->PerformAction( wxACTION_BUTTON_TOGGLE
, m_toolCapture
->GetId() );
885 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() );
888 m_toolCapture
= NULL
;
892 //else: don't do anything special about the double click
895 return wxStdInputHandler::HandleMouse(consumer
, event
);
898 bool wxStdToolbarInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
899 const wxMouseEvent
& event
)
901 if ( !wxStdInputHandler::HandleMouseMove(consumer
, event
) )
903 wxToolBar
*tbar
= wxStaticCast(consumer
->GetInputWindow(), wxToolBar
);
906 if ( event
.Leaving() )
908 // We cannot possibly be over a tool when
909 // leaving the toolbar
914 tool
= (wxToolBarTool
*) tbar
->FindToolForPosition( event
.GetX(), event
.GetY() );
919 // During capture we only care of the captured tool
920 if (tool
&& (tool
!= m_toolCapture
))
923 if (tool
== m_toolLast
)
927 consumer
->PerformAction( wxACTION_BUTTON_PRESS
, m_toolCapture
->GetId() );
929 consumer
->PerformAction( wxACTION_BUTTON_RELEASE
, m_toolCapture
->GetId() );
935 if (tool
== m_toolLast
)
940 // Leave old tool if any
941 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolLast
->GetId() );
946 // Enter new tool if any
947 consumer
->PerformAction( wxACTION_TOOLBAR_ENTER
, tool
->GetId() );
959 bool wxStdToolbarInputHandler::HandleFocus(wxInputConsumer
*consumer
,
960 const wxFocusEvent
& WXUNUSED(event
))
964 // We shouldn't be left with a highlighted button
965 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() );
971 bool wxStdToolbarInputHandler::HandleActivation(wxInputConsumer
*consumer
,
974 if (m_toolCapture
&& !activated
)
976 // We shouldn't be left with a highlighted button
977 consumer
->PerformAction( wxACTION_TOOLBAR_LEAVE
, m_toolCapture
->GetId() );
983 #endif // wxUSE_TOOLBAR