The rounded corners look really dumb at this size.
[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 // Copyright: (C) Peter Cawley
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16
17 #if wxUSE_RIBBON
18
19 #include "wx/ribbon/panel.h"
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_RIBBONBUTTONBAR_CLICKED, wxRibbonButtonBarEvent);
32 wxDEFINE_EVENT(wxEVT_RIBBONBUTTONBAR_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 wxClientDataContainer 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 {
282 return InsertButton(GetButtonCount(), button_id, label, bitmap,
283 bitmap_small, bitmap_disabled,bitmap_small_disabled, kind, help_string);
284 }
285
286 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertButton(
287 size_t pos,
288 int button_id,
289 const wxString& label,
290 const wxBitmap& bitmap,
291 const wxBitmap& bitmap_small,
292 const wxBitmap& bitmap_disabled,
293 const wxBitmap& bitmap_small_disabled,
294 wxRibbonButtonKind kind,
295 const wxString& help_string)
296 {
297 wxASSERT(bitmap.IsOk() || bitmap_small.IsOk());
298 if(m_buttons.IsEmpty())
299 {
300 if(bitmap.IsOk())
301 {
302 m_bitmap_size_large = bitmap.GetSize();
303 if(!bitmap_small.IsOk())
304 {
305 m_bitmap_size_small = m_bitmap_size_large;
306 m_bitmap_size_small *= 0.5;
307 }
308 }
309 if(bitmap_small.IsOk())
310 {
311 m_bitmap_size_small = bitmap_small.GetSize();
312 if(!bitmap.IsOk())
313 {
314 m_bitmap_size_large = m_bitmap_size_small;
315 m_bitmap_size_large *= 2.0;
316 }
317 }
318 }
319
320 wxRibbonButtonBarButtonBase* base = new wxRibbonButtonBarButtonBase;
321 base->id = button_id;
322 base->label = label;
323 base->bitmap_large = bitmap;
324 if(!base->bitmap_large.IsOk())
325 {
326 base->bitmap_large = MakeResizedBitmap(base->bitmap_small,
327 m_bitmap_size_large);
328 }
329 else if(base->bitmap_large.GetSize() != m_bitmap_size_large)
330 {
331 base->bitmap_large = MakeResizedBitmap(base->bitmap_large,
332 m_bitmap_size_large);
333 }
334 base->bitmap_small = bitmap_small;
335 if(!base->bitmap_small.IsOk())
336 {
337 base->bitmap_small = MakeResizedBitmap(base->bitmap_large,
338 m_bitmap_size_small);
339 }
340 else if(base->bitmap_small.GetSize() != m_bitmap_size_small)
341 {
342 base->bitmap_small = MakeResizedBitmap(base->bitmap_small,
343 m_bitmap_size_small);
344 }
345 base->bitmap_large_disabled = bitmap_disabled;
346 if(!base->bitmap_large_disabled.IsOk())
347 {
348 base->bitmap_large_disabled = MakeDisabledBitmap(base->bitmap_large);
349 }
350 base->bitmap_small_disabled = bitmap_small_disabled;
351 if(!base->bitmap_small_disabled.IsOk())
352 {
353 base->bitmap_small_disabled = MakeDisabledBitmap(base->bitmap_small);
354 }
355 base->kind = kind;
356 base->help_string = help_string;
357 base->state = 0;
358
359 wxClientDC temp_dc(this);
360 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_SMALL, temp_dc);
361 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, temp_dc);
362 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_LARGE, temp_dc);
363
364 m_buttons.Insert(base, pos);
365 m_layouts_valid = false;
366 return base;
367 }
368
369
370 void
371 wxRibbonButtonBar::SetItemClientObject(wxRibbonButtonBarButtonBase* item,
372 wxClientData* data)
373 {
374 wxCHECK_RET( item, "Can't associate client object with an invalid item" );
375
376 item->client_data.SetClientObject(data);
377 }
378
379 wxClientData*
380 wxRibbonButtonBar::GetItemClientObject(const wxRibbonButtonBarButtonBase* item) const
381 {
382 wxCHECK_MSG( item, NULL, "Can't get client object for an invalid item" );
383
384 return item->client_data.GetClientObject();
385 }
386
387 void
388 wxRibbonButtonBar::SetItemClientData(wxRibbonButtonBarButtonBase* item,
389 void* data)
390 {
391 wxCHECK_RET( item, "Can't associate client data with an invalid item" );
392
393 item->client_data.SetClientData(data);
394 }
395
396 void*
397 wxRibbonButtonBar::GetItemClientData(const wxRibbonButtonBarButtonBase* item) const
398 {
399 wxCHECK_MSG( item, NULL, "Can't get client data for an invalid item" );
400
401 return item->client_data.GetClientData();
402 }
403
404
405 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertButton(
406 size_t pos,
407 int button_id,
408 const wxString& label,
409 const wxBitmap& bitmap,
410 const wxString& help_string,
411 wxRibbonButtonKind kind)
412 {
413 return InsertButton(pos, button_id, label, bitmap, wxNullBitmap,
414 wxNullBitmap, wxNullBitmap, kind, help_string);
415 }
416
417 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertDropdownButton(
418 size_t pos,
419 int button_id,
420 const wxString& label,
421 const wxBitmap& bitmap,
422 const wxString& help_string)
423 {
424 return InsertButton(pos, button_id, label, bitmap, help_string,
425 wxRIBBON_BUTTON_DROPDOWN);
426 }
427
428 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertToggleButton(
429 size_t pos,
430 int button_id,
431 const wxString& label,
432 const wxBitmap& bitmap,
433 const wxString& help_string)
434 {
435 return InsertButton(pos, button_id, label, bitmap, help_string,
436 wxRIBBON_BUTTON_TOGGLE);
437 }
438
439 wxRibbonButtonBarButtonBase* wxRibbonButtonBar::InsertHybridButton(
440 size_t pos,
441 int button_id,
442 const wxString& label,
443 const wxBitmap& bitmap,
444 const wxString& help_string)
445 {
446 return InsertButton(pos, button_id, label, bitmap, help_string,
447 wxRIBBON_BUTTON_HYBRID);
448 }
449
450 void wxRibbonButtonBar::FetchButtonSizeInfo(wxRibbonButtonBarButtonBase* button,
451 wxRibbonButtonBarButtonState size, wxDC& dc)
452 {
453 wxRibbonButtonBarButtonSizeInfo& info = button->sizes[size];
454 if(m_art)
455 {
456 info.is_supported = m_art->GetButtonBarButtonSize(dc, this,
457 button->kind, size, button->label, m_bitmap_size_large,
458 m_bitmap_size_small, &info.size, &info.normal_region,
459 &info.dropdown_region);
460 }
461 else
462 info.is_supported = false;
463 }
464
465 wxBitmap wxRibbonButtonBar::MakeResizedBitmap(const wxBitmap& original, wxSize size)
466 {
467 wxImage img(original.ConvertToImage());
468 img.Rescale(size.GetWidth(), size.GetHeight(), wxIMAGE_QUALITY_HIGH);
469 return wxBitmap(img);
470 }
471
472 wxBitmap wxRibbonButtonBar::MakeDisabledBitmap(const wxBitmap& original)
473 {
474 wxImage img(original.ConvertToImage());
475 return wxBitmap(img.ConvertToGreyscale());
476 }
477
478 size_t wxRibbonButtonBar::GetButtonCount() const
479 {
480 return m_buttons.GetCount();
481 }
482
483 bool wxRibbonButtonBar::Realize()
484 {
485 if(!m_layouts_valid)
486 {
487 MakeLayouts();
488 m_layouts_valid = true;
489 }
490 return true;
491 }
492
493 void wxRibbonButtonBar::ClearButtons()
494 {
495 m_layouts_valid = false;
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 delete button;
502 }
503 m_buttons.Clear();
504 Realize();
505 }
506
507 bool wxRibbonButtonBar::DeleteButton(int button_id)
508 {
509 size_t count = m_buttons.GetCount();
510 size_t i;
511 for(i = 0; i < count; ++i)
512 {
513 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
514 if(button->id == button_id)
515 {
516 m_layouts_valid = false;
517 m_buttons.RemoveAt(i);
518 Realize();
519 Refresh();
520 return true;
521 }
522 }
523 return false;
524 }
525
526 void wxRibbonButtonBar::EnableButton(int button_id, bool enable)
527 {
528 size_t count = m_buttons.GetCount();
529 size_t i;
530 for(i = 0; i < count; ++i)
531 {
532 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
533 if(button->id == button_id)
534 {
535 if(enable)
536 {
537 if(button->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED)
538 {
539 button->state &= ~wxRIBBON_BUTTONBAR_BUTTON_DISABLED;
540 Refresh();
541 }
542 }
543 else
544 {
545 if((button->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
546 {
547 button->state |= wxRIBBON_BUTTONBAR_BUTTON_DISABLED;
548 Refresh();
549 }
550 }
551 return;
552 }
553 }
554 }
555
556 void wxRibbonButtonBar::ToggleButton(int button_id, bool checked)
557 {
558 size_t count = m_buttons.GetCount();
559 size_t i;
560 for(i = 0; i < count; ++i)
561 {
562 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
563 if(button->id == button_id)
564 {
565 if(checked)
566 {
567 if((button->state & wxRIBBON_BUTTONBAR_BUTTON_TOGGLED) == 0)
568 {
569 button->state |= wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
570 Refresh();
571 }
572 }
573 else
574 {
575 if(button->state & wxRIBBON_BUTTONBAR_BUTTON_TOGGLED)
576 {
577 button->state &= ~wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
578 Refresh();
579 }
580 }
581 return;
582 }
583 }
584 }
585
586 void wxRibbonButtonBar::SetArtProvider(wxRibbonArtProvider* art)
587 {
588 if(art == m_art)
589 {
590 return;
591 }
592
593 wxRibbonControl::SetArtProvider(art);
594
595 wxClientDC temp_dc(this);
596 size_t btn_count = m_buttons.Count();
597 size_t btn_i;
598 for(btn_i = 0; btn_i < btn_count; ++btn_i)
599 {
600 wxRibbonButtonBarButtonBase* base = m_buttons.Item(btn_i);
601
602 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_SMALL, temp_dc);
603 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM, temp_dc);
604 FetchButtonSizeInfo(base, wxRIBBON_BUTTONBAR_BUTTON_LARGE, temp_dc);
605 }
606
607 m_layouts_valid = false;
608 Realize();
609 }
610
611 bool wxRibbonButtonBar::IsSizingContinuous() const
612 {
613 return false;
614 }
615
616 wxSize wxRibbonButtonBar::DoGetNextSmallerSize(wxOrientation direction,
617 wxSize result) const
618 {
619 size_t nlayouts = m_layouts.GetCount();
620 size_t i;
621 for(i = 0; i < nlayouts; ++i)
622 {
623 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
624 wxSize size = layout->overall_size;
625 switch(direction)
626 {
627 case wxHORIZONTAL:
628 if(size.x < result.x && size.y <= result.y)
629 {
630 result.x = size.x;
631 break;
632 }
633 else
634 continue;
635 case wxVERTICAL:
636 if(size.x <= result.x && size.y < result.y)
637 {
638 result.y = size.y;
639 break;
640 }
641 else
642 continue;
643 case wxBOTH:
644 if(size.x < result.x && size.y < result.y)
645 {
646 result = size;
647 break;
648 }
649 else
650 continue;
651 }
652 break;
653 }
654 return result;
655 }
656
657 wxSize wxRibbonButtonBar::DoGetNextLargerSize(wxOrientation direction,
658 wxSize result) const
659 {
660 size_t nlayouts = m_layouts.GetCount();
661 size_t i = nlayouts;
662 while(i > 0)
663 {
664 --i;
665 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
666 wxSize size = layout->overall_size;
667 switch(direction)
668 {
669 case wxHORIZONTAL:
670 if(size.x > result.x && size.y <= result.y)
671 {
672 result.x = size.x;
673 break;
674 }
675 else
676 continue;
677 case wxVERTICAL:
678 if(size.x <= result.x && size.y > result.y)
679 {
680 result.y = size.y;
681 break;
682 }
683 else
684 continue;
685 case wxBOTH:
686 if(size.x > result.x && size.y > result.y)
687 {
688 result = size;
689 break;
690 }
691 else
692 continue;
693 }
694 break;
695 }
696 return result;
697 }
698
699 void wxRibbonButtonBar::UpdateWindowUI(long flags)
700 {
701 wxWindowBase::UpdateWindowUI(flags);
702
703 // don't waste time updating state of tools in a hidden toolbar
704 if ( !IsShown() )
705 return;
706
707 size_t btn_count = m_buttons.size();
708 bool rerealize = false;
709 for ( size_t btn_i = 0; btn_i < btn_count; ++btn_i )
710 {
711 wxRibbonButtonBarButtonBase& btn = *m_buttons.Item(btn_i);
712 int id = btn.id;
713
714 wxUpdateUIEvent event(id);
715 event.SetEventObject(this);
716
717 if ( ProcessWindowEvent(event) )
718 {
719 if ( event.GetSetEnabled() )
720 EnableButton(id, event.GetEnabled());
721 if ( event.GetSetChecked() )
722 ToggleButton(id, event.GetChecked());
723 if ( event.GetSetText() )
724 {
725 btn.label = event.GetText();
726 rerealize = true;
727 }
728 }
729 }
730
731 if ( rerealize )
732 Realize();
733 }
734
735 void wxRibbonButtonBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
736 {
737 // All painting done in main paint handler to minimise flicker
738 }
739
740 void wxRibbonButtonBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
741 {
742 wxAutoBufferedPaintDC dc(this);
743 m_art->DrawButtonBarBackground(dc, this, GetSize());
744
745 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
746
747 size_t btn_count = layout->buttons.Count();
748 size_t btn_i;
749 for(btn_i = 0; btn_i < btn_count; ++btn_i)
750 {
751 wxRibbonButtonBarButtonInstance& button = layout->buttons.Item(btn_i);
752 wxRibbonButtonBarButtonBase* base = button.base;
753
754 wxBitmap* bitmap = &base->bitmap_large;
755 wxBitmap* bitmap_small = &base->bitmap_small;
756 if(base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED)
757 {
758 bitmap = &base->bitmap_large_disabled;
759 bitmap_small = &base->bitmap_small_disabled;
760 }
761 wxRect rect(button.position + m_layout_offset, base->sizes[button.size].size);
762
763 m_art->DrawButtonBarButton(dc, this, rect, base->kind,
764 base->state | button.size, base->label, *bitmap, *bitmap_small);
765 }
766 }
767
768 void wxRibbonButtonBar::OnSize(wxSizeEvent& evt)
769 {
770 wxSize new_size = evt.GetSize();
771 size_t layout_count = m_layouts.GetCount();
772 size_t layout_i;
773 m_current_layout = layout_count - 1;
774 for(layout_i = 0; layout_i < layout_count; ++layout_i)
775 {
776 wxSize layout_size = m_layouts.Item(layout_i)->overall_size;
777 if(layout_size.x <= new_size.x && layout_size.y <= new_size.y)
778 {
779 m_layout_offset.x = (new_size.x - layout_size.x) / 2;
780 m_layout_offset.y = (new_size.y - layout_size.y) / 2;
781 m_current_layout = layout_i;
782 break;
783 }
784 }
785 m_hovered_button = m_layouts.Item(m_current_layout)->FindSimilarInstance(m_hovered_button);
786 Refresh();
787 }
788
789 void wxRibbonButtonBar::CommonInit(long WXUNUSED(style))
790 {
791 m_bitmap_size_large = wxSize(32, 32);
792 m_bitmap_size_small = wxSize(16, 16);
793
794 wxRibbonButtonBarLayout* placeholder_layout = new wxRibbonButtonBarLayout;
795 placeholder_layout->overall_size = wxSize(20, 20);
796 m_layouts.Add(placeholder_layout);
797 m_current_layout = 0;
798 m_layout_offset = wxPoint(0, 0);
799 m_hovered_button = NULL;
800 m_active_button = NULL;
801 m_lock_active_state = false;
802 m_show_tooltips_for_disabled = false;
803
804 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
805 }
806
807 void wxRibbonButtonBar::SetShowToolTipsForDisabled(bool show)
808 {
809 m_show_tooltips_for_disabled = show;
810 }
811
812 bool wxRibbonButtonBar::GetShowToolTipsForDisabled() const
813 {
814 return m_show_tooltips_for_disabled;
815 }
816
817 wxSize wxRibbonButtonBar::GetMinSize() const
818 {
819 return m_layouts.Last()->overall_size;
820 }
821
822 wxSize wxRibbonButtonBar::DoGetBestSize() const
823 {
824 return m_layouts.Item(0)->overall_size;
825 }
826
827 void wxRibbonButtonBar::MakeLayouts()
828 {
829 if(m_layouts_valid || m_art == NULL)
830 {
831 return;
832 }
833 {
834 // Clear existing layouts
835 if(m_hovered_button)
836 {
837 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
838 m_hovered_button = NULL;
839 }
840 if(m_active_button)
841 {
842 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
843 m_active_button = NULL;
844 }
845 size_t count = m_layouts.GetCount();
846 size_t i;
847 for(i = 0; i < count; ++i)
848 {
849 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
850 delete layout;
851 }
852 m_layouts.Clear();
853 }
854 size_t btn_count = m_buttons.Count();
855 size_t btn_i;
856 {
857 // Best layout : all buttons large, stacking horizontally
858 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
859 wxPoint cursor(0, 0);
860 layout->overall_size.SetHeight(0);
861 for(btn_i = 0; btn_i < btn_count; ++btn_i)
862 {
863 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
864 wxRibbonButtonBarButtonInstance instance = button->NewInstance();
865 instance.position = cursor;
866 instance.size = button->GetLargestSize();
867 wxSize& size = button->sizes[instance.size].size;
868 cursor.x += size.GetWidth();
869 layout->overall_size.SetHeight(wxMax(layout->overall_size.GetHeight(),
870 size.GetHeight()));
871 layout->buttons.Add(instance);
872 }
873 layout->overall_size.SetWidth(cursor.x);
874 m_layouts.Add(layout);
875 }
876 if(btn_count >= 2)
877 {
878 // Collapse the rightmost buttons and stack them vertically
879 size_t iLast = btn_count - 1;
880 while(TryCollapseLayout(m_layouts.Last(), iLast, &iLast) && iLast > 0)
881 {
882 --iLast;
883 }
884 }
885 }
886
887 bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
888 size_t first_btn, size_t* last_button)
889 {
890 size_t btn_count = m_buttons.Count();
891 size_t btn_i;
892 int used_height = 0;
893 int used_width = 0;
894 int available_width = 0;
895 int available_height = 0;
896
897 for(btn_i = first_btn + 1; btn_i > 0; /* decrement is inside loop */)
898 {
899 --btn_i;
900 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
901 wxRibbonButtonBarButtonState large_size_class = button->GetLargestSize();
902 wxSize large_size = button->sizes[large_size_class].size;
903 int t_available_height = wxMax(available_height,
904 large_size.GetHeight());
905 int t_available_width = available_width + large_size.GetWidth();
906 wxRibbonButtonBarButtonState small_size_class = large_size_class;
907 if(!button->GetSmallerSize(&small_size_class))
908 {
909 return false;
910 }
911 wxSize small_size = button->sizes[small_size_class].size;
912 int t_used_height = used_height + small_size.GetHeight();
913 int t_used_width = wxMax(used_width, small_size.GetWidth());
914
915 if(t_used_height > t_available_height)
916 {
917 ++btn_i;
918 break;
919 }
920 else
921 {
922 used_height = t_used_height;
923 used_width = t_used_width;
924 available_width = t_available_width;
925 available_height = t_available_height;
926 }
927 }
928
929 if(btn_i >= first_btn || used_width >= available_width)
930 {
931 return false;
932 }
933 if(last_button != NULL)
934 {
935 *last_button = btn_i;
936 }
937
938 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
939 WX_APPEND_ARRAY(layout->buttons, original->buttons);
940 wxPoint cursor(layout->buttons.Item(btn_i).position);
941 bool preserve_height = false;
942 if(btn_i == 0)
943 {
944 // If height isn't preserved (i.e. it is reduced), then the minimum
945 // size for the button bar will decrease, preventing the original
946 // layout from being used (in some cases).
947 // It may be a good idea to always preserve the height, but for now
948 // it is only done when the first button is involved in a collapse.
949 preserve_height = true;
950 }
951
952 for(; btn_i <= first_btn; ++btn_i)
953 {
954 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
955 instance.base->GetSmallerSize(&instance.size);
956 instance.position = cursor;
957 cursor.y += instance.base->sizes[instance.size].size.GetHeight();
958 }
959
960 int x_adjust = available_width - used_width;
961
962 for(; btn_i < btn_count; ++btn_i)
963 {
964 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
965 instance.position.x -= x_adjust;
966 }
967
968 layout->CalculateOverallSize();
969
970 // Sanity check
971 if(layout->overall_size.GetWidth() >= original->overall_size.GetWidth() ||
972 layout->overall_size.GetHeight() > original->overall_size.GetHeight())
973 {
974 delete layout;
975 wxFAIL_MSG("Layout collapse resulted in increased size");
976 return false;
977 }
978
979 if(preserve_height)
980 {
981 layout->overall_size.SetHeight(original->overall_size.GetHeight());
982 }
983
984 m_layouts.Add(layout);
985 return true;
986 }
987
988 void wxRibbonButtonBar::OnMouseMove(wxMouseEvent& evt)
989 {
990 wxPoint cursor(evt.GetPosition());
991 wxRibbonButtonBarButtonInstance* new_hovered = NULL;
992 wxRibbonButtonBarButtonInstance* tooltipButton = NULL;
993 long new_hovered_state = 0;
994
995 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
996 size_t btn_count = layout->buttons.Count();
997 size_t btn_i;
998 for(btn_i = 0; btn_i < btn_count; ++btn_i)
999 {
1000 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
1001 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
1002 wxRect btn_rect;
1003 btn_rect.SetTopLeft(m_layout_offset + instance.position);
1004 btn_rect.SetSize(size.size);
1005 if(btn_rect.Contains(cursor))
1006 {
1007 if((instance.base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
1008 {
1009 tooltipButton = &instance;
1010 new_hovered = &instance;
1011 new_hovered_state = instance.base->state;
1012 new_hovered_state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1013 wxPoint offset(cursor);
1014 offset -= btn_rect.GetTopLeft();
1015 if(size.normal_region.Contains(offset))
1016 {
1017 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED;
1018 }
1019 if(size.dropdown_region.Contains(offset))
1020 {
1021 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_HOVERED;
1022 }
1023 break;
1024 }
1025 else if (m_show_tooltips_for_disabled)
1026 {
1027 tooltipButton = &instance;
1028 }
1029 }
1030 }
1031
1032 #if wxUSE_TOOLTIPS
1033 if(tooltipButton == NULL && GetToolTip())
1034 {
1035 UnsetToolTip();
1036 }
1037 if(tooltipButton)
1038 {
1039 SetToolTip(tooltipButton->base->help_string);
1040 }
1041 #endif
1042
1043 if(new_hovered != m_hovered_button || (m_hovered_button != NULL &&
1044 new_hovered_state != m_hovered_button->base->state))
1045 {
1046 if(m_hovered_button != NULL)
1047 {
1048 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1049 }
1050 m_hovered_button = new_hovered;
1051 if(m_hovered_button != NULL)
1052 {
1053 m_hovered_button->base->state = new_hovered_state;
1054 }
1055 Refresh(false);
1056 }
1057
1058 if(m_active_button && !m_lock_active_state)
1059 {
1060 long new_active_state = m_active_button->base->state;
1061 new_active_state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1062 wxRibbonButtonBarButtonSizeInfo& size =
1063 m_active_button->base->sizes[m_active_button->size];
1064 wxRect btn_rect;
1065 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1066 btn_rect.SetSize(size.size);
1067 if(btn_rect.Contains(cursor))
1068 {
1069 wxPoint offset(cursor);
1070 offset -= btn_rect.GetTopLeft();
1071 if(size.normal_region.Contains(offset))
1072 {
1073 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1074 }
1075 if(size.dropdown_region.Contains(offset))
1076 {
1077 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1078 }
1079 }
1080 if(new_active_state != m_active_button->base->state)
1081 {
1082 m_active_button->base->state = new_active_state;
1083 Refresh(false);
1084 }
1085 }
1086 }
1087
1088 void wxRibbonButtonBar::OnMouseDown(wxMouseEvent& evt)
1089 {
1090 wxPoint cursor(evt.GetPosition());
1091 m_active_button = NULL;
1092
1093 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
1094 size_t btn_count = layout->buttons.Count();
1095 size_t btn_i;
1096 for(btn_i = 0; btn_i < btn_count; ++btn_i)
1097 {
1098 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
1099 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
1100 wxRect btn_rect;
1101 btn_rect.SetTopLeft(m_layout_offset + instance.position);
1102 btn_rect.SetSize(size.size);
1103 if(btn_rect.Contains(cursor))
1104 {
1105 if((instance.base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
1106 {
1107 m_active_button = &instance;
1108 cursor -= btn_rect.GetTopLeft();
1109 long state = 0;
1110 if(size.normal_region.Contains(cursor))
1111 state = wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1112 else if(size.dropdown_region.Contains(cursor))
1113 state = wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1114 instance.base->state |= state;
1115 Refresh(false);
1116 break;
1117 }
1118 }
1119 }
1120 }
1121
1122 void wxRibbonButtonBar::OnMouseUp(wxMouseEvent& evt)
1123 {
1124 wxPoint cursor(evt.GetPosition());
1125
1126 if(m_active_button)
1127 {
1128 wxRibbonButtonBarButtonSizeInfo& size =
1129 m_active_button->base->sizes[m_active_button->size];
1130 wxRect btn_rect;
1131 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1132 btn_rect.SetSize(size.size);
1133 if(btn_rect.Contains(cursor))
1134 {
1135 int id = m_active_button->base->id;
1136 cursor -= btn_rect.GetTopLeft();
1137 wxEventType event_type;
1138 do
1139 {
1140 if(size.normal_region.Contains(cursor))
1141 event_type = wxEVT_RIBBONBUTTONBAR_CLICKED;
1142 else if(size.dropdown_region.Contains(cursor))
1143 event_type = wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED;
1144 else
1145 break;
1146 wxRibbonButtonBarEvent notification(event_type, id);
1147 if(m_active_button->base->kind == wxRIBBON_BUTTON_TOGGLE)
1148 {
1149 m_active_button->base->state ^=
1150 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
1151 notification.SetInt(m_active_button->base->state &
1152 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
1153 }
1154 notification.SetEventObject(this);
1155 notification.SetBar(this);
1156 notification.SetButton(m_active_button->base);
1157 m_lock_active_state = true;
1158 ProcessWindowEvent(notification);
1159 m_lock_active_state = false;
1160
1161 wxStaticCast(m_parent, wxRibbonPanel)->HideIfExpanded();
1162 } while(false);
1163 if(m_active_button) // may have been NULLed by event handler
1164 {
1165 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1166 m_active_button = NULL;
1167 }
1168 Refresh(false);
1169 }
1170 }
1171 }
1172
1173 void wxRibbonButtonBar::OnMouseEnter(wxMouseEvent& evt)
1174 {
1175 if(m_active_button && !evt.LeftIsDown())
1176 {
1177 m_active_button = NULL;
1178 }
1179 }
1180
1181 void wxRibbonButtonBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
1182 {
1183 bool repaint = false;
1184 if(m_hovered_button != NULL)
1185 {
1186 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1187 m_hovered_button = NULL;
1188 repaint = true;
1189 }
1190 if(m_active_button != NULL && !m_lock_active_state)
1191 {
1192 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1193 repaint = true;
1194 }
1195 if(repaint)
1196 Refresh(false);
1197 }
1198
1199 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetActiveItem() const
1200 {
1201 return m_active_button == NULL ? NULL : m_active_button->base;
1202 }
1203
1204
1205 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetHoveredItem() const
1206 {
1207 return m_hovered_button == NULL ? NULL : m_hovered_button->base;
1208 }
1209
1210
1211 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetItem(size_t n) const
1212 {
1213 wxCHECK_MSG(n < m_buttons.GetCount(), NULL, "wxRibbonButtonBar item's index is out of bound");
1214 return m_buttons.Item(n);
1215 }
1216
1217 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetItemById(int button_id) const
1218 {
1219 size_t count = m_buttons.GetCount();
1220 for ( size_t i = 0; i < count; ++i )
1221 {
1222 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
1223 if ( button->id == button_id )
1224 return button;
1225 }
1226
1227 return NULL;
1228
1229 }
1230
1231 int wxRibbonButtonBar::GetItemId(wxRibbonButtonBarButtonBase *item) const
1232 {
1233 wxCHECK_MSG(item != NULL, wxNOT_FOUND, "wxRibbonButtonBar item should not be NULL");
1234 return item->id;
1235 }
1236
1237
1238 bool wxRibbonButtonBarEvent::PopupMenu(wxMenu* menu)
1239 {
1240 wxPoint pos = wxDefaultPosition;
1241 if(m_bar->m_active_button)
1242 {
1243 wxRibbonButtonBarButtonSizeInfo& size =
1244 m_bar->m_active_button->base->sizes[m_bar->m_active_button->size];
1245 wxRect btn_rect;
1246 btn_rect.SetTopLeft(m_bar->m_layout_offset +
1247 m_bar->m_active_button->position);
1248 btn_rect.SetSize(size.size);
1249 pos = btn_rect.GetBottomLeft();
1250 pos.y++;
1251 }
1252 return m_bar->PopupMenu(menu, pos);
1253 }
1254
1255 #endif // wxUSE_RIBBON