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