wxUniv toolbar compilation fixes
[wxWidgets.git] / src / univ / toolbar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/toolbar.cpp
3 // Purpose: implementation of wxToolBar for wxUniversal
4 // Author: Robert Roebling, Vadim Zeitlin (universalization)
5 // Modified by:
6 // Created: 20.02.02
7 // Id: $Id$
8 // Copyright: (c) 2001 Robert Roebling,
9 // (c) 2002 SciTech Software, Inc. (www.scitechsoft.com)
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #ifdef __GNUG__
22 #pragma implementation "univtoolbar.h"
23 #endif
24
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 #if wxUSE_TOOLBAR
33
34 #ifndef WX_PRECOMP
35 #include "wx/utils.h"
36 #include "wx/app.h"
37 #endif
38
39 #include "wx/univ/renderer.h"
40
41 #include "wx/toolbar.h"
42 #include "wx/image.h"
43
44 // ----------------------------------------------------------------------------
45 // constants
46 // ----------------------------------------------------------------------------
47
48 // value meaning that m_widthSeparator is not initialized
49 static const wxCoord INVALID_WIDTH = -1;
50
51 // ----------------------------------------------------------------------------
52 // wxToolBarTool: our implementation of wxToolBarToolBase
53 // ----------------------------------------------------------------------------
54
55 class WXDLLEXPORT wxToolBarTool : public wxToolBarToolBase
56 {
57 public:
58 wxToolBarTool(wxToolBar *tbar,
59 int id,
60 const wxString& label,
61 const wxBitmap& bmpNormal,
62 const wxBitmap& bmpDisabled,
63 wxItemKind kind,
64 wxObject *clientData,
65 const wxString& shortHelp,
66 const wxString& longHelp)
67 : wxToolBarToolBase(tbar, id, label, bmpNormal, bmpDisabled, kind,
68 clientData, shortHelp, longHelp)
69 {
70 // no position yet
71 m_x =
72 m_y = -1;
73
74 // not pressed yet
75 m_isInverted = FALSE;
76 }
77
78 // is this tool pressed, even temporarily? (this is different from being
79 // permanently toggled which is what IsToggled() returns)
80 bool IsPressed() const
81 { return CanBeToggled() ? IsToggled() != m_isInverted : m_isInverted; }
82
83 // are we temporarily pressed/unpressed?
84 bool IsInverted() const { return m_isInverted; }
85
86 // press the tool temporarily by inverting its toggle state
87 void Invert() { m_isInverted = !m_isInverted; }
88
89 public:
90 // the tool position (the size is known by the toolbar itself)
91 int m_x,
92 m_y;
93
94 private:
95 // TRUE if the tool is pressed
96 bool m_isInverted;
97 };
98
99 // ============================================================================
100 // wxToolBar implementation
101 // ============================================================================
102
103 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl);
104
105 // ----------------------------------------------------------------------------
106 // wxToolBar creation
107 // ----------------------------------------------------------------------------
108
109 void wxToolBar::Init()
110 {
111 // no tools yet
112 m_needsLayout = FALSE;
113
114 // unknown widths for the tools and separators
115 m_widthSeparator = INVALID_WIDTH;
116
117 m_maxWidth =
118 m_maxHeight = 0;
119
120 m_toolPressed = NULL;
121 m_toolCurrent = NULL;
122
123 wxRenderer *renderer = GetRenderer();
124
125 SetToolBitmapSize(renderer->GetToolBarButtonSize(&m_widthSeparator));
126 SetMargins(renderer->GetToolBarMargin());
127 }
128
129 bool wxToolBar::Create(wxWindow *parent,
130 wxWindowID id,
131 const wxPoint& pos,
132 const wxSize& size,
133 long style,
134 const wxString& name)
135 {
136 if ( !wxToolBarBase::Create(parent, id, pos, size, style,
137 wxDefaultValidator, name) )
138 {
139 return FALSE;
140 }
141
142 CreateInputHandler(wxINP_HANDLER_TOOLBAR);
143
144 SetBestSize(size);
145
146 return TRUE;
147 }
148
149 wxToolBar::~wxToolBar()
150 {
151 }
152
153 // ----------------------------------------------------------------------------
154 // wxToolBar tool-related methods
155 // ----------------------------------------------------------------------------
156
157 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
158 {
159 // check the "other" direction first: it must be inside the toolbar or we
160 // don't risk finding anything
161 if ( IsVertical() )
162 {
163 if ( x < 0 || x > m_maxWidth )
164 return NULL;
165
166 // we always use x, even for a vertical toolbar, this makes the code
167 // below simpler
168 x = y;
169 }
170 else // horizontal
171 {
172 if ( y < 0 || y > m_maxHeight )
173 return NULL;
174 }
175
176 for ( wxToolBarToolsList::Node *node = m_tools.GetFirst();
177 node;
178 node = node->GetNext() )
179 {
180 wxToolBarToolBase *tool = node->GetData();
181 wxRect rectTool = GetToolRect(tool);
182
183 wxCoord startTool, endTool;
184 GetRectLimits(rectTool, &startTool, &endTool);
185
186 if ( x >= startTool && x <= endTool )
187 {
188 // don't return the separators from here, they don't accept any
189 // input anyhow
190 return tool->IsSeparator() ? NULL : tool;
191 }
192 }
193
194 return NULL;
195 }
196
197 void wxToolBar::SetToolShortHelp(int id, const wxString& help)
198 {
199 wxToolBarToolBase *tool = FindById(id);
200
201 wxCHECK_RET( tool, _T("SetToolShortHelp: no such tool") );
202
203 tool->SetShortHelp(help);
204 }
205
206 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos),
207 wxToolBarToolBase * WXUNUSED(tool))
208 {
209 // recalculate the toolbar geometry before redrawing it the next time
210 m_needsLayout = TRUE;
211
212 // and ensure that we indeed are going to redraw
213 Refresh();
214
215 return TRUE;
216 }
217
218 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos),
219 wxToolBarToolBase * WXUNUSED(tool))
220 {
221 // as above
222 m_needsLayout = TRUE;
223
224 Refresh();
225
226 return TRUE;
227 }
228
229 void wxToolBar::DoEnableTool(wxToolBarToolBase *tool, bool enable)
230 {
231 // created disabled-state bitmap on demand
232 if ( !enable && !tool->GetDisabledBitmap().Ok() )
233 {
234 wxImage image( tool->GetNormalBitmap().ConvertToImage() );
235
236 // TODO: don't hardcode 180
237 unsigned char bg_red = 180;
238 unsigned char bg_green = 180;
239 unsigned char bg_blue = 180;
240
241 unsigned char mask_red = image.GetMaskRed();
242 unsigned char mask_green = image.GetMaskGreen();
243 unsigned char mask_blue = image.GetMaskBlue();
244
245 bool has_mask = image.HasMask();
246
247 int x,y;
248 for (y = 0; y < image.GetHeight(); y++)
249 {
250 for (x = 0; x < image.GetWidth(); x++)
251 {
252 unsigned char red = image.GetRed(x,y);
253 unsigned char green = image.GetGreen(x,y);
254 unsigned char blue = image.GetBlue(x,y);
255 if (!has_mask || red != mask_red || green != mask_green || blue != mask_blue)
256 {
257 red = (((wxInt32) red - bg_red) >> 1) + bg_red;
258 green = (((wxInt32) green - bg_green) >> 1) + bg_green;
259 blue = (((wxInt32) blue - bg_blue) >> 1) + bg_blue;
260 image.SetRGB( x, y, red, green, blue );
261 }
262 }
263 }
264
265 for (y = 0; y < image.GetHeight(); y++)
266 {
267 for (x = y % 2; x < image.GetWidth(); x += 2)
268 {
269 unsigned char red = image.GetRed(x,y);
270 unsigned char green = image.GetGreen(x,y);
271 unsigned char blue = image.GetBlue(x,y);
272 if (!has_mask || red != mask_red || green != mask_green || blue != mask_blue)
273 {
274 red = (((wxInt32) red - bg_red) >> 1) + bg_red;
275 green = (((wxInt32) green - bg_green) >> 1) + bg_green;
276 blue = (((wxInt32) blue - bg_blue) >> 1) + bg_blue;
277 image.SetRGB( x, y, red, green, blue );
278 }
279 }
280 }
281
282 tool->SetDisabledBitmap(image);
283 }
284
285 RefreshTool(tool);
286 }
287
288 void wxToolBar::DoToggleTool(wxToolBarToolBase *tool, bool WXUNUSED(toggle))
289 {
290 // note that if we're called the tool did change state (the base class
291 // checks for it), so it's not necessary to check for this again here
292 RefreshTool(tool);
293 }
294
295 void wxToolBar::DoSetToggle(wxToolBarToolBase *tool, bool WXUNUSED(toggle))
296 {
297 RefreshTool(tool);
298 }
299
300 wxToolBarToolBase *wxToolBar::CreateTool(int id,
301 const wxString& label,
302 const wxBitmap& bmpNormal,
303 const wxBitmap& bmpDisabled,
304 wxItemKind kind,
305 wxObject *clientData,
306 const wxString& shortHelp,
307 const wxString& longHelp)
308 {
309 return new wxToolBarTool(this, id, label, bmpNormal, bmpDisabled, kind,
310 clientData, shortHelp, longHelp);
311 }
312
313 wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
314 {
315 wxFAIL_MSG( wxT("Toolbar doesn't support controls yet (TODO)") );
316
317 return NULL;
318 }
319
320 // ----------------------------------------------------------------------------
321 // wxToolBar geometry
322 // ----------------------------------------------------------------------------
323
324 wxRect wxToolBar::GetToolRect(wxToolBarToolBase *toolBase) const
325 {
326 const wxToolBarTool *tool = (wxToolBarTool *)toolBase;
327
328 wxRect rect;
329
330 wxCHECK_MSG( tool, rect, _T("GetToolRect: NULL tool") );
331
332 // ensure that we always have the valid tool position
333 if ( m_needsLayout )
334 {
335 wxConstCast(this, wxToolBar)->DoLayout();
336 }
337
338 rect.x = tool->m_x - m_xMargin;
339 rect.y = tool->m_y - m_yMargin;
340
341 if ( IsVertical() )
342 {
343 rect.width = m_defaultWidth;
344 rect.height = tool->IsSeparator() ? m_widthSeparator : m_defaultHeight;
345 }
346 else // horizontal
347 {
348 rect.width = tool->IsSeparator() ? m_widthSeparator : m_defaultWidth;
349 rect.height = m_defaultHeight;
350 }
351
352 rect.width += 2*m_xMargin;
353 rect.height += 2*m_yMargin;
354
355 return rect;
356 }
357
358 bool wxToolBar::Realize()
359 {
360 if ( !wxToolBarBase::Realize() )
361 return FALSE;
362
363 m_needsLayout = TRUE;
364 DoLayout();
365
366 SetBestSize(wxDefaultSize);
367
368 return TRUE;
369 }
370
371 void wxToolBar::DoLayout()
372 {
373 wxASSERT_MSG( m_needsLayout, _T("why are we called?") );
374
375 m_needsLayout = FALSE;
376
377 wxCoord x = m_xMargin,
378 y = m_yMargin;
379
380 const wxCoord widthTool = IsVertical() ? m_defaultHeight : m_defaultWidth;
381 wxCoord margin = IsVertical() ? m_xMargin : m_yMargin,
382 *pCur = IsVertical() ? &y : &x;
383
384 // calculate the positions of all elements
385 for ( wxToolBarToolsList::Node *node = m_tools.GetFirst();
386 node;
387 node = node->GetNext() )
388 {
389 wxToolBarTool *tool = (wxToolBarTool *) node->GetData();
390
391 tool->m_x = x;
392 tool->m_y = y;
393
394 *pCur += (tool->IsSeparator() ? m_widthSeparator : widthTool) + margin;
395 }
396
397 // calculate the total toolbar size
398 wxCoord xMin = m_defaultWidth + 2*m_xMargin,
399 yMin = m_defaultHeight + 2*m_yMargin;
400
401 m_maxWidth = x < xMin ? xMin : x;
402 m_maxHeight = y < yMin ? yMin : y;
403 }
404
405 wxSize wxToolBar::DoGetBestClientSize() const
406 {
407 return wxSize(m_maxWidth, m_maxHeight);
408 }
409
410 // ----------------------------------------------------------------------------
411 // wxToolBar drawing
412 // ----------------------------------------------------------------------------
413
414 void wxToolBar::RefreshTool(wxToolBarToolBase *tool)
415 {
416 RefreshRect(GetToolRect(tool));
417 }
418
419 void wxToolBar::GetRectLimits(const wxRect& rect,
420 wxCoord *start,
421 wxCoord *end) const
422 {
423 wxCHECK_RET( start && end, _T("NULL pointer in GetRectLimits") );
424
425 if ( IsVertical() )
426 {
427 *start = rect.GetTop();
428 *end = rect.GetBottom();
429 }
430 else // horizontal
431 {
432 *start = rect.GetLeft();
433 *end = rect.GetRight();
434 }
435 }
436
437 void wxToolBar::DoDraw(wxControlRenderer *renderer)
438 {
439 // prepare the variables used below
440 wxDC& dc = renderer->GetDC();
441 wxRenderer *rend = renderer->GetRenderer();
442 // dc.SetFont(GetFont()); -- uncomment when we support labels
443
444 // draw the border separating us from the menubar (if there is no menubar
445 // we probably shouldn't draw it?)
446 if ( !IsVertical() )
447 {
448 rend->DrawHorizontalLine(dc, 0, 0, GetClientSize().x);
449 }
450
451 // get the update rect and its limits depending on the orientation
452 wxRect rectUpdate = GetUpdateClientRect();
453 wxCoord start, end;
454 GetRectLimits(rectUpdate, &start, &end);
455
456 // and redraw all the tools intersecting it
457 for ( wxToolBarToolsList::Node *node = m_tools.GetFirst();
458 node;
459 node = node->GetNext() )
460 {
461 wxToolBarToolBase *tool = node->GetData();
462 wxRect rectTool = GetToolRect(tool);
463 wxCoord startTool, endTool;
464 GetRectLimits(rectTool, &startTool, &endTool);
465
466 if ( endTool < start )
467 {
468 // we're still to the left of the area to redraw
469 continue;
470 }
471
472 if ( startTool > end )
473 {
474 // we're beyond the area to redraw, nothing left to do
475 break;
476 }
477
478 // deal with the flags
479 int flags = 0;
480
481 if ( tool->IsEnabled() )
482 {
483 // the toolbars without wxTB_FLAT don't react to the mouse hovering
484 if ( HasFlag(wxTB_FLAT) && (tool == m_toolCurrent) )
485 flags |= wxCONTROL_CURRENT;
486 }
487 else // disabled tool
488 {
489 flags |= wxCONTROL_DISABLED;
490 }
491
492 if ( tool == m_toolPressed )
493 flags |= wxCONTROL_FOCUSED;
494
495 if ( ((wxToolBarTool *)tool)->IsPressed() )
496 flags |= wxCONTROL_PRESSED;
497
498 wxString label;
499 wxBitmap bitmap;
500 if ( !tool->IsSeparator() )
501 {
502 label = tool->GetLabel();
503 bitmap = tool->GetBitmap();
504 }
505 //else: leave both the label and the bitmap invalid to draw a separator
506
507 rend->DrawToolBarButton(dc, label, bitmap, rectTool, flags);
508 }
509 }
510
511 // ----------------------------------------------------------------------------
512 // wxToolBar actions
513 // ----------------------------------------------------------------------------
514
515 void wxToolBar::Press()
516 {
517 wxCHECK_RET( m_toolCurrent, _T("no tool to press?") );
518
519 wxLogTrace(_T("toolbar"),
520 _T("Button '%s' pressed."),
521 m_toolCurrent->GetShortHelp().c_str());
522
523 // this is the tool whose state is going to change
524 m_toolPressed = (wxToolBarTool *)m_toolCurrent;
525
526 // we must toggle it regardless of whether it is a checkable tool or not,
527 // so use Invert() and not Toggle() here
528 m_toolPressed->Invert();
529
530 RefreshTool(m_toolPressed);
531 }
532
533 void wxToolBar::Release()
534 {
535 wxCHECK_RET( m_toolPressed, _T("no tool to release?") );
536
537 wxLogTrace(_T("toolbar"),
538 _T("Button '%s' released."),
539 m_toolCurrent->GetShortHelp().c_str());
540
541 wxASSERT_MSG( m_toolPressed->IsInverted(), _T("release unpressed button?") );
542
543 m_toolPressed->Invert();
544
545 RefreshTool(m_toolPressed);
546 }
547
548 void wxToolBar::Toggle()
549 {
550 m_toolCurrent = m_toolPressed;
551
552 Release();
553
554 Click();
555 }
556
557 void wxToolBar::Click()
558 {
559 wxCHECK_RET( m_toolCurrent, _T("no tool to click?") );
560
561 bool isToggled;
562 if ( m_toolCurrent->CanBeToggled() )
563 {
564 m_toolCurrent->Toggle();
565
566 RefreshTool(m_toolCurrent);
567
568 isToggled = m_toolCurrent->IsToggled();
569 }
570 else // simple non-checkable tool
571 {
572 isToggled = FALSE;
573 }
574
575 OnLeftClick(m_toolCurrent->GetId(), isToggled);
576 }
577
578 bool wxToolBar::PerformAction(const wxControlAction& action,
579 long numArg,
580 const wxString& strArg)
581 {
582 if ( action == wxACTION_TOOLBAR_TOGGLE )
583 Toggle();
584 else if ( action == wxACTION_TOOLBAR_PRESS )
585 Press();
586 else if ( action == wxACTION_TOOLBAR_RELEASE )
587 Release();
588 else if ( action == wxACTION_TOOLBAR_CLICK )
589 Click();
590 else if ( action == wxACTION_TOOLBAR_ENTER )
591 {
592 wxToolBarToolBase *toolCurrentOld = m_toolCurrent;
593 m_toolCurrent = FindById((int)numArg);
594
595 if ( m_toolCurrent != toolCurrentOld )
596 {
597 // the appearance of the current tool only changes for the flat
598 // toolbars
599 if ( HasFlag(wxTB_FLAT) )
600 {
601 // and only if the tool was/is enabled
602 if ( toolCurrentOld && toolCurrentOld->IsEnabled() )
603 RefreshTool(toolCurrentOld);
604
605 if ( m_toolCurrent )
606 {
607 if ( m_toolCurrent->IsEnabled() )
608 RefreshTool(m_toolCurrent);
609 }
610 else
611 {
612 wxFAIL_MSG( _T("no current tool in wxACTION_TOOLBAR_ENTER?") );
613 }
614 }
615 }
616 }
617 else if ( action == wxACTION_TOOLBAR_LEAVE )
618 {
619 if ( m_toolCurrent )
620 {
621 wxToolBarToolBase *toolCurrentOld = m_toolCurrent;
622 m_toolCurrent = NULL;
623
624 RefreshTool(toolCurrentOld);
625 }
626 }
627 else
628 return wxControl::PerformAction(action, numArg, strArg);
629
630 return TRUE;
631 }
632
633 // ============================================================================
634 // wxStdToolbarInputHandler implementation
635 // ============================================================================
636
637 wxStdToolbarInputHandler::wxStdToolbarInputHandler(wxInputHandler *handler)
638 : wxStdButtonInputHandler(handler)
639 {
640 }
641
642 bool wxStdToolbarInputHandler::HandleKey(wxInputConsumer *consumer,
643 const wxKeyEvent& event,
644 bool pressed)
645 {
646 // TODO: when we have a current button we should allow the arrow
647 // keys to move it
648 return wxStdInputHandler::HandleKey(consumer, event, pressed);
649 }
650
651 bool wxStdToolbarInputHandler::HandleMouse(wxInputConsumer *consumer,
652 const wxMouseEvent& event)
653 {
654 // don't let the base class press the disabled buttons but simply ignore
655 // all events on them
656 wxToolBar *tbar = wxStaticCast(consumer->GetInputWindow(), wxToolBar);
657 wxToolBarToolBase *tool = tbar->FindToolForPosition(event.GetX(), event.GetY());
658
659 if ( !tool || !tool->IsEnabled() )
660 return TRUE;
661
662 return wxStdButtonInputHandler::HandleMouse(consumer, event);
663 }
664
665 bool wxStdToolbarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
666 const wxMouseEvent& event)
667 {
668 if ( !wxStdButtonInputHandler::HandleMouseMove(consumer, event) )
669 {
670 wxToolBarToolBase *tool;
671
672 if ( event.Leaving() )
673 {
674 tool = NULL;
675 }
676 else
677 {
678 wxToolBar *tbar = wxStaticCast(consumer->GetInputWindow(), wxToolBar);
679 tool = tbar->FindToolForPosition(event.GetX(), event.GetY());
680 }
681
682 if ( tool )
683 consumer->PerformAction(wxACTION_TOOLBAR_ENTER, tool->GetId());
684 else
685 consumer->PerformAction(wxACTION_TOOLBAR_LEAVE);
686
687 return TRUE;
688 }
689
690 return FALSE;
691 }
692
693 bool wxStdToolbarInputHandler::HandleFocus(wxInputConsumer *consumer,
694 const wxFocusEvent& event)
695 {
696 // we shouldn't be left with a highlighted button
697 consumer->PerformAction(wxACTION_TOOLBAR_LEAVE);
698
699 return TRUE;
700 }
701
702 bool wxStdToolbarInputHandler::HandleActivation(wxInputConsumer *consumer,
703 bool activated)
704 {
705 // as above
706 if ( !activated )
707 consumer->PerformAction(wxACTION_TOOLBAR_LEAVE);
708
709 return TRUE;
710 }
711
712 #endif // wxUSE_TOOLBAR
713