]> git.saurik.com Git - wxWidgets.git/blame - src/ribbon/buttonbar.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / ribbon / buttonbar.cpp
CommitLineData
3c3ead1d
PC
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
3c3ead1d
PC
18#if wxUSE_RIBBON
19
42d73941 20#include "wx/ribbon/panel.h"
4cf018e1 21#include "wx/ribbon/buttonbar.h"
3c3ead1d
PC
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
32wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBUTTON_CLICKED, wxRibbonButtonBarEvent);
33wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBUTTON_DROPDOWN_CLICKED, wxRibbonButtonBarEvent);
34
35IMPLEMENT_DYNAMIC_CLASS(wxRibbonButtonBarEvent, wxCommandEvent)
36IMPLEMENT_CLASS(wxRibbonButtonBar, wxRibbonControl)
37
38BEGIN_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)
47END_EVENT_TABLE()
48
49class wxRibbonButtonBarButtonSizeInfo
50{
51public:
52 bool is_supported;
53 wxSize size;
54 wxRect normal_region;
55 wxRect dropdown_region;
56};
57
58class wxRibbonButtonBarButtonInstance
59{
60public:
61 wxPoint position;
62 wxRibbonButtonBarButtonBase* base;
63 wxRibbonButtonBarButtonState size;
64};
65
66class wxRibbonButtonBarButtonBase
67{
68public:
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];
652aa936 120 wxClientDataContainer client_data;
3c3ead1d
PC
121 int id;
122 wxRibbonButtonKind kind;
123 long state;
124};
125
126WX_DECLARE_OBJARRAY(wxRibbonButtonBarButtonInstance, wxArrayRibbonButtonBarButtonInstance);
14371273 127#include "wx/arrimpl.cpp"
65391c8f 128WX_DEFINE_OBJARRAY(wxArrayRibbonButtonBarButtonInstance)
3c3ead1d
PC
129
130class wxRibbonButtonBarLayout
131{
132public:
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
179wxRibbonButtonBar::wxRibbonButtonBar()
180{
181 m_layouts_valid = false;
fd6e1597 182 CommonInit (0);
3c3ead1d
PC
183}
184
185wxRibbonButtonBar::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
197wxRibbonButtonBar::~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
217bool 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
232wxRibbonButtonBarButtonBase* 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
243wxRibbonButtonBarButtonBase* 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
955bad41
PC
253wxRibbonButtonBarButtonBase* 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
3c3ead1d
PC
263wxRibbonButtonBarButtonBase* 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}
ce00f59b 272
3c3ead1d
PC
273wxRibbonButtonBarButtonBase* 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,
652aa936 281 const wxString& help_string)
ff4cb916
VZ
282{
283 return InsertButton(GetButtonCount(), button_id, label, bitmap,
652aa936 284 bitmap_small, bitmap_disabled,bitmap_small_disabled, kind, help_string);
ff4cb916
VZ
285}
286
287wxRibbonButtonBarButtonBase* 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,
652aa936 296 const wxString& help_string)
3c3ead1d
PC
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;
3c3ead1d
PC
358 base->state = 0;
359
089ca539 360 wxClientDC temp_dc(this);
3c3ead1d
PC
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
ff4cb916 365 m_buttons.Insert(base, pos);
3c3ead1d
PC
366 m_layouts_valid = false;
367 return base;
368}
369
652aa936
VZ
370
371void
372wxRibbonButtonBar::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
380wxClientData*
381wxRibbonButtonBar::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
388void
389wxRibbonButtonBar::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
397void*
398wxRibbonButtonBar::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
ff4cb916
VZ
406wxRibbonButtonBarButtonBase* 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
418wxRibbonButtonBarButtonBase* 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
429wxRibbonButtonBarButtonBase* 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
440wxRibbonButtonBarButtonBase* 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
3c3ead1d
PC
451void 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
466wxBitmap 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
473wxBitmap wxRibbonButtonBar::MakeDisabledBitmap(const wxBitmap& original)
474{
475 wxImage img(original.ConvertToImage());
476 return wxBitmap(img.ConvertToGreyscale());
477}
478
ff4cb916
VZ
479size_t wxRibbonButtonBar::GetButtonCount() const
480{
481 return m_buttons.GetCount();
482}
483
3c3ead1d
PC
484bool wxRibbonButtonBar::Realize()
485{
486 if(!m_layouts_valid)
487 {
488 MakeLayouts();
489 m_layouts_valid = true;
490 }
491 return true;
492}
493
494void 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
508bool 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
527void 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
955bad41
PC
557void 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
3c3ead1d
PC
587void wxRibbonButtonBar::SetArtProvider(wxRibbonArtProvider* art)
588{
589 if(art == m_art)
590 {
591 return;
592 }
593
594 wxRibbonControl::SetArtProvider(art);
595
089ca539 596 wxClientDC temp_dc(this);
3c3ead1d
PC
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
612bool wxRibbonButtonBar::IsSizingContinuous() const
613{
614 return false;
615}
616
617wxSize 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
658wxSize 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
f01e5624
VZ
700void 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
3c3ead1d
PC
736void wxRibbonButtonBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
737{
738 // All painting done in main paint handler to minimise flicker
739}
740
741void 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);
ce00f59b 763
3c3ead1d
PC
764 m_art->DrawButtonBarButton(dc, this, rect, base->kind,
765 base->state | button.size, base->label, *bitmap, *bitmap_small);
766 }
767}
768
769void 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
790void 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);
65391c8f 805}
3c3ead1d
PC
806
807wxSize wxRibbonButtonBar::GetMinSize() const
808{
809 return m_layouts.Last()->overall_size;
810}
811
812wxSize wxRibbonButtonBar::DoGetBestSize() const
813{
814 return m_layouts.Item(0)->overall_size;
815}
816
817void 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
877bool 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;
65391c8f 965 wxFAIL_MSG("Layout collapse resulted in increased size");
3c3ead1d
PC
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
978void 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 {
209dc298 996 if((instance.base->state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) == 0)
3c3ead1d 997 {
209dc298
VZ
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;
3c3ead1d 1012 }
3c3ead1d
PC
1013 }
1014 }
1015
f8193bbb 1016#if wxUSE_TOOLTIPS
349f41fd
VZ
1017 if(new_hovered == NULL && GetToolTip())
1018 {
1019 UnsetToolTip();
1020 }
f8193bbb 1021#endif
349f41fd 1022
3c3ead1d
PC
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;
f8193bbb 1034#if wxUSE_TOOLTIPS
349f41fd 1035 SetToolTip(m_hovered_button->base->help_string);
f8193bbb 1036#endif
3c3ead1d
PC
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
1071void 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 {
209dc298
VZ
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 }
3c3ead1d
PC
1101 }
1102 }
1103}
1104
1105void 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);
955bad41
PC
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 }
3c3ead1d
PC
1137 notification.SetEventObject(this);
1138 notification.SetBar(this);
7f08b828 1139 notification.SetButton(m_active_button->base);
3c3ead1d
PC
1140 m_lock_active_state = true;
1141 ProcessWindowEvent(notification);
1142 m_lock_active_state = false;
42d73941
VZ
1143
1144 wxStaticCast(m_parent, wxRibbonPanel)->HideIfExpanded();
3c3ead1d
PC
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
1156void wxRibbonButtonBar::OnMouseEnter(wxMouseEvent& evt)
1157{
1158 if(m_active_button && !evt.LeftIsDown())
1159 {
1160 m_active_button = NULL;
1161 }
1162}
1163
1164void 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}
02a40ac1
VZ
1181
1182wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetActiveItem() const
1183{
1184 return m_active_button == NULL ? NULL : m_active_button->base;
1185}
1186
1187
1188wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetHoveredItem() const
1189{
1190 return m_hovered_button == NULL ? NULL : m_hovered_button->base;
1191}
1192
1193
71a77e77
VZ
1194wxRibbonButtonBarButtonBase *wxRibbonButtonBar::GetItem(size_t n) const
1195{
975c496d 1196 wxCHECK_MSG(n < m_buttons.GetCount(), NULL, "wxRibbonButtonBar item's index is out of bound");
71a77e77
VZ
1197 return m_buttons.Item(n);
1198}
1199
1200wxRibbonButtonBarButtonBase *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
1214int 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
3c3ead1d
PC
1220
1221bool 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