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