Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / common / tbarbase.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/tbarbase.cpp
3 // Purpose: wxToolBarBase implementation
4 // Author: Julian Smart
5 // Modified by: VZ at 11.12.99 (wxScrollableToolBar split off)
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_TOOLBAR
27
28 #include "wx/toolbar.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/control.h"
32 #include "wx/frame.h"
33 #include "wx/settings.h"
34 #if WXWIN_COMPATIBILITY_2_8
35 #include "wx/image.h"
36 #endif // WXWIN_COMPATIBILITY_2_8
37 #include "wx/menu.h"
38 #endif
39
40 extern WXDLLEXPORT_DATA(const char) wxToolBarNameStr[] = "toolbar";
41
42 // ----------------------------------------------------------------------------
43 // wxWidgets macros
44 // ----------------------------------------------------------------------------
45
46 BEGIN_EVENT_TABLE(wxToolBarBase, wxControl)
47 END_EVENT_TABLE()
48
49 #include "wx/listimpl.cpp"
50
51 WX_DEFINE_LIST(wxToolBarToolsList)
52
53 // ============================================================================
54 // implementation
55 // ============================================================================
56
57 // ----------------------------------------------------------------------------
58 // wxToolBarToolBase
59 // ----------------------------------------------------------------------------
60
61 IMPLEMENT_DYNAMIC_CLASS(wxToolBarToolBase, wxObject)
62
63 wxToolBarToolBase::~wxToolBarToolBase()
64 {
65 #if wxUSE_MENUS
66 delete m_dropdownMenu;
67 #endif
68
69 if ( IsControl() )
70 GetControl()->Destroy();
71 }
72
73
74 bool wxToolBarToolBase::Enable(bool enable)
75 {
76 if ( m_enabled == enable )
77 return false;
78
79 m_enabled = enable;
80
81 return true;
82 }
83
84 bool wxToolBarToolBase::Toggle(bool toggle)
85 {
86 wxASSERT_MSG( CanBeToggled(), wxT("can't toggle this tool") );
87
88 if ( m_toggled == toggle )
89 return false;
90
91 m_toggled = toggle;
92
93 return true;
94 }
95
96 bool wxToolBarToolBase::SetToggle(bool toggle)
97 {
98 wxItemKind kind = toggle ? wxITEM_CHECK : wxITEM_NORMAL;
99 if ( m_kind == kind )
100 return false;
101
102 m_kind = kind;
103
104 return true;
105 }
106
107 bool wxToolBarToolBase::SetShortHelp(const wxString& help)
108 {
109 if ( m_shortHelpString == help )
110 return false;
111
112 m_shortHelpString = help;
113
114 return true;
115 }
116
117 bool wxToolBarToolBase::SetLongHelp(const wxString& help)
118 {
119 if ( m_longHelpString == help )
120 return false;
121
122 m_longHelpString = help;
123
124 return true;
125 }
126
127
128 #if wxUSE_MENUS
129 void wxToolBarToolBase::SetDropdownMenu(wxMenu* menu)
130 {
131 delete m_dropdownMenu;
132 m_dropdownMenu = menu;
133 }
134 #endif
135
136
137 // ----------------------------------------------------------------------------
138 // wxToolBarBase adding/deleting items
139 // ----------------------------------------------------------------------------
140
141 wxToolBarBase::wxToolBarBase()
142 {
143 // the list owns the pointers
144 m_xMargin = m_yMargin = 0;
145 m_maxRows = m_maxCols = 0;
146 m_toolPacking = m_toolSeparation = 0;
147 m_defaultWidth = 16;
148 m_defaultHeight = 15;
149 }
150
151 void wxToolBarBase::FixupStyle()
152 {
153 if ( !HasFlag(wxTB_TOP | wxTB_LEFT | wxTB_RIGHT | wxTB_BOTTOM) )
154 {
155 // this is the default
156 m_windowStyle |= wxTB_TOP;
157 }
158 }
159
160 wxToolBarToolBase *wxToolBarBase::DoAddTool(int toolid,
161 const wxString& label,
162 const wxBitmap& bitmap,
163 const wxBitmap& bmpDisabled,
164 wxItemKind kind,
165 const wxString& shortHelp,
166 const wxString& longHelp,
167 wxObject *clientData,
168 wxCoord WXUNUSED(xPos),
169 wxCoord WXUNUSED(yPos))
170 {
171 InvalidateBestSize();
172 return InsertTool(GetToolsCount(), toolid, label, bitmap, bmpDisabled,
173 kind, shortHelp, longHelp, clientData);
174 }
175
176 wxToolBarToolBase *wxToolBarBase::InsertTool(size_t pos,
177 int toolid,
178 const wxString& label,
179 const wxBitmap& bitmap,
180 const wxBitmap& bmpDisabled,
181 wxItemKind kind,
182 const wxString& shortHelp,
183 const wxString& longHelp,
184 wxObject *clientData)
185 {
186 wxCHECK_MSG( pos <= GetToolsCount(), NULL,
187 wxT("invalid position in wxToolBar::InsertTool()") );
188
189 return DoInsertNewTool(pos, CreateTool(toolid, label, bitmap, bmpDisabled, kind,
190 clientData, shortHelp, longHelp));
191 }
192
193 wxToolBarToolBase *wxToolBarBase::AddTool(wxToolBarToolBase *tool)
194 {
195 return InsertTool(GetToolsCount(), tool);
196 }
197
198 wxToolBarToolBase *
199 wxToolBarBase::InsertTool(size_t pos, wxToolBarToolBase *tool)
200 {
201 wxCHECK_MSG( pos <= GetToolsCount(), NULL,
202 wxT("invalid position in wxToolBar::InsertTool()") );
203
204 if ( !tool || !DoInsertTool(pos, tool) )
205 {
206 return NULL;
207 }
208
209 m_tools.Insert(pos, tool);
210 tool->Attach(this);
211
212 return tool;
213 }
214
215 wxToolBarToolBase *
216 wxToolBarBase::AddControl(wxControl *control, const wxString& label)
217 {
218 return InsertControl(GetToolsCount(), control, label);
219 }
220
221 wxToolBarToolBase *
222 wxToolBarBase::InsertControl(size_t pos,
223 wxControl *control,
224 const wxString& label)
225 {
226 wxCHECK_MSG( control, NULL,
227 wxT("toolbar: can't insert NULL control") );
228
229 wxCHECK_MSG( control->GetParent() == this, NULL,
230 wxT("control must have toolbar as parent") );
231
232 return DoInsertNewTool(pos, CreateTool(control, label));
233 }
234
235 wxControl *wxToolBarBase::FindControl( int toolid )
236 {
237 for ( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
238 node;
239 node = node->GetNext() )
240 {
241 const wxToolBarToolBase * const tool = node->GetData();
242 if ( tool->IsControl() )
243 {
244 wxControl * const control = tool->GetControl();
245
246 if ( !control )
247 {
248 wxFAIL_MSG( wxT("NULL control in toolbar?") );
249 }
250 else if ( control->GetId() == toolid )
251 {
252 // found
253 return control;
254 }
255 }
256 }
257
258 return NULL;
259 }
260
261 wxToolBarToolBase *wxToolBarBase::AddSeparator()
262 {
263 return InsertSeparator(GetToolsCount());
264 }
265
266 wxToolBarToolBase *wxToolBarBase::InsertSeparator(size_t pos)
267 {
268 return DoInsertNewTool(pos, CreateSeparator());
269 }
270
271 wxToolBarToolBase *wxToolBarBase::AddStretchableSpace()
272 {
273 return InsertStretchableSpace(GetToolsCount());
274 }
275
276 wxToolBarToolBase *wxToolBarBase::InsertStretchableSpace(size_t pos)
277 {
278 wxToolBarToolBase * const tool = CreateSeparator();
279 if ( tool )
280 {
281 // this is a hack but we know that all the current implementations
282 // don't really use the tool when it's created, they will do it
283 // InsertTool() at earliest and maybe even in Realize() much later
284 //
285 // so we can create the tool as a plain separator and mark it as being
286 // a stretchable space later
287 tool->MakeStretchable();
288 }
289
290 return DoInsertNewTool(pos, tool);
291 }
292
293 wxToolBarToolBase *wxToolBarBase::RemoveTool(int toolid)
294 {
295 size_t pos = 0;
296 wxToolBarToolsList::compatibility_iterator node;
297 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
298 {
299 if ( node->GetData()->GetId() == toolid )
300 break;
301
302 pos++;
303 }
304
305 if ( !node )
306 {
307 // don't give any error messages - sometimes we might call RemoveTool()
308 // without knowing whether the tool is or not in the toolbar
309 return NULL;
310 }
311
312 wxToolBarToolBase *tool = node->GetData();
313 wxCHECK_MSG( tool, NULL, "NULL tool in the tools list?" );
314
315 if ( !DoDeleteTool(pos, tool) )
316 return NULL;
317
318 m_tools.Erase(node);
319
320 tool->Detach();
321
322 return tool;
323 }
324
325 bool wxToolBarBase::DeleteToolByPos(size_t pos)
326 {
327 wxCHECK_MSG( pos < GetToolsCount(), false,
328 wxT("invalid position in wxToolBar::DeleteToolByPos()") );
329
330 wxToolBarToolsList::compatibility_iterator node = m_tools.Item(pos);
331
332 if ( !DoDeleteTool(pos, node->GetData()) )
333 {
334 return false;
335 }
336
337 delete node->GetData();
338 m_tools.Erase(node);
339
340 return true;
341 }
342
343 bool wxToolBarBase::DeleteTool(int toolid)
344 {
345 size_t pos = 0;
346 wxToolBarToolsList::compatibility_iterator node;
347 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
348 {
349 if ( node->GetData()->GetId() == toolid )
350 break;
351
352 pos++;
353 }
354
355 if ( !node || !DoDeleteTool(pos, node->GetData()) )
356 {
357 return false;
358 }
359
360 delete node->GetData();
361 m_tools.Erase(node);
362
363 return true;
364 }
365
366 wxToolBarToolBase *wxToolBarBase::FindById(int toolid) const
367 {
368 wxToolBarToolBase *tool = NULL;
369
370 for ( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
371 node;
372 node = node->GetNext() )
373 {
374 tool = node->GetData();
375 if ( tool->GetId() == toolid )
376 {
377 // found
378 break;
379 }
380
381 tool = NULL;
382 }
383
384 return tool;
385 }
386
387 void wxToolBarBase::UnToggleRadioGroup(wxToolBarToolBase *tool)
388 {
389 wxCHECK_RET( tool, wxT("NULL tool in wxToolBarTool::UnToggleRadioGroup") );
390
391 if ( !tool->IsButton() || tool->GetKind() != wxITEM_RADIO )
392 return;
393
394 wxToolBarToolsList::compatibility_iterator node = m_tools.Find(tool);
395 wxCHECK_RET( node, wxT("invalid tool in wxToolBarTool::UnToggleRadioGroup") );
396
397 wxToolBarToolsList::compatibility_iterator nodeNext = node->GetNext();
398 while ( nodeNext )
399 {
400 wxToolBarToolBase *toolNext = nodeNext->GetData();
401
402 if ( !toolNext->IsButton() || toolNext->GetKind() != wxITEM_RADIO )
403 break;
404
405 if ( toolNext->Toggle(false) )
406 {
407 DoToggleTool(toolNext, false);
408 }
409
410 nodeNext = nodeNext->GetNext();
411 }
412
413 wxToolBarToolsList::compatibility_iterator nodePrev = node->GetPrevious();
414 while ( nodePrev )
415 {
416 wxToolBarToolBase *toolNext = nodePrev->GetData();
417
418 if ( !toolNext->IsButton() || toolNext->GetKind() != wxITEM_RADIO )
419 break;
420
421 if ( toolNext->Toggle(false) )
422 {
423 DoToggleTool(toolNext, false);
424 }
425
426 nodePrev = nodePrev->GetPrevious();
427 }
428 }
429
430 void wxToolBarBase::ClearTools()
431 {
432 while ( GetToolsCount() )
433 {
434 DeleteToolByPos(0);
435 }
436 }
437
438 void wxToolBarBase::AdjustToolBitmapSize()
439 {
440 const wxSize sizeOrig(m_defaultWidth, m_defaultHeight);
441
442 wxSize sizeActual(sizeOrig);
443
444 for ( wxToolBarToolsList::const_iterator i = m_tools.begin();
445 i != m_tools.end();
446 ++i )
447 {
448 const wxBitmap& bmp = (*i)->GetNormalBitmap();
449 if ( bmp.IsOk() )
450 sizeActual.IncTo(bmp.GetSize());
451 }
452
453 if ( sizeActual != sizeOrig )
454 SetToolBitmapSize(sizeActual);
455 }
456
457 bool wxToolBarBase::Realize()
458 {
459 // check if we have anything to do
460 if ( m_tools.empty() )
461 return false;
462
463 // make sure tool size is large enough for all bitmaps to fit in
464 AdjustToolBitmapSize();
465
466 return true;
467 }
468
469 wxToolBarBase::~wxToolBarBase()
470 {
471 WX_CLEAR_LIST(wxToolBarToolsList, m_tools);
472
473 // notify the frame that it doesn't have a tool bar any longer to avoid
474 // dangling pointers
475 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
476 if ( frame && frame->GetToolBar() == this )
477 {
478 frame->SetToolBar(NULL);
479 }
480 }
481
482 // ----------------------------------------------------------------------------
483 // wxToolBarBase tools state
484 // ----------------------------------------------------------------------------
485
486 void wxToolBarBase::EnableTool(int toolid, bool enable)
487 {
488 wxToolBarToolBase *tool = FindById(toolid);
489 if ( tool )
490 {
491 if ( tool->Enable(enable) )
492 {
493 DoEnableTool(tool, enable);
494 }
495 }
496 }
497
498 void wxToolBarBase::ToggleTool(int toolid, bool toggle)
499 {
500 wxToolBarToolBase *tool = FindById(toolid);
501 if ( tool && tool->CanBeToggled() )
502 {
503 if ( tool->Toggle(toggle) )
504 {
505 UnToggleRadioGroup(tool);
506 DoToggleTool(tool, toggle);
507 }
508 }
509 }
510
511 void wxToolBarBase::SetToggle(int toolid, bool toggle)
512 {
513 wxToolBarToolBase *tool = FindById(toolid);
514 if ( tool )
515 {
516 if ( tool->SetToggle(toggle) )
517 {
518 DoSetToggle(tool, toggle);
519 }
520 }
521 }
522
523 void wxToolBarBase::SetToolShortHelp(int toolid, const wxString& help)
524 {
525 wxToolBarToolBase *tool = FindById(toolid);
526 if ( tool )
527 {
528 (void)tool->SetShortHelp(help);
529 }
530 }
531
532 void wxToolBarBase::SetToolLongHelp(int toolid, const wxString& help)
533 {
534 wxToolBarToolBase *tool = FindById(toolid);
535 if ( tool )
536 {
537 (void)tool->SetLongHelp(help);
538 }
539 }
540
541 wxObject *wxToolBarBase::GetToolClientData(int toolid) const
542 {
543 wxToolBarToolBase *tool = FindById(toolid);
544
545 return tool ? tool->GetClientData() : NULL;
546 }
547
548 void wxToolBarBase::SetToolClientData(int toolid, wxObject *clientData)
549 {
550 wxToolBarToolBase *tool = FindById(toolid);
551
552 wxCHECK_RET( tool, wxT("no such tool in wxToolBar::SetToolClientData") );
553
554 tool->SetClientData(clientData);
555 }
556
557 int wxToolBarBase::GetToolPos(int toolid) const
558 {
559 size_t pos = 0;
560 wxToolBarToolsList::compatibility_iterator node;
561
562 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
563 {
564 if ( node->GetData()->GetId() == toolid )
565 return pos;
566
567 pos++;
568 }
569
570 return wxNOT_FOUND;
571 }
572
573 bool wxToolBarBase::GetToolState(int toolid) const
574 {
575 wxToolBarToolBase *tool = FindById(toolid);
576 wxCHECK_MSG( tool, false, wxT("no such tool") );
577
578 return tool->IsToggled();
579 }
580
581 bool wxToolBarBase::GetToolEnabled(int toolid) const
582 {
583 wxToolBarToolBase *tool = FindById(toolid);
584 wxCHECK_MSG( tool, false, wxT("no such tool") );
585
586 return tool->IsEnabled();
587 }
588
589 wxString wxToolBarBase::GetToolShortHelp(int toolid) const
590 {
591 wxToolBarToolBase *tool = FindById(toolid);
592 wxCHECK_MSG( tool, wxEmptyString, wxT("no such tool") );
593
594 return tool->GetShortHelp();
595 }
596
597 wxString wxToolBarBase::GetToolLongHelp(int toolid) const
598 {
599 wxToolBarToolBase *tool = FindById(toolid);
600 wxCHECK_MSG( tool, wxEmptyString, wxT("no such tool") );
601
602 return tool->GetLongHelp();
603 }
604
605 // ----------------------------------------------------------------------------
606 // wxToolBarBase geometry
607 // ----------------------------------------------------------------------------
608
609 void wxToolBarBase::SetMargins(int x, int y)
610 {
611 m_xMargin = x;
612 m_yMargin = y;
613 }
614
615 void wxToolBarBase::SetRows(int WXUNUSED(nRows))
616 {
617 // nothing
618 }
619
620 bool wxToolBarBase::IsVertical() const
621 {
622 return HasFlag(wxTB_LEFT | wxTB_RIGHT);
623 }
624
625
626 // ----------------------------------------------------------------------------
627 // event processing
628 // ----------------------------------------------------------------------------
629
630 // Only allow toggle if returns true
631 bool wxToolBarBase::OnLeftClick(int toolid, bool toggleDown)
632 {
633 wxCommandEvent event(wxEVT_TOOL, toolid);
634 event.SetEventObject(this);
635
636 // we use SetInt() to make wxCommandEvent::IsChecked() return toggleDown
637 event.SetInt((int)toggleDown);
638
639 // and SetExtraLong() for backwards compatibility
640 event.SetExtraLong((long)toggleDown);
641
642 // Send events to this toolbar instead (and thence up the window hierarchy)
643 HandleWindowEvent(event);
644
645 return true;
646 }
647
648 // Call when right button down.
649 void wxToolBarBase::OnRightClick(int toolid,
650 long WXUNUSED(x),
651 long WXUNUSED(y))
652 {
653 wxCommandEvent event(wxEVT_TOOL_RCLICKED, toolid);
654 event.SetEventObject(this);
655 event.SetInt(toolid);
656
657 GetEventHandler()->ProcessEvent(event);
658 }
659
660 // Called when the mouse cursor enters a tool bitmap (no button pressed).
661 // Argument is wxID_ANY if mouse is exiting the toolbar.
662 // Note that for this event, the toolid of the window is used,
663 // and the integer parameter of wxCommandEvent is used to retrieve
664 // the tool toolid.
665 void wxToolBarBase::OnMouseEnter(int toolid)
666 {
667 wxCommandEvent event(wxEVT_TOOL_ENTER, GetId());
668 event.SetEventObject(this);
669 event.SetInt(toolid);
670
671 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
672 if ( frame )
673 {
674 wxString help;
675 if ( toolid != wxID_ANY )
676 {
677 const wxToolBarToolBase * const tool = FindById(toolid);
678 if ( tool )
679 help = tool->GetLongHelp();
680 }
681
682 // call DoGiveHelp() even if help string is empty to avoid showing the
683 // help for the previously selected tool when another one is selected
684 frame->DoGiveHelp(help, toolid != wxID_ANY);
685 }
686
687 (void)GetEventHandler()->ProcessEvent(event);
688 }
689
690 // ----------------------------------------------------------------------------
691 // UI updates
692 // ----------------------------------------------------------------------------
693
694 // Do the toolbar button updates (check for EVT_UPDATE_UI handlers)
695 void wxToolBarBase::UpdateWindowUI(long flags)
696 {
697 wxWindowBase::UpdateWindowUI(flags);
698
699 // don't waste time updating state of tools in a hidden toolbar
700 if ( !IsShown() )
701 return;
702
703 wxEvtHandler* evtHandler = GetEventHandler() ;
704
705 for ( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
706 node;
707 node = node->GetNext() )
708 {
709 wxToolBarToolBase * const tool = node->GetData();
710 if ( tool->IsSeparator() )
711 continue;
712
713 int toolid = tool->GetId();
714
715 wxUpdateUIEvent event(toolid);
716 event.SetEventObject(this);
717
718 if ( evtHandler->ProcessEvent(event) )
719 {
720 if ( event.GetSetEnabled() )
721 EnableTool(toolid, event.GetEnabled());
722 if ( event.GetSetChecked() )
723 ToggleTool(toolid, event.GetChecked());
724 #if 0
725 if ( event.GetSetText() )
726 // Set tooltip?
727 #endif // 0
728 }
729 }
730 }
731
732 #if wxUSE_MENUS
733 bool wxToolBarBase::SetDropdownMenu(int toolid, wxMenu* menu)
734 {
735 wxToolBarToolBase * const tool = FindById(toolid);
736 wxCHECK_MSG( tool, false, wxT("invalid tool toolid") );
737
738 wxCHECK_MSG( tool->GetKind() == wxITEM_DROPDOWN, false,
739 wxT("menu can be only associated with drop down tools") );
740
741 tool->SetDropdownMenu(menu);
742
743 return true;
744 }
745 #endif
746
747 #if WXWIN_COMPATIBILITY_2_8
748
749 bool wxCreateGreyedImage(const wxImage& in, wxImage& out)
750 {
751 #if wxUSE_IMAGE
752 out = in.ConvertToGreyscale();
753 if ( out.IsOk() )
754 return true;
755 #endif // wxUSE_IMAGE
756 return false;
757 }
758
759 #endif // WXWIN_COMPATIBILITY_2_8
760
761 #endif // wxUSE_TOOLBAR