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