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