]> git.saurik.com Git - wxWidgets.git/blob - src/ribbon/buttonbar.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[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
804 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
805 }
806
807 wxSize wxRibbonButtonBar::GetMinSize() const
808 {
809 return m_layouts.Last()->overall_size;
810 }
811
812 wxSize wxRibbonButtonBar::DoGetBestSize() const
813 {
814 return m_layouts.Item(0)->overall_size;
815 }
816
817 void wxRibbonButtonBar::MakeLayouts()
818 {
819 if(m_layouts_valid || m_art == NULL)
820 {
821 return;
822 }
823 {
824 // Clear existing layouts
825 if(m_hovered_button)
826 {
827 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
828 m_hovered_button = NULL;
829 }
830 if(m_active_button)
831 {
832 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
833 m_active_button = NULL;
834 }
835 size_t count = m_layouts.GetCount();
836 size_t i;
837 for(i = 0; i < count; ++i)
838 {
839 wxRibbonButtonBarLayout* layout = m_layouts.Item(i);
840 delete layout;
841 }
842 m_layouts.Clear();
843 }
844 size_t btn_count = m_buttons.Count();
845 size_t btn_i;
846 {
847 // Best layout : all buttons large, stacking horizontally
848 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
849 wxPoint cursor(0, 0);
850 layout->overall_size.SetHeight(0);
851 for(btn_i = 0; btn_i < btn_count; ++btn_i)
852 {
853 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
854 wxRibbonButtonBarButtonInstance instance = button->NewInstance();
855 instance.position = cursor;
856 instance.size = button->GetLargestSize();
857 wxSize& size = button->sizes[instance.size].size;
858 cursor.x += size.GetWidth();
859 layout->overall_size.SetHeight(wxMax(layout->overall_size.GetHeight(),
860 size.GetHeight()));
861 layout->buttons.Add(instance);
862 }
863 layout->overall_size.SetWidth(cursor.x);
864 m_layouts.Add(layout);
865 }
866 if(btn_count >= 2)
867 {
868 // Collapse the rightmost buttons and stack them vertically
869 size_t iLast = btn_count - 1;
870 while(TryCollapseLayout(m_layouts.Last(), iLast, &iLast) && iLast > 0)
871 {
872 --iLast;
873 }
874 }
875 }
876
877 bool wxRibbonButtonBar::TryCollapseLayout(wxRibbonButtonBarLayout* original,
878 size_t first_btn, size_t* last_button)
879 {
880 size_t btn_count = m_buttons.Count();
881 size_t btn_i;
882 int used_height = 0;
883 int used_width = 0;
884 int available_width = 0;
885 int available_height = 0;
886
887 for(btn_i = first_btn + 1; btn_i > 0; /* decrement is inside loop */)
888 {
889 --btn_i;
890 wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i);
891 wxRibbonButtonBarButtonState large_size_class = button->GetLargestSize();
892 wxSize large_size = button->sizes[large_size_class].size;
893 int t_available_height = wxMax(available_height,
894 large_size.GetHeight());
895 int t_available_width = available_width + large_size.GetWidth();
896 wxRibbonButtonBarButtonState small_size_class = large_size_class;
897 if(!button->GetSmallerSize(&small_size_class))
898 {
899 return false;
900 }
901 wxSize small_size = button->sizes[small_size_class].size;
902 int t_used_height = used_height + small_size.GetHeight();
903 int t_used_width = wxMax(used_width, small_size.GetWidth());
904
905 if(t_used_height > t_available_height)
906 {
907 ++btn_i;
908 break;
909 }
910 else
911 {
912 used_height = t_used_height;
913 used_width = t_used_width;
914 available_width = t_available_width;
915 available_height = t_available_height;
916 }
917 }
918
919 if(btn_i >= first_btn || used_width >= available_width)
920 {
921 return false;
922 }
923 if(last_button != NULL)
924 {
925 *last_button = btn_i;
926 }
927
928 wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout;
929 WX_APPEND_ARRAY(layout->buttons, original->buttons);
930 wxPoint cursor(layout->buttons.Item(btn_i).position);
931 bool preserve_height = false;
932 if(btn_i == 0)
933 {
934 // If height isn't preserved (i.e. it is reduced), then the minimum
935 // size for the button bar will decrease, preventing the original
936 // layout from being used (in some cases).
937 // It may be a good idea to always preverse the height, but for now
938 // it is only done when the first button is involved in a collapse.
939 preserve_height = true;
940 }
941
942 for(; btn_i <= first_btn; ++btn_i)
943 {
944 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
945 instance.base->GetSmallerSize(&instance.size);
946 instance.position = cursor;
947 cursor.y += instance.base->sizes[instance.size].size.GetHeight();
948 }
949
950 int x_adjust = available_width - used_width;
951
952 for(; btn_i < btn_count; ++btn_i)
953 {
954 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
955 instance.position.x -= x_adjust;
956 }
957
958 layout->CalculateOverallSize();
959
960 // Sanity check
961 if(layout->overall_size.GetWidth() >= original->overall_size.GetWidth() ||
962 layout->overall_size.GetHeight() > original->overall_size.GetHeight())
963 {
964 delete layout;
965 wxFAIL_MSG("Layout collapse resulted in increased size");
966 return false;
967 }
968
969 if(preserve_height)
970 {
971 layout->overall_size.SetHeight(original->overall_size.GetHeight());
972 }
973
974 m_layouts.Add(layout);
975 return true;
976 }
977
978 void wxRibbonButtonBar::OnMouseMove(wxMouseEvent& evt)
979 {
980 wxPoint cursor(evt.GetPosition());
981 wxRibbonButtonBarButtonInstance* new_hovered = NULL;
982 long new_hovered_state = 0;
983
984 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
985 size_t btn_count = layout->buttons.Count();
986 size_t btn_i;
987 for(btn_i = 0; btn_i < btn_count; ++btn_i)
988 {
989 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
990 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
991 wxRect btn_rect;
992 btn_rect.SetTopLeft(m_layout_offset + instance.position);
993 btn_rect.SetSize(size.size);
994 if(btn_rect.Contains(cursor))
995 {
996 if((instance.base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
997 {
998 new_hovered = &instance;
999 new_hovered_state = instance.base->state;
1000 new_hovered_state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1001 wxPoint offset(cursor);
1002 offset -= btn_rect.GetTopLeft();
1003 if(size.normal_region.Contains(offset))
1004 {
1005 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED;
1006 }
1007 if(size.dropdown_region.Contains(offset))
1008 {
1009 new_hovered_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_HOVERED;
1010 }
1011 break;
1012 }
1013 }
1014 }
1015
1016 #if wxUSE_TOOLTIPS
1017 if(new_hovered == NULL && GetToolTip())
1018 {
1019 UnsetToolTip();
1020 }
1021 #endif
1022
1023 if(new_hovered != m_hovered_button || (m_hovered_button != NULL &&
1024 new_hovered_state != m_hovered_button->base->state))
1025 {
1026 if(m_hovered_button != NULL)
1027 {
1028 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1029 }
1030 m_hovered_button = new_hovered;
1031 if(m_hovered_button != NULL)
1032 {
1033 m_hovered_button->base->state = new_hovered_state;
1034 #if wxUSE_TOOLTIPS
1035 SetToolTip(m_hovered_button->base->help_string);
1036 #endif
1037 }
1038 Refresh(false);
1039 }
1040
1041 if(m_active_button && !m_lock_active_state)
1042 {
1043 long new_active_state = m_active_button->base->state;
1044 new_active_state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1045 wxRibbonButtonBarButtonSizeInfo& size =
1046 m_active_button->base->sizes[m_active_button->size];
1047 wxRect btn_rect;
1048 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1049 btn_rect.SetSize(size.size);
1050 if(btn_rect.Contains(cursor))
1051 {
1052 wxPoint offset(cursor);
1053 offset -= btn_rect.GetTopLeft();
1054 if(size.normal_region.Contains(offset))
1055 {
1056 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1057 }
1058 if(size.dropdown_region.Contains(offset))
1059 {
1060 new_active_state |= wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1061 }
1062 }
1063 if(new_active_state != m_active_button->base->state)
1064 {
1065 m_active_button->base->state = new_active_state;
1066 Refresh(false);
1067 }
1068 }
1069 }
1070
1071 void wxRibbonButtonBar::OnMouseDown(wxMouseEvent& evt)
1072 {
1073 wxPoint cursor(evt.GetPosition());
1074 m_active_button = NULL;
1075
1076 wxRibbonButtonBarLayout* layout = m_layouts.Item(m_current_layout);
1077 size_t btn_count = layout->buttons.Count();
1078 size_t btn_i;
1079 for(btn_i = 0; btn_i < btn_count; ++btn_i)
1080 {
1081 wxRibbonButtonBarButtonInstance& instance = layout->buttons.Item(btn_i);
1082 wxRibbonButtonBarButtonSizeInfo& size = instance.base->sizes[instance.size];
1083 wxRect btn_rect;
1084 btn_rect.SetTopLeft(m_layout_offset + instance.position);
1085 btn_rect.SetSize(size.size);
1086 if(btn_rect.Contains(cursor))
1087 {
1088 if((instance.base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
1089 {
1090 m_active_button = &instance;
1091 cursor -= btn_rect.GetTopLeft();
1092 long state = 0;
1093 if(size.normal_region.Contains(cursor))
1094 state = wxRIBBON_BUTTONBAR_BUTTON_NORMAL_ACTIVE;
1095 else if(size.dropdown_region.Contains(cursor))
1096 state = wxRIBBON_BUTTONBAR_BUTTON_DROPDOWN_ACTIVE;
1097 instance.base->state |= state;
1098 Refresh(false);
1099 break;
1100 }
1101 }
1102 }
1103 }
1104
1105 void wxRibbonButtonBar::OnMouseUp(wxMouseEvent& evt)
1106 {
1107 wxPoint cursor(evt.GetPosition());
1108
1109 if(m_active_button)
1110 {
1111 wxRibbonButtonBarButtonSizeInfo& size =
1112 m_active_button->base->sizes[m_active_button->size];
1113 wxRect btn_rect;
1114 btn_rect.SetTopLeft(m_layout_offset + m_active_button->position);
1115 btn_rect.SetSize(size.size);
1116 if(btn_rect.Contains(cursor))
1117 {
1118 int id = m_active_button->base->id;
1119 cursor -= btn_rect.GetTopLeft();
1120 wxEventType event_type;
1121 do
1122 {
1123 if(size.normal_region.Contains(cursor))
1124 event_type = wxEVT_COMMAND_RIBBONBUTTON_CLICKED;
1125 else if(size.dropdown_region.Contains(cursor))
1126 event_type = wxEVT_COMMAND_RIBBONBUTTON_DROPDOWN_CLICKED;
1127 else
1128 break;
1129 wxRibbonButtonBarEvent notification(event_type, id);
1130 if(m_active_button->base->kind == wxRIBBON_BUTTON_TOGGLE)
1131 {
1132 m_active_button->base->state ^=
1133 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
1134 notification.SetInt(m_active_button->base->state &
1135 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
1136 }
1137 notification.SetEventObject(this);
1138 notification.SetBar(this);
1139 notification.SetButton(m_active_button->base);
1140 m_lock_active_state = true;
1141 ProcessWindowEvent(notification);
1142 m_lock_active_state = false;
1143
1144 wxStaticCast(m_parent, wxRibbonPanel)->HideIfExpanded();
1145 } while(false);
1146 if(m_active_button) // may have been NULLed by event handler
1147 {
1148 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1149 m_active_button = NULL;
1150 }
1151 Refresh(false);
1152 }
1153 }
1154 }
1155
1156 void wxRibbonButtonBar::OnMouseEnter(wxMouseEvent& evt)
1157 {
1158 if(m_active_button && !evt.LeftIsDown())
1159 {
1160 m_active_button = NULL;
1161 }
1162 }
1163
1164 void wxRibbonButtonBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
1165 {
1166 bool repaint = false;
1167 if(m_hovered_button != NULL)
1168 {
1169 m_hovered_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_HOVER_MASK;
1170 m_hovered_button = NULL;
1171 repaint = true;
1172 }
1173 if(m_active_button != NULL && !m_lock_active_state)
1174 {
1175 m_active_button->base->state &= ~wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK;
1176 repaint = true;
1177 }
1178 if(repaint)
1179 Refresh(false);
1180 }
1181
1182 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetActiveItem() const
1183 {
1184 return m_active_button == NULL ? NULL : m_active_button->base;
1185 }
1186
1187
1188 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetHoveredItem() const
1189 {
1190 return m_hovered_button == NULL ? NULL : m_hovered_button->base;
1191 }
1192
1193
1194 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetItem(size_t n) const
1195 {
1196 wxCHECK_MSG(n < m_buttons.GetCount(), NULL, "wxRibbonButtonBar item's index is out of bound");
1197 return m_buttons.Item(n);
1198 }
1199
1200 wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetItemById(int button_id) const
1201 {
1202 size_t count = m_buttons.GetCount();
1203 for ( size_t i = 0; i < count; ++i )
1204 {
1205 wxRibbonButtonBarButtonBase* button = m_buttons.Item(i);
1206 if ( button->id == button_id )
1207 return button;
1208 }
1209
1210 return NULL;
1211
1212 }
1213
1214 int wxRibbonButtonBar::GetItemId(wxRibbonButtonBarButtonBase *item) const
1215 {
1216 wxCHECK_MSG(item != NULL, wxNOT_FOUND, "wxRibbonButtonBar item should not be NULL");
1217 return item->id;
1218 }
1219
1220
1221 bool wxRibbonButtonBarEvent::PopupMenu(wxMenu* menu)
1222 {
1223 wxPoint pos = wxDefaultPosition;
1224 if(m_bar->m_active_button)
1225 {
1226 wxRibbonButtonBarButtonSizeInfo& size =
1227 m_bar->m_active_button->base->sizes[m_bar->m_active_button->size];
1228 wxRect btn_rect;
1229 btn_rect.SetTopLeft(m_bar->m_layout_offset +
1230 m_bar->m_active_button->position);
1231 btn_rect.SetSize(size.size);
1232 pos = btn_rect.GetBottomLeft();
1233 pos.y++;
1234 }
1235 return m_bar->PopupMenu(menu, pos);
1236 }
1237
1238 #endif // wxUSE_RIBBON