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