Fix wxRibbon compilation when wxUSE_TOOLTIPS==0.
[wxWidgets.git] / src / ribbon / buttonbar.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/buttonbar.cpp
3 // Purpose: Ribbon control similar to a tool bar
4 // Author: Peter Cawley
5 // Modified by:
6 // Created: 2009-07-01
7 // RCS-ID: $Id$
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_RIBBON
19
20 #include "wx/ribbon/buttonbar.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/dcbuffer.h"
23
24 #ifndef WX_PRECOMP
25 #endif
26
27 #ifdef __WXMSW__
28 #include "wx/msw/private.h"
29 #endif
30
31 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBUTTON_CLICKED, wxRibbonButtonBarEvent);
32 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBUTTON_DROPDOWN_CLICKED, wxRibbonButtonBarEvent);
33
34 IMPLEMENT_DYNAMIC_CLASS(wxRibbonButtonBarEvent, wxCommandEvent)
35 IMPLEMENT_CLASS(wxRibbonButtonBar, wxRibbonControl)
36
37 BEGIN_EVENT_TABLE(wxRibbonButtonBar, wxRibbonControl)
38 EVT_ERASE_BACKGROUND(wxRibbonButtonBar::OnEraseBackground)
39 EVT_ENTER_WINDOW(wxRibbonButtonBar::OnMouseEnter)
40 EVT_LEAVE_WINDOW(wxRibbonButtonBar::OnMouseLeave)
41 EVT_MOTION(wxRibbonButtonBar::OnMouseMove)
42 EVT_PAINT(wxRibbonButtonBar::OnPaint)
43 EVT_SIZE(wxRibbonButtonBar::OnSize)
44 EVT_LEFT_DOWN(wxRibbonButtonBar::OnMouseDown)
45 EVT_LEFT_UP(wxRibbonButtonBar::OnMouseUp)
46 END_EVENT_TABLE()
47
48 class wxRibbonButtonBarButtonSizeInfo
49 {
50 public:
51 bool is_supported;
52 wxSize size;
53 wxRect normal_region;
54 wxRect dropdown_region;
55 };
56
57 class wxRibbonButtonBarButtonInstance
58 {
59 public:
60 wxPoint position;
61 wxRibbonButtonBarButtonBase* base;
62 wxRibbonButtonBarButtonState size;
63 };
64
65 class wxRibbonButtonBarButtonBase
66 {
67 public:
68 wxRibbonButtonBarButtonInstance NewInstance()
69 {
70 wxRibbonButtonBarButtonInstance i;
71 i.base = this;
72 return i;
73 }
74
75 wxRibbonButtonBarButtonState GetLargestSize()
76 {
77 if(sizes[wxRIBBON_BUTTONBAR_BUTTON_LARGE].is_supported)
78 return wxRIBBON_BUTTONBAR_BUTTON_LARGE;
79 if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported)
80 return wxRIBBON_BUTTONBAR_BUTTON_MEDIUM;
81 wxASSERT(sizes[wxRIBBON_BUTTONBAR_BUTTON_SMALL].is_supported);
82 return wxRIBBON_BUTTONBAR_BUTTON_SMALL;
83 }
84
85 bool GetSmallerSize(
86 wxRibbonButtonBarButtonState* size, int n = 1)
87 {
88 for(; n > 0; --n)
89 {
90 switch(*size)
91 {
92 case wxRIBBON_BUTTONBAR_BUTTON_LARGE:
93 if(sizes[wxRIBBON_BUTTONBAR_BUTTON_MEDIUM].is_supported)
94 {
95 *size = wxRIBBON_BUTTONBAR_BUTTON_MEDIUM;
96 break;
97 }
98 case wxRIBBON_BUTTONBAR_BUTTON_MEDIUM:
99 if(sizes[wxRIBBON_BUTTONBAR_BUTTON_SMALL].is_supported)
100 {
101 *size = wxRIBBON_BUTTONBAR_BUTTON_SMALL;
102 break;
103 }
104 case wxRIBBON_BUTTONBAR_BUTTON_SMALL:
105 default:
106 return false;
107 }
108 }
109 return true;
110 }
111
112 wxString label;
113 wxString help_string;
114 wxBitmap bitmap_large;
115 wxBitmap bitmap_large_disabled;
116 wxBitmap bitmap_small;
117 wxBitmap bitmap_small_disabled;
118 wxRibbonButtonBarButtonSizeInfo sizes[3];
119 wxObject* client_data;
120 int id;
121 wxRibbonButtonKind kind;
122 long state;
123 };
124
125 WX_DECLARE_OBJARRAY(wxRibbonButtonBarButtonInstance, wxArrayRibbonButtonBarButtonInstance);
126 #include "wx/arrimpl.cpp"
127 WX_DEFINE_OBJARRAY(wxArrayRibbonButtonBarButtonInstance)
128
129 class wxRibbonButtonBarLayout
130 {
131 public:
132 wxSize overall_size;
133 wxArrayRibbonButtonBarButtonInstance buttons;
134
135 void CalculateOverallSize()
136 {
137 overall_size = wxSize(0, 0);
138 size_t btn_count = buttons.Count();
139 size_t btn_i;
140 for(btn_i = 0; btn_i < btn_count; ++btn_i)
141 {
142 wxRibbonButtonBarButtonInstance& instance = buttons.Item(btn_i);
143 wxSize size = instance.base->sizes[instance.size].size;
144 int right = instance.position.x + size.GetWidth();
145 int bottom = instance.position.y + size.GetHeight();
146 if(right > overall_size.GetWidth())
147 {
148 overall_size.SetWidth(right);
149 }
150 if(bottom > overall_size.GetHeight())
151 {
152 overall_size.SetHeight(bottom);
153 }
154 }
155 }
156
157 wxRibbonButtonBarButtonInstance* FindSimilarInstance(
158 wxRibbonButtonBarButtonInstance* inst)
159 {
160 if(inst == NULL)
161 {
162 return NULL;
163 }
164 size_t btn_count = buttons.Count();
165 size_t btn_i;
166 for(btn_i = 0; btn_i < btn_count; ++btn_i)
167 {
168 wxRibbonButtonBarButtonInstance& instance = buttons.Item(btn_i);
169 if(instance.base == inst->base)
170 {
171 return &instance;
172 }
173 }
174 return NULL;
175 }
176 };
177
178 wxRibbonButtonBar::wxRibbonButtonBar()
179 {
180 m_layouts_valid = false;
181 CommonInit (0);
182 }
183
184 wxRibbonButtonBar::wxRibbonButtonBar(wxWindow* parent,
185 wxWindowID id,
186 const wxPoint& pos,
187 const wxSize& size,
188 long style)
189 : wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
190 {
191 m_layouts_valid = false;
192
193 CommonInit(style);
194 }
195
196 wxRibbonButtonBar::~wxRibbonButtonBar()
197 {
198 size_t count = m_buttons.GetCount();
199 size_t i;
200 for(i = 0; i < count; ++i)
201 {
202 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
203 delete button;
204 }
205 m_buttons.Clear();
206
207 count = m_layouts.GetCount();
208 for(i = 0; i < count; ++i)
209 {
210 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
211 delete layout;
212 }
213 m_layouts.Clear();
214 }
215
216 bool wxRibbonButtonBar::Create(wxWindow* parent,
217 wxWindowID id,
218 const wxPoint& pos,
219 const wxSize& size,
220 long style)
221 {
222 if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
223 {
224 return false;
225 }
226
227 CommonInit(style);
228 return true;
229 }
230
231 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::AddButton(
232 int button_id,
233 const wxString& label,
234 const wxBitmap& bitmap,
235 const wxString& help_string,
236 wxRibbonButtonKind kind)
237 {
238 return AddButton(button_id, label, bitmap, wxNullBitmap, wxNullBitmap,
239 wxNullBitmap, kind, help_string);
240 }
241
242 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::AddDropdownButton(
243 int button_id,
244 const wxString& label,
245 const wxBitmap& bitmap,
246 const wxString& help_string)
247 {
248 return AddButton(button_id, label, bitmap, help_string,
249 wxRIBBON_BUTTON_DROPDOWN);
250 }
251
252 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::AddToggleButton(
253 int button_id,
254 const wxString& label,
255 const wxBitmap& bitmap,
256 const wxString& help_string)
257 {
258 return AddButton(button_id, label, bitmap, help_string,
259 wxRIBBON_BUTTON_TOGGLE);
260 }
261
262 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::AddHybridButton(
263 int button_id,
264 const wxString& label,
265 const wxBitmap& bitmap,
266 const wxString& help_string)
267 {
268 return AddButton(button_id, label, bitmap, help_string,
269 wxRIBBON_BUTTON_HYBRID);
270 }
271
272 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::AddButton(
273 int button_id,
274 const wxString& label,
275 const wxBitmap& bitmap,
276 const wxBitmap& bitmap_small,
277 const wxBitmap& bitmap_disabled,
278 const wxBitmap& bitmap_small_disabled,
279 wxRibbonButtonKind kind,
280 const wxString& help_string,
281 wxObject* client_data)
282 {
283 return InsertButton(GetButtonCount(), button_id, label, bitmap,
284 bitmap_small, bitmap_disabled,bitmap_small_disabled, kind, help_string,
285 client_data);
286 }
287
288 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertButton(
289 size_t pos,
290 int button_id,
291 const wxString& label,
292 const wxBitmap& bitmap,
293 const wxBitmap& bitmap_small,
294 const wxBitmap& bitmap_disabled,
295 const wxBitmap& bitmap_small_disabled,
296 wxRibbonButtonKind kind,
297 const wxString& help_string,
298 wxObject* client_data)
299 {
300 wxASSERT(bitmap.IsOk() || bitmap_small.IsOk());
301 if(m_buttons.IsEmpty())
302 {
303 if(bitmap.IsOk())
304 {
305 m_bitmap_size_large = bitmap.GetSize();
306 if(!bitmap_small.IsOk())
307 {
308 m_bitmap_size_small = m_bitmap_size_large;
309 m_bitmap_size_small *= 0.5;
310 }
311 }
312 if(bitmap_small.IsOk())
313 {
314 m_bitmap_size_small = bitmap_small.GetSize();
315 if(!bitmap.IsOk())
316 {
317 m_bitmap_size_large = m_bitmap_size_small;
318 m_bitmap_size_large *= 2.0;
319 }
320 }
321 }
322
323 wxRibbonButtonBarButtonBase* base = new wxRibbonButtonBarButtonBase;
324 base->id = button_id;
325 base->label = label;
326 base->bitmap_large = bitmap;
327 if(!base->bitmap_large.IsOk())
328 {
329 base->bitmap_large = MakeResizedBitmap(base->bitmap_small,
330 m_bitmap_size_large);
331 }
332 else if(base->bitmap_large.GetSize() != m_bitmap_size_large)
333 {
334 base->bitmap_large = MakeResizedBitmap(base->bitmap_large,
335 m_bitmap_size_large);
336 }
337 base->bitmap_small = bitmap_small;
338 if(!base->bitmap_small.IsOk())
339 {
340 base->bitmap_small = MakeResizedBitmap(base->bitmap_large,
341 m_bitmap_size_small);
342 }
343 else if(base->bitmap_small.GetSize() != m_bitmap_size_small)
344 {
345 base->bitmap_small = MakeResizedBitmap(base->bitmap_small,
346 m_bitmap_size_small);
347 }
348 base->bitmap_large_disabled = bitmap_disabled;
349 if(!base->bitmap_large_disabled.IsOk())
350 {
351 base->bitmap_large_disabled = MakeDisabledBitmap(base->bitmap_large);
352 }
353 base->bitmap_small_disabled = bitmap_small_disabled;
354 if(!base->bitmap_small_disabled.IsOk())
355 {
356 base->bitmap_small_disabled = MakeDisabledBitmap(base->bitmap_small);
357 }
358 base->kind = kind;
359 base->help_string = help_string;
360 base->client_data = client_data;
361 base->state = 0;
362
363 wxClientDC temp_dc(this);
364 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_SMALL, temp_dc);
365 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, temp_dc);
366 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_LARGE, temp_dc);
367
368 m_buttons.Insert(base, pos);
369 m_layouts_valid = false;
370 return base;
371 }
372
373 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertButton(
374 size_t pos,
375 int button_id,
376 const wxString& label,
377 const wxBitmap& bitmap,
378 const wxString& help_string,
379 wxRibbonButtonKind kind)
380 {
381 return InsertButton(pos, button_id, label, bitmap, wxNullBitmap,
382 wxNullBitmap, wxNullBitmap, kind, help_string);
383 }
384
385 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertDropdownButton(
386 size_t pos,
387 int button_id,
388 const wxString& label,
389 const wxBitmap& bitmap,
390 const wxString& help_string)
391 {
392 return InsertButton(pos, button_id, label, bitmap, help_string,
393 wxRIBBON_BUTTON_DROPDOWN);
394 }
395
396 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertToggleButton(
397 size_t pos,
398 int button_id,
399 const wxString& label,
400 const wxBitmap& bitmap,
401 const wxString& help_string)
402 {
403 return InsertButton(pos, button_id, label, bitmap, help_string,
404 wxRIBBON_BUTTON_TOGGLE);
405 }
406
407 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertHybridButton(
408 size_t pos,
409 int button_id,
410 const wxString& label,
411 const wxBitmap& bitmap,
412 const wxString& help_string)
413 {
414 return InsertButton(pos, button_id, label, bitmap, help_string,
415 wxRIBBON_BUTTON_HYBRID);
416 }
417
418 void wxRibbonButtonBar::FetchButtonSizeInfo(wxRibbonButtonBarButtonBase* button,
419 wxRibbonButtonBarButtonState size, wxDC& dc)
420 {
421 wxRibbonButtonBarButtonSizeInfo& info = button->sizes[size];
422 if(m_art)
423 {
424 info.is_supported = m_art->GetButtonBarButtonSize(dc, this,
425 button->kind, size, button->label, m_bitmap_size_large,
426 m_bitmap_size_small, &info.size, &info.normal_region,
427 &info.dropdown_region);
428 }
429 else
430 info.is_supported = false;
431 }
432
433 wxBitmap wxRibbonButtonBar::MakeResizedBitmap(const wxBitmap& original, wxSize size)
434 {
435 wxImage img(original.ConvertToImage());
436 img.Rescale(size.GetWidth(), size.GetHeight(), wxIMAGE_QUALITY_HIGH);
437 return wxBitmap(img);
438 }
439
440 wxBitmap wxRibbonButtonBar::MakeDisabledBitmap(const wxBitmap& original)
441 {
442 wxImage img(original.ConvertToImage());
443 return wxBitmap(img.ConvertToGreyscale());
444 }
445
446 size_t wxRibbonButtonBar::GetButtonCount() const
447 {
448 return m_buttons.GetCount();
449 }
450
451 bool wxRibbonButtonBar::Realize()
452 {
453 if(!m_layouts_valid)
454 {
455 MakeLayouts();
456 m_layouts_valid = true;
457 }
458 return true;
459 }
460
461 void wxRibbonButtonBar::ClearButtons()
462 {
463 m_layouts_valid = false;
464 size_t count = m_buttons.GetCount();
465 size_t i;
466 for(i = 0; i < count; ++i)
467 {
468 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
469 delete button;
470 }
471 m_buttons.Clear();
472 Realize();
473 }
474
475 bool wxRibbonButtonBar::DeleteButton(int button_id)
476 {
477 size_t count = m_buttons.GetCount();
478 size_t i;
479 for(i = 0; i < count; ++i)
480 {
481 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
482 if(button->id == button_id)
483 {
484 m_layouts_valid = false;
485 m_buttons.RemoveAt(i);
486 Realize();
487 Refresh();
488 return true;
489 }
490 }
491 return false;
492 }
493
494 void wxRibbonButtonBar::EnableButton(int button_id, bool enable)
495 {
496 size_t count = m_buttons.GetCount();
497 size_t i;
498 for(i = 0; i < count; ++i)
499 {
500 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
501 if(button->id == button_id)
502 {
503 if(enable)
504 {
505 if(button->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED)
506 {
507 button->state &= ~wxRIBBON_BUTTONBAR_BUTTON_DISABLED;
508 Refresh();
509 }
510 }
511 else
512 {
513 if((button->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
514 {
515 button->state |= wxRIBBON_BUTTONBAR_BUTTON_DISABLED;
516 Refresh();
517 }
518 }
519 return;
520 }
521 }
522 }
523
524 void wxRibbonButtonBar::ToggleButton(int button_id, bool checked)
525 {
526 size_t count = m_buttons.GetCount();
527 size_t i;
528 for(i = 0; i < count; ++i)
529 {
530 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
531 if(button->id == button_id)
532 {
533 if(checked)
534 {
535 if((button->state & wxRIBBON_BUTTONBAR_BUTTON_TOGGLED) == 0)
536 {
537 button->state |= wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
538 Refresh();
539 }
540 }
541 else
542 {
543 if(button->state & wxRIBBON_BUTTONBAR_BUTTON_TOGGLED)
544 {
545 button->state &= ~wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
546 Refresh();
547 }
548 }
549 return;
550 }
551 }
552 }
553
554 void wxRibbonButtonBar::SetArtProvider(wxRibbonArtProvider* art)
555 {
556 if(art == m_art)
557 {
558 return;
559 }
560
561 wxRibbonControl::SetArtProvider(art);
562
563 wxClientDC temp_dc(this);
564 size_t btn_count = m_buttons.Count();
565 size_t btn_i;
566 for(btn_i = 0; btn_i < btn_count; ++btn_i)
567 {
568 wxRibbonButtonBarButtonBase* base = m_buttons.Item(btn_i);
569
570 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_SMALL, temp_dc);
571 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, temp_dc);
572 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_LARGE, temp_dc);
573 }
574
575 m_layouts_valid = false;
576 Realize();
577 }
578
579 bool wxRibbonButtonBar::IsSizingContinuous() const
580 {
581 return false;
582 }
583
584 wxSize wxRibbonButtonBar::DoGetNextSmallerSize(wxOrientation direction,
585 wxSize result) const
586 {
587 size_t nlayouts = m_layouts.GetCount();
588 size_t i;
589 for(i = 0; i < nlayouts; ++i)
590 {
591 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
592 wxSize size = layout->overall_size;
593 switch(direction)
594 {
595 case wxHORIZONTAL:
596 if(size.x < result.x && size.y <= result.y)
597 {
598 result.x = size.x;
599 break;
600 }
601 else
602 continue;
603 case wxVERTICAL:
604 if(size.x <= result.x && size.y < result.y)
605 {
606 result.y = size.y;
607 break;
608 }
609 else
610 continue;
611 case wxBOTH:
612 if(size.x < result.x && size.y < result.y)
613 {
614 result = size;
615 break;
616 }
617 else
618 continue;
619 }
620 break;
621 }
622 return result;
623 }
624
625 wxSize wxRibbonButtonBar::DoGetNextLargerSize(wxOrientation direction,
626 wxSize result) const
627 {
628 size_t nlayouts = m_layouts.GetCount();
629 size_t i = nlayouts;
630 while(i > 0)
631 {
632 --i;
633 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
634 wxSize size = layout->overall_size;
635 switch(direction)
636 {
637 case wxHORIZONTAL:
638 if(size.x > result.x && size.y <= result.y)
639 {
640 result.x = size.x;
641 break;
642 }
643 else
644 continue;
645 case wxVERTICAL:
646 if(size.x <= result.x && size.y > result.y)
647 {
648 result.y = size.y;
649 break;
650 }
651 else
652 continue;
653 case wxBOTH:
654 if(size.x > result.x && size.y > result.y)
655 {
656 result = size;
657 break;
658 }
659 else
660 continue;
661 }
662 break;
663 }
664 return result;
665 }
666
667 void wxRibbonButtonBar::UpdateWindowUI(long flags)
668 {
669 wxWindowBase::UpdateWindowUI(flags);
670
671 // don't waste time updating state of tools in a hidden toolbar
672 if ( !IsShown() )
673 return;
674
675 size_t btn_count = m_buttons.size();
676 bool rerealize = false;
677 for ( size_t btn_i = 0; btn_i < btn_count; ++btn_i )
678 {
679 wxRibbonButtonBarButtonBase& btn = *m_buttons.Item(btn_i);
680 int id = btn.id;
681
682 wxUpdateUIEvent event(id);
683 event.SetEventObject(this);
684
685 if ( ProcessWindowEvent(event) )
686 {
687 if ( event.GetSetEnabled() )
688 EnableButton(id, event.GetEnabled());
689 if ( event.GetSetChecked() )
690 ToggleButton(id, event.GetChecked());
691 if ( event.GetSetText() )
692 {
693 btn.label = event.GetText();
694 rerealize = true;
695 }
696 }
697 }
698
699 if ( rerealize )
700 Realize();
701 }
702
703 void wxRibbonButtonBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
704 {
705 // All painting done in main paint handler to minimise flicker
706 }
707
708 void wxRibbonButtonBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
709 {
710 wxAutoBufferedPaintDC dc(this);
711 m_art->DrawButtonBarBackground(dc, this, GetSize());
712
713 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
714
715 size_t btn_count = layout->buttons.Count();
716 size_t btn_i;
717 for(btn_i = 0; btn_i < btn_count; ++btn_i)
718 {
719 wxRibbonButtonBarButtonInstance& button = layout->buttons.Item(btn_i);
720 wxRibbonButtonBarButtonBase* base = button.base;
721
722 wxBitmap* bitmap = &base->bitmap_large;
723 wxBitmap* bitmap_small = &base->bitmap_small;
724 if(base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED)
725 {
726 bitmap = &base->bitmap_large_disabled;
727 bitmap_small = &base->bitmap_small_disabled;
728 }
729 wxRect rect(button.position + m_layout_offset, base->sizes[button.size].size);
730
731 m_art->DrawButtonBarButton(dc, this, rect, base->kind,
732 base->state | button.size, base->label, *bitmap, *bitmap_small);
733 }
734 }
735
736 void wxRibbonButtonBar::OnSize(wxSizeEvent& evt)
737 {
738 wxSize new_size = evt.GetSize();
739 size_t layout_count = m_layouts.GetCount();
740 size_t layout_i;
741 m_current_layout = layout_count - 1;
742 for(layout_i = 0; layout_i < layout_count; ++layout_i)
743 {
744 wxSize layout_size = m_layouts.Item(layout_i)->overall_size;
745 if(layout_size.x <= new_size.x && layout_size.y <= new_size.y)
746 {
747 m_layout_offset.x = (new_size.x - layout_size.x) / 2;
748 m_layout_offset.y = (new_size.y - layout_size.y) / 2;
749 m_current_layout = layout_i;
750 break;
751 }
752 }
753 m_hovered_button = m_layouts.Item(m_current_layout)->FindSimilarInstance(m_hovered_button);
754 Refresh();
755 }
756
757 void wxRibbonButtonBar::CommonInit(long WXUNUSED(style))
758 {
759 m_bitmap_size_large = wxSize(32, 32);
760 m_bitmap_size_small = wxSize(16, 16);
761
762 wxRibbonButtonBarLayout* placeholder_layout = new wxRibbonButtonBarLayout;
763 placeholder_layout->overall_size = wxSize(20, 20);
764 m_layouts.Add(placeholder_layout);
765 m_current_layout = 0;
766 m_layout_offset = wxPoint(0, 0);
767 m_hovered_button = NULL;
768 m_active_button = NULL;
769 m_lock_active_state = false;
770
771 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
772 }
773
774 wxSize wxRibbonButtonBar::GetMinSize() const
775 {
776 return m_layouts.Last()->overall_size;
777 }
778
779 wxSize wxRibbonButtonBar::DoGetBestSize() const
780 {
781 return m_layouts.Item(0)->overall_size;
782 }
783
784 void wxRibbonButtonBar::MakeLayouts()
785 {
786 if(m_layouts_valid || m_art == NULL)
787 {
788 return;
789 }
790 {
791 // Clear existing layouts
792 if(m_hovered_button)
793 {
794 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
795 m_hovered_button = NULL;
796 }
797 if(m_active_button)
798 {
799 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
800 m_active_button = NULL;
801 }
802 size_t count = m_layouts.GetCount();
803 size_t i;
804 for(i = 0; i < count; ++i)
805 {
806 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
807 delete layout;
808 }
809 m_layouts.Clear();
810 }
811 size_t btn_count = m_buttons.Count();
812 size_t btn_i;
813 {
814 // Best layout : all buttons large, stacking horizontally
815 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
816 wxPoint cursor(0, 0);
817 layout->overall_size.SetHeight(0);
818 for(btn_i = 0; btn_i < btn_count; ++btn_i)
819 {
820 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
821 wxRibbonButtonBarButtonInstance instance = button->NewInstance();
822 instance.position = cursor;
823 instance.size = button->GetLargestSize();
824 wxSize& size = button->sizes[instance.size].size;
825 cursor.x += size.GetWidth();
826 layout->overall_size.SetHeight(wxMax(layout->overall_size.GetHeight(),
827 size.GetHeight()));
828 layout->buttons.Add(instance);
829 }
830 layout->overall_size.SetWidth(cursor.x);
831 m_layouts.Add(layout);
832 }
833 if(btn_count >= 2)
834 {
835 // Collapse the rightmost buttons and stack them vertically
836 size_t iLast = btn_count - 1;
837 while(TryCollapseLayout(m_layouts.Last(), iLast, &iLast) && iLast > 0)
838 {
839 --iLast;
840 }
841 }
842 }
843
844 bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
845 size_t first_btn, size_t* last_button)
846 {
847 size_t btn_count = m_buttons.Count();
848 size_t btn_i;
849 int used_height = 0;
850 int used_width = 0;
851 int available_width = 0;
852 int available_height = 0;
853
854 for(btn_i = first_btn + 1; btn_i > 0; /* decrement is inside loop */)
855 {
856 --btn_i;
857 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
858 wxRibbonButtonBarButtonState large_size_class = button->GetLargestSize();
859 wxSize large_size = button->sizes[large_size_class].size;
860 int t_available_height = wxMax(available_height,
861 large_size.GetHeight());
862 int t_available_width = available_width + large_size.GetWidth();
863 wxRibbonButtonBarButtonState small_size_class = large_size_class;
864 if(!button->GetSmallerSize(&small_size_class))
865 {
866 return false;
867 }
868 wxSize small_size = button->sizes[small_size_class].size;
869 int t_used_height = used_height + small_size.GetHeight();
870 int t_used_width = wxMax(used_width, small_size.GetWidth());
871
872 if(t_used_height > t_available_height)
873 {
874 ++btn_i;
875 break;
876 }
877 else
878 {
879 used_height = t_used_height;
880 used_width = t_used_width;
881 available_width = t_available_width;
882 available_height = t_available_height;
883 }
884 }
885
886 if(btn_i >= first_btn || used_width >= available_width)
887 {
888 return false;
889 }
890 if(last_button != NULL)
891 {
892 *last_button = btn_i;
893 }
894
895 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
896 WX_APPEND_ARRAY(layout->buttons, original->buttons);
897 wxPoint cursor(layout->buttons.Item(btn_i).position);
898 bool preserve_height = false;
899 if(btn_i == 0)
900 {
901 // If height isn't preserved (i.e. it is reduced), then the minimum
902 // size for the button bar will decrease, preventing the original
903 // layout from being used (in some cases).
904 // It may be a good idea to always preverse the height, but for now
905 // it is only done when the first button is involved in a collapse.
906 preserve_height = true;
907 }
908
909 for(; btn_i <= first_btn; ++btn_i)
910 {
911 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
912 instance.base->GetSmallerSize(&instance.size);
913 instance.position = cursor;
914 cursor.y += instance.base->sizes[instance.size].size.GetHeight();
915 }
916
917 int x_adjust = available_width - used_width;
918
919 for(; btn_i < btn_count; ++btn_i)
920 {
921 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
922 instance.position.x -= x_adjust;
923 }
924
925 layout->CalculateOverallSize();
926
927 // Sanity check
928 if(layout->overall_size.GetWidth() >= original->overall_size.GetWidth() ||
929 layout->overall_size.GetHeight() > original->overall_size.GetHeight())
930 {
931 delete layout;
932 wxFAIL_MSG("Layout collapse resulted in increased size");
933 return false;
934 }
935
936 if(preserve_height)
937 {
938 layout->overall_size.SetHeight(original->overall_size.GetHeight());
939 }
940
941 m_layouts.Add(layout);
942 return true;
943 }
944
945 void wxRibbonButtonBar::OnMouseMove(wxMouseEvent& evt)
946 {
947 wxPoint cursor(evt.GetPosition());
948 wxRibbonButtonBarButtonInstance* new_hovered = NULL;
949 long new_hovered_state = 0;
950
951 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
952 size_t btn_count = layout->buttons.Count();
953 size_t btn_i;
954 for(btn_i = 0; btn_i < btn_count; ++btn_i)
955 {
956 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
957 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
958 wxRect btn_rect;
959 btn_rect.SetTopLeft(m_layout_offset + instance.position);
960 btn_rect.SetSize(size.size);
961 if(btn_rect.Contains(cursor))
962 {
963 new_hovered = &instance;
964 new_hovered_state = instance.base->state;
965 new_hovered_state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
966 wxPoint offset(cursor);
967 offset -= btn_rect.GetTopLeft();
968 if(size.normal_region.Contains(offset))
969 {
970 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED;
971 }
972 if(size.dropdown_region.Contains(offset))
973 {
974 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_HOVERED;
975 }
976 break;
977 }
978 }
979
980 #if wxUSE_TOOLTIPS
981 if(new_hovered == NULL && GetToolTip())
982 {
983 UnsetToolTip();
984 }
985 #endif
986
987 if(new_hovered != m_hovered_button || (m_hovered_button != NULL &&
988 new_hovered_state != m_hovered_button->base->state))
989 {
990 if(m_hovered_button != NULL)
991 {
992 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
993 }
994 m_hovered_button = new_hovered;
995 if(m_hovered_button != NULL)
996 {
997 m_hovered_button->base->state = new_hovered_state;
998 #if wxUSE_TOOLTIPS
999 SetToolTip(m_hovered_button->base->help_string);
1000 #endif
1001 }
1002 Refresh(false);
1003 }
1004
1005 if(m_active_button && !m_lock_active_state)
1006 {
1007 long new_active_state = m_active_button->base->state;
1008 new_active_state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1009 wxRibbonButtonBarButtonSizeInfo& size =
1010 m_active_button->base->sizes[m_active_button->size];
1011 wxRect btn_rect;
1012 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1013 btn_rect.SetSize(size.size);
1014 if(btn_rect.Contains(cursor))
1015 {
1016 wxPoint offset(cursor);
1017 offset -= btn_rect.GetTopLeft();
1018 if(size.normal_region.Contains(offset))
1019 {
1020 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1021 }
1022 if(size.dropdown_region.Contains(offset))
1023 {
1024 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1025 }
1026 }
1027 if(new_active_state != m_active_button->base->state)
1028 {
1029 m_active_button->base->state = new_active_state;
1030 Refresh(false);
1031 }
1032 }
1033 }
1034
1035 void wxRibbonButtonBar::OnMouseDown(wxMouseEvent& evt)
1036 {
1037 wxPoint cursor(evt.GetPosition());
1038 m_active_button = NULL;
1039
1040 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
1041 size_t btn_count = layout->buttons.Count();
1042 size_t btn_i;
1043 for(btn_i = 0; btn_i < btn_count; ++btn_i)
1044 {
1045 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
1046 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
1047 wxRect btn_rect;
1048 btn_rect.SetTopLeft(m_layout_offset + instance.position);
1049 btn_rect.SetSize(size.size);
1050 if(btn_rect.Contains(cursor))
1051 {
1052 m_active_button = &instance;
1053 cursor -= btn_rect.GetTopLeft();
1054 long state = 0;
1055 if(size.normal_region.Contains(cursor))
1056 state = wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1057 else if(size.dropdown_region.Contains(cursor))
1058 state = wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1059 instance.base->state |= state;
1060 Refresh(false);
1061 break;
1062 }
1063 }
1064 }
1065
1066 void wxRibbonButtonBar::OnMouseUp(wxMouseEvent& evt)
1067 {
1068 wxPoint cursor(evt.GetPosition());
1069
1070 if(m_active_button)
1071 {
1072 wxRibbonButtonBarButtonSizeInfo& size =
1073 m_active_button->base->sizes[m_active_button->size];
1074 wxRect btn_rect;
1075 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1076 btn_rect.SetSize(size.size);
1077 if(btn_rect.Contains(cursor))
1078 {
1079 int id = m_active_button->base->id;
1080 cursor -= btn_rect.GetTopLeft();
1081 wxEventType event_type;
1082 do
1083 {
1084 if(size.normal_region.Contains(cursor))
1085 event_type = wxEVT_COMMAND_RIBBONBUTTON_CLICKED;
1086 else if(size.dropdown_region.Contains(cursor))
1087 event_type = wxEVT_COMMAND_RIBBONBUTTON_DROPDOWN_CLICKED;
1088 else
1089 break;
1090 wxRibbonButtonBarEvent notification(event_type, id);
1091 if(m_active_button->base->kind == wxRIBBON_BUTTON_TOGGLE)
1092 {
1093 m_active_button->base->state ^=
1094 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
1095 notification.SetInt(m_active_button->base->state &
1096 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
1097 }
1098 notification.SetEventObject(this);
1099 notification.SetBar(this);
1100 m_lock_active_state = true;
1101 ProcessWindowEvent(notification);
1102 m_lock_active_state = false;
1103 } while(false);
1104 if(m_active_button) // may have been NULLed by event handler
1105 {
1106 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1107 m_active_button = NULL;
1108 }
1109 Refresh(false);
1110 }
1111 }
1112 }
1113
1114 void wxRibbonButtonBar::OnMouseEnter(wxMouseEvent& evt)
1115 {
1116 if(m_active_button && !evt.LeftIsDown())
1117 {
1118 m_active_button = NULL;
1119 }
1120 }
1121
1122 void wxRibbonButtonBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
1123 {
1124 bool repaint = false;
1125 if(m_hovered_button != NULL)
1126 {
1127 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1128 m_hovered_button = NULL;
1129 repaint = true;
1130 }
1131 if(m_active_button != NULL && !m_lock_active_state)
1132 {
1133 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1134 repaint = true;
1135 }
1136 if(repaint)
1137 Refresh(false);
1138 }
1139
1140 bool wxRibbonButtonBarEvent::PopupMenu(wxMenu* menu)
1141 {
1142 wxPoint pos = wxDefaultPosition;
1143 if(m_bar->m_active_button)
1144 {
1145 wxRibbonButtonBarButtonSizeInfo& size =
1146 m_bar->m_active_button->base->sizes[m_bar->m_active_button->size];
1147 wxRect btn_rect;
1148 btn_rect.SetTopLeft(m_bar->m_layout_offset +
1149 m_bar->m_active_button->position);
1150 btn_rect.SetSize(size.size);
1151 pos = btn_rect.GetBottomLeft();
1152 pos.y++;
1153 }
1154 return m_bar->PopupMenu(menu, pos);
1155 }
1156
1157 #endif // wxUSE_RIBBON