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