1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/ribbon/bar.cpp
3 // Purpose:     Top-level component of the ribbon-bar-style interface
4 // Author:      Peter Cawley
5 // Modified by:
6 // Created:     2009-05-23
7 // Copyright:   (C) Peter Cawley
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 #include "wx/wxprec.h"
12 
13 
14 #if wxUSE_RIBBON
15 
16 #include "wx/ribbon/bar.h"
17 #include "wx/ribbon/art.h"
18 #include "wx/dcbuffer.h"
19 #include "wx/app.h"
20 #include "wx/vector.h"
21 
22 #ifndef WX_PRECOMP
23 #endif
24 
25 #ifdef __WXMSW__
26 #include "wx/msw/private.h"
27 #endif
28 
29 #include "wx/arrimpl.cpp"
30 #include "wx/imaglist.h"
31 
32 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray)
33 
34 wxDEFINE_EVENT(wxEVT_RIBBONBAR_PAGE_CHANGED, wxRibbonBarEvent);
35 wxDEFINE_EVENT(wxEVT_RIBBONBAR_PAGE_CHANGING, wxRibbonBarEvent);
36 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_MIDDLE_DOWN, wxRibbonBarEvent);
37 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_MIDDLE_UP, wxRibbonBarEvent);
38 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_RIGHT_DOWN, wxRibbonBarEvent);
39 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_RIGHT_UP, wxRibbonBarEvent);
40 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_LEFT_DCLICK, wxRibbonBarEvent);
41 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TOGGLED, wxRibbonBarEvent);
42 wxDEFINE_EVENT(wxEVT_RIBBONBAR_HELP_CLICK, wxRibbonBarEvent);
43 
44 wxIMPLEMENT_CLASS(wxRibbonBar, wxRibbonControl);
45 wxIMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent, wxNotifyEvent);
46 
wxBEGIN_EVENT_TABLE(wxRibbonBar,wxRibbonControl)47 wxBEGIN_EVENT_TABLE(wxRibbonBar, wxRibbonControl)
48   EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground)
49   EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave)
50   EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown)
51   EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp)
52   EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown)
53   EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp)
54   EVT_MOTION(wxRibbonBar::OnMouseMove)
55   EVT_PAINT(wxRibbonBar::OnPaint)
56   EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown)
57   EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp)
58   EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick)
59   EVT_SIZE(wxRibbonBar::OnSize)
60   EVT_KILL_FOCUS(wxRibbonBar::OnKillFocus)
61 wxEND_EVENT_TABLE()
62 
63 void wxRibbonBar::AddPage(wxRibbonPage *page)
64 {
65     wxRibbonPageTabInfo info;
66 
67     info.page = page;
68     info.active = false;
69     info.hovered = false;
70     info.highlight = false;
71     info.shown = true;
72     // info.rect not set (intentional)
73 
74     wxClientDC dcTemp(this);
75     wxString label;
76     if(m_flags & wxRIBBON_BAR_SHOW_PAGE_LABELS)
77         label = page->GetLabel();
78     wxBitmap icon;
79     if(m_flags & wxRIBBON_BAR_SHOW_PAGE_ICONS)
80         icon = page->GetIcon();
81     m_art->GetBarTabWidth(dcTemp, this, label, icon,
82                           &info.ideal_width,
83                           &info.small_begin_need_separator_width,
84                           &info.small_must_have_separator_width,
85                           &info.minimum_width);
86 
87     if(m_pages.IsEmpty())
88     {
89         m_tabs_total_width_ideal = info.ideal_width;
90         m_tabs_total_width_minimum = info.minimum_width;
91     }
92     else
93     {
94         int sep = m_art->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE);
95         m_tabs_total_width_ideal += sep + info.ideal_width;
96         m_tabs_total_width_minimum += sep + info.minimum_width;
97     }
98     m_pages.Add(info);
99 
100     page->Hide(); // Most likely case is that this new page is not the active tab
101     page->SetArtProvider(m_art);
102 
103     if(m_pages.GetCount() == 1)
104     {
105         SetActivePage((size_t)0);
106     }
107 }
108 
DismissExpandedPanel()109 bool wxRibbonBar::DismissExpandedPanel()
110 {
111     if(m_current_page == -1)
112         return false;
113     return m_pages.Item(m_current_page).page->DismissExpandedPanel();
114 }
115 
116 
ShowPanels(wxRibbonDisplayMode mode)117 void wxRibbonBar::ShowPanels(wxRibbonDisplayMode mode)
118 {
119     switch ( mode )
120     {
121         case wxRIBBON_BAR_PINNED:
122         case wxRIBBON_BAR_EXPANDED:
123             m_arePanelsShown = true;
124             break;
125 
126         case wxRIBBON_BAR_MINIMIZED:
127             m_arePanelsShown = false;
128             break;
129     }
130 
131     SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
132     Realise();
133     GetParent()->Layout();
134 
135     m_ribbon_state = mode;
136 }
137 
138 
ShowPanels(bool show)139 void wxRibbonBar::ShowPanels(bool show)
140 {
141     ShowPanels( show ? wxRIBBON_BAR_PINNED : wxRIBBON_BAR_MINIMIZED );
142 }
143 
SetWindowStyleFlag(long style)144 void wxRibbonBar::SetWindowStyleFlag(long style)
145 {
146     m_flags = style;
147     if(m_art)
148         m_art->SetFlags(style);
149 }
150 
GetWindowStyleFlag() const151 long wxRibbonBar::GetWindowStyleFlag() const
152 {
153     return m_flags;
154 }
155 
Realize()156 bool wxRibbonBar::Realize()
157 {
158     bool status = true;
159 
160     wxClientDC dcTemp(this);
161     int sep = m_art->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE);
162     size_t numtabs = m_pages.GetCount();
163     bool firstVisible = true;
164     size_t i;
165     for(i = 0; i < numtabs; ++i)
166     {
167         wxRibbonPageTabInfo& info = m_pages.Item(i);
168         if (!info.shown)
169             continue;
170         RepositionPage(info.page);
171         if(!info.page->Realize())
172         {
173             status = false;
174         }
175         wxString label;
176         if(m_flags & wxRIBBON_BAR_SHOW_PAGE_LABELS)
177             label = info.page->GetLabel();
178         wxBitmap icon;
179         if(m_flags & wxRIBBON_BAR_SHOW_PAGE_ICONS)
180             icon = info.page->GetIcon();
181         m_art->GetBarTabWidth(dcTemp, this, label, icon,
182                               &info.ideal_width,
183                               &info.small_begin_need_separator_width,
184                               &info.small_must_have_separator_width,
185                               &info.minimum_width);
186 
187         if ( firstVisible )
188         {
189             firstVisible = false;
190 
191             m_tabs_total_width_ideal = info.ideal_width;
192             m_tabs_total_width_minimum = info.minimum_width;
193         }
194         else
195         {
196             m_tabs_total_width_ideal += sep + info.ideal_width;
197             m_tabs_total_width_minimum += sep + info.minimum_width;
198         }
199     }
200     m_tab_height = m_art->GetTabCtrlHeight(dcTemp, this, m_pages);
201 
202     RecalculateMinSize();
203     RecalculateTabSizes();
204     Refresh();
205 
206     return status;
207 }
208 
OnMouseMove(wxMouseEvent & evt)209 void wxRibbonBar::OnMouseMove(wxMouseEvent& evt)
210 {
211     int x = evt.GetX();
212     int y = evt.GetY();
213     int hovered_page = -1;
214     bool refresh_tabs = false;
215     if(y < m_tab_height)
216     {
217         // It is quite likely that the mouse moved a small amount and is still over the same tab
218         if(m_current_hovered_page != -1 && m_pages.Item((size_t)m_current_hovered_page).rect.Contains(x, y))
219         {
220             hovered_page = m_current_hovered_page;
221             // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
222             if(m_tab_scroll_buttons_shown)
223             {
224                 if(x >= m_tab_scroll_right_button_rect.GetX() || x < m_tab_scroll_left_button_rect.GetRight())
225                 {
226                     hovered_page = -1;
227                 }
228             }
229         }
230         else
231         {
232             HitTestTabs(evt.GetPosition(), &hovered_page);
233         }
234     }
235     if(hovered_page != m_current_hovered_page)
236     {
237         if(m_current_hovered_page != -1)
238         {
239             m_pages.Item((int)m_current_hovered_page).hovered = false;
240         }
241         m_current_hovered_page = hovered_page;
242         if(m_current_hovered_page != -1)
243         {
244             m_pages.Item((int)m_current_hovered_page).hovered = true;
245         }
246         refresh_tabs = true;
247     }
248     if(m_tab_scroll_buttons_shown)
249     {
250 #define SET_FLAG(variable, flag) \
251     { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
252 #define UNSET_FLAG(variable, flag) \
253     { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
254 
255         if(m_tab_scroll_left_button_rect.Contains(x, y))
256             SET_FLAG(m_tab_scroll_left_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
257         else
258             UNSET_FLAG(m_tab_scroll_left_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
259 
260         if(m_tab_scroll_right_button_rect.Contains(x, y))
261             SET_FLAG(m_tab_scroll_right_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
262         else
263             UNSET_FLAG(m_tab_scroll_right_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
264 #undef SET_FLAG
265 #undef UNSET_FLAG
266     }
267     if(refresh_tabs)
268     {
269         RefreshTabBar();
270     }
271     if ( m_flags & wxRIBBON_BAR_SHOW_TOGGLE_BUTTON )
272         HitTestRibbonButton(m_toggle_button_rect, evt.GetPosition(), m_toggle_button_hovered);
273     if ( m_flags & wxRIBBON_BAR_SHOW_HELP_BUTTON )
274         HitTestRibbonButton(m_help_button_rect, evt.GetPosition(), m_help_button_hovered);
275 }
276 
OnMouseLeave(wxMouseEvent & WXUNUSED (evt))277 void wxRibbonBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
278 {
279     // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
280     // can leave the window quickly and leave a tab in the hovered state.
281     bool refresh_tabs = false;
282     if(m_current_hovered_page != -1)
283     {
284         m_pages.Item((int)m_current_hovered_page).hovered = false;
285         m_current_hovered_page = -1;
286         refresh_tabs = true;
287     }
288     if(m_tab_scroll_left_button_state & wxRIBBON_SCROLL_BTN_HOVERED)
289     {
290         m_tab_scroll_left_button_state &= ~wxRIBBON_SCROLL_BTN_HOVERED;
291         refresh_tabs = true;
292     }
293     if(m_tab_scroll_right_button_state & wxRIBBON_SCROLL_BTN_HOVERED)
294     {
295         m_tab_scroll_right_button_state &= ~wxRIBBON_SCROLL_BTN_HOVERED;
296         refresh_tabs = true;
297     }
298     if(refresh_tabs)
299     {
300         RefreshTabBar();
301     }
302     if(m_toggle_button_hovered)
303     {
304         m_bar_hovered = false;
305         m_toggle_button_hovered = false;
306         Refresh(false);
307     }
308     if ( m_help_button_hovered )
309     {
310         m_help_button_hovered = false;
311         m_bar_hovered = false;
312         Refresh(false);
313     }
314 }
315 
GetPage(int n)316 wxRibbonPage* wxRibbonBar::GetPage(int n)
317 {
318     if(n < 0 || (size_t)n >= m_pages.GetCount())
319         return 0;
320     return m_pages.Item(n).page;
321 }
322 
GetPageCount() const323 size_t wxRibbonBar::GetPageCount() const
324 {
325     return m_pages.GetCount();
326 }
327 
IsPageShown(size_t page) const328 bool wxRibbonBar::IsPageShown(size_t page) const
329 {
330     if (page >= m_pages.GetCount())
331         return false;
332     return m_pages.Item(page).shown;
333 }
334 
ShowPage(size_t page,bool show)335 void wxRibbonBar::ShowPage(size_t page, bool show)
336 {
337     if(page >= m_pages.GetCount())
338         return;
339     m_pages.Item(page).shown = show;
340 }
341 
IsPageHighlighted(size_t page) const342 bool wxRibbonBar::IsPageHighlighted(size_t page) const
343 {
344     if (page >= m_pages.GetCount())
345         return false;
346     return m_pages.Item(page).highlight;
347 }
348 
AddPageHighlight(size_t page,bool highlight)349 void wxRibbonBar::AddPageHighlight(size_t page, bool highlight)
350 {
351     if(page >= m_pages.GetCount())
352         return;
353     m_pages.Item(page).highlight = highlight;
354 }
355 
DeletePage(size_t n)356 void wxRibbonBar::DeletePage(size_t n)
357 {
358     if(n < m_pages.GetCount())
359     {
360         wxRibbonPage *page = m_pages.Item(n).page;
361 
362         // Schedule page object for destruction and not destroying directly
363         // as this function can be called in an event handler and page functions
364         // can be called afeter removing.
365         // Like in wxRibbonButtonBar::OnMouseUp
366         if(!wxTheApp->IsScheduledForDestruction(page))
367         {
368             wxTheApp->ScheduleForDestruction(page);
369         }
370 
371         m_pages.RemoveAt(n);
372 
373         if(m_current_page == static_cast<int>(n))
374         {
375             m_current_page = -1;
376 
377             if(m_pages.GetCount() > 0)
378             {
379                 if(n >= m_pages.GetCount())
380                 {
381                     SetActivePage(m_pages.GetCount() - 1);
382                 }
383                 else
384                 {
385                     SetActivePage(n - 1);
386                 }
387             }
388         }
389         else if(m_current_page > static_cast<int>(n))
390         {
391             m_current_page--;
392         }
393     }
394 }
395 
ClearPages()396 void wxRibbonBar::ClearPages()
397 {
398     size_t i;
399     for(i=0; i<m_pages.GetCount(); i++)
400     {
401         wxRibbonPage *page = m_pages.Item(i).page;
402         // Schedule page object for destruction and not destroying directly
403         // as this function can be called in an event handler and page functions
404         // can be called afeter removing.
405         // Like in wxRibbonButtonBar::OnMouseUp
406         if(!wxTheApp->IsScheduledForDestruction(page))
407         {
408             wxTheApp->ScheduleForDestruction(page);
409         }
410     }
411     m_pages.Empty();
412     Realize();
413     m_current_page = -1;
414     Refresh();
415 }
416 
SetActivePage(size_t page)417 bool wxRibbonBar::SetActivePage(size_t page)
418 {
419     if(m_current_page == (int)page)
420     {
421         return true;
422     }
423 
424     if(page >= m_pages.GetCount())
425     {
426         return false;
427     }
428 
429     if(m_current_page != -1)
430     {
431         m_pages.Item((size_t)m_current_page).active = false;
432         m_pages.Item((size_t)m_current_page).page->Hide();
433     }
434     m_current_page = (int)page;
435     m_pages.Item(page).active = true;
436     m_pages.Item(page).shown = true;
437     {
438         wxRibbonPage* wnd = m_pages.Item(page).page;
439         RepositionPage(wnd);
440         wnd->Layout();
441         wnd->Show();
442     }
443     Refresh();
444 
445     return true;
446 }
447 
SetActivePage(wxRibbonPage * page)448 bool wxRibbonBar::SetActivePage(wxRibbonPage* page)
449 {
450     size_t numpages = m_pages.GetCount();
451     size_t i;
452     for(i = 0; i < numpages; ++i)
453     {
454         if(m_pages.Item(i).page == page)
455         {
456             return SetActivePage(i);
457         }
458     }
459     return false;
460 }
461 
GetPageNumber(wxRibbonPage * page) const462 int wxRibbonBar::GetPageNumber(wxRibbonPage* page) const
463 {
464     size_t numpages = m_pages.GetCount();
465     for(size_t i = 0; i < numpages; ++i)
466     {
467         if(m_pages.Item(i).page == page)
468         {
469             return i;
470         }
471     }
472     return wxNOT_FOUND;
473 }
474 
475 
GetActivePage() const476 int wxRibbonBar::GetActivePage() const
477 {
478     return m_current_page;
479 }
480 
SetTabCtrlMargins(int left,int right)481 void wxRibbonBar::SetTabCtrlMargins(int left, int right)
482 {
483     m_tab_margin_left = left;
484     m_tab_margin_right = right;
485 
486     RecalculateTabSizes();
487 }
488 
489 struct PageComparedBySmallWidthAsc
490 {
PageComparedBySmallWidthAscPageComparedBySmallWidthAsc491     explicit PageComparedBySmallWidthAsc(wxRibbonPageTabInfo* page)
492         : m_page(page)
493     {
494     }
495 
operator <PageComparedBySmallWidthAsc496     bool operator<(const PageComparedBySmallWidthAsc& other) const
497     {
498         return m_page->small_must_have_separator_width
499                 < other.m_page->small_must_have_separator_width;
500     }
501 
502     wxRibbonPageTabInfo *m_page;
503 };
504 
RecalculateTabSizes()505 void wxRibbonBar::RecalculateTabSizes()
506 {
507     size_t numtabs = m_pages.GetCount();
508 
509     if(numtabs == 0)
510         return;
511 
512     int width = GetSize().GetWidth() - m_tab_margin_left - m_tab_margin_right;
513     int tabsep = m_art->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE);
514     int x = m_tab_margin_left;
515     const int y = 0;
516 
517     if(width >= m_tabs_total_width_ideal)
518     {
519         // Simple case: everything at ideal width
520         size_t i;
521         for(i = 0; i < numtabs; ++i)
522         {
523             wxRibbonPageTabInfo& info = m_pages.Item(i);
524             if (!info.shown)
525                 continue;
526             info.rect.x = x;
527             info.rect.y = y;
528             info.rect.width = info.ideal_width;
529             info.rect.height = m_tab_height;
530             x += info.rect.width + tabsep;
531         }
532         m_tab_scroll_buttons_shown = false;
533         m_tab_scroll_left_button_rect.SetWidth(0);
534         m_tab_scroll_right_button_rect.SetWidth(0);
535     }
536     else if(width < m_tabs_total_width_minimum)
537     {
538         // Simple case: everything minimum with scrollbar
539         size_t i;
540         for(i = 0; i < numtabs; ++i)
541         {
542             wxRibbonPageTabInfo& info = m_pages.Item(i);
543             if (!info.shown)
544                 continue;
545             info.rect.x = x;
546             info.rect.y = y;
547             info.rect.width = info.minimum_width;
548             info.rect.height = m_tab_height;
549             x += info.rect.width + tabsep;
550         }
551         if(!m_tab_scroll_buttons_shown)
552         {
553             m_tab_scroll_left_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
554             m_tab_scroll_right_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
555             m_tab_scroll_buttons_shown = true;
556         }
557         {
558             wxClientDC temp_dc(this);
559             int right_button_pos = GetClientSize().GetWidth() - m_tab_margin_right - m_tab_scroll_right_button_rect.GetWidth();
560             if ( right_button_pos < m_tab_margin_left )
561                 right_button_pos = m_tab_margin_left;
562 
563             m_tab_scroll_left_button_rect.SetWidth(m_art->GetScrollButtonMinimumSize(temp_dc, this, wxRIBBON_SCROLL_BTN_LEFT | wxRIBBON_SCROLL_BTN_NORMAL | wxRIBBON_SCROLL_BTN_FOR_TABS).GetWidth());
564             m_tab_scroll_left_button_rect.SetHeight(m_tab_height);
565             m_tab_scroll_left_button_rect.SetX(m_tab_margin_left);
566             m_tab_scroll_left_button_rect.SetY(0);
567             m_tab_scroll_right_button_rect.SetWidth(m_art->GetScrollButtonMinimumSize(temp_dc, this, wxRIBBON_SCROLL_BTN_RIGHT | wxRIBBON_SCROLL_BTN_NORMAL | wxRIBBON_SCROLL_BTN_FOR_TABS).GetWidth());
568             m_tab_scroll_right_button_rect.SetHeight(m_tab_height);
569             m_tab_scroll_right_button_rect.SetX(right_button_pos);
570             m_tab_scroll_right_button_rect.SetY(0);
571         }
572         if(m_tab_scroll_amount == 0)
573         {
574             m_tab_scroll_left_button_rect.SetWidth(0);
575         }
576         else if(m_tab_scroll_amount + width >= m_tabs_total_width_minimum)
577         {
578             m_tab_scroll_amount = m_tabs_total_width_minimum - width;
579             m_tab_scroll_right_button_rect.SetX(m_tab_scroll_right_button_rect.GetX() + m_tab_scroll_right_button_rect.GetWidth());
580             m_tab_scroll_right_button_rect.SetWidth(0);
581         }
582         for(i = 0; i < numtabs; ++i)
583         {
584             wxRibbonPageTabInfo& info = m_pages.Item(i);
585             if (!info.shown)
586                 continue;
587             info.rect.x -= m_tab_scroll_amount;
588         }
589     }
590     else
591     {
592         m_tab_scroll_buttons_shown = false;
593         m_tab_scroll_left_button_rect.SetWidth(0);
594         m_tab_scroll_right_button_rect.SetWidth(0);
595         // Complex case: everything sized such that: minimum <= width < ideal
596         /*
597            Strategy:
598              1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
599              2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
600              3) Uniformly reduce all tabs down to their minimum width
601         */
602         int smallest_tab_width = INT_MAX;
603         int total_small_width = tabsep * (numtabs - 1);
604         size_t i;
605         for(i = 0; i < numtabs; ++i)
606         {
607             wxRibbonPageTabInfo& info = m_pages.Item(i);
608             if (!info.shown)
609                 continue;
610             if(info.small_must_have_separator_width < smallest_tab_width)
611             {
612                 smallest_tab_width = info.small_must_have_separator_width;
613             }
614             total_small_width += info.small_must_have_separator_width;
615         }
616         if(width >= total_small_width)
617         {
618             // Do (1)
619             int total_delta = m_tabs_total_width_ideal - total_small_width;
620             total_small_width -= tabsep * (numtabs - 1);
621             width -= tabsep * (numtabs - 1);
622             for(i = 0; i < numtabs; ++i)
623             {
624                 wxRibbonPageTabInfo& info = m_pages.Item(i);
625                 if (!info.shown)
626                     continue;
627                 int delta = info.ideal_width - info.small_must_have_separator_width;
628                 info.rect.x = x;
629                 info.rect.y = y;
630                 info.rect.width = info.small_must_have_separator_width + delta * (width - total_small_width) / total_delta;
631                 info.rect.height = m_tab_height;
632 
633                 x += info.rect.width + tabsep;
634                 total_delta -= delta;
635                 total_small_width -= info.small_must_have_separator_width;
636                 width -= info.rect.width;
637             }
638         }
639         else
640         {
641             total_small_width = tabsep * (numtabs - 1);
642             for(i = 0; i < numtabs; ++i)
643             {
644                 wxRibbonPageTabInfo& info = m_pages.Item(i);
645                 if (!info.shown)
646                     continue;
647                 if(info.minimum_width < smallest_tab_width)
648                 {
649                     total_small_width += smallest_tab_width;
650                 }
651                 else
652                 {
653                     total_small_width += info.minimum_width;
654                 }
655             }
656             if(width >= total_small_width)
657             {
658                 // Do (2)
659                 wxVector<PageComparedBySmallWidthAsc> sorted_pages;
660                 sorted_pages.reserve(numtabs);
661                 for ( i = 0; i < numtabs; ++i )
662                     sorted_pages.push_back(PageComparedBySmallWidthAsc(&m_pages.Item(i)));
663 
664                 wxVectorSort(sorted_pages);
665                 width -= tabsep * (numtabs - 1);
666                 for(i = 0; i < numtabs; ++i)
667                 {
668                     wxRibbonPageTabInfo* info = sorted_pages[i].m_page;
669                     if (!info->shown)
670                         continue;
671                     if(info->small_must_have_separator_width * (int)(numtabs - i) <= width)
672                     {
673                         info->rect.width = info->small_must_have_separator_width;
674                     }
675                     else
676                     {
677                         info->rect.width = width / (numtabs - i);
678                     }
679                     width -= info->rect.width;
680                 }
681                 for(i = 0; i < numtabs; ++i)
682                 {
683                     wxRibbonPageTabInfo& info = m_pages.Item(i);
684                     if (!info.shown)
685                         continue;
686                     info.rect.x = x;
687                     info.rect.y = y;
688                     info.rect.height = m_tab_height;
689                     x += info.rect.width + tabsep;
690                 }
691             }
692             else
693             {
694                 // Do (3)
695                 total_small_width = (smallest_tab_width + tabsep) * numtabs - tabsep;
696                 int total_delta = total_small_width - m_tabs_total_width_minimum;
697                 total_small_width = m_tabs_total_width_minimum - tabsep * (numtabs - 1);
698                 width -= tabsep * (numtabs - 1);
699                 for(i = 0; i < numtabs; ++i)
700                 {
701                     wxRibbonPageTabInfo& info = m_pages.Item(i);
702                     if (!info.shown)
703                         continue;
704                     int delta = smallest_tab_width - info.minimum_width;
705                     info.rect.x = x;
706                     info.rect.y = y;
707                     info.rect.width = info.minimum_width + delta * (width - total_small_width) / total_delta;
708                     info.rect.height = m_tab_height;
709 
710                     x += info.rect.width + tabsep;
711                     total_delta -= delta;
712                     total_small_width -= info.minimum_width;
713                     width -= info.rect.width;
714                 }
715             }
716         }
717     }
718 }
719 
wxRibbonBar()720 wxRibbonBar::wxRibbonBar()
721 {
722     m_flags = 0;
723     m_tabs_total_width_ideal = 0;
724     m_tabs_total_width_minimum = 0;
725     m_tab_margin_left = 0;
726     m_tab_margin_right = 0;
727     m_tab_height = 0;
728     m_tab_scroll_amount = 0;
729     m_current_page = -1;
730     m_current_hovered_page = -1;
731     m_tab_scroll_left_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
732     m_tab_scroll_right_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
733     m_tab_scroll_buttons_shown = false;
734     m_arePanelsShown = true;
735     m_help_button_hovered = false;
736 
737 }
738 
wxRibbonBar(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)739 wxRibbonBar::wxRibbonBar(wxWindow* parent,
740                          wxWindowID id,
741                          const wxPoint& pos,
742                          const wxSize& size,
743                          long style)
744     : wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
745 {
746     CommonInit(style);
747 }
748 
~wxRibbonBar()749 wxRibbonBar::~wxRibbonBar()
750 {
751     SetArtProvider(NULL);
752 
753     for ( size_t n = 0; n < m_image_lists.size(); ++n )
754     {
755         delete m_image_lists[n];
756     }
757 }
758 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)759 bool wxRibbonBar::Create(wxWindow* parent,
760                 wxWindowID id,
761                 const wxPoint& pos,
762                 const wxSize& size,
763                 long style)
764 {
765     if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
766         return false;
767 
768     CommonInit(style);
769 
770     return true;
771 }
772 
CommonInit(long style)773 void wxRibbonBar::CommonInit(long style)
774 {
775     SetName(wxT("wxRibbonBar"));
776 
777     m_flags = style;
778     m_tabs_total_width_ideal = 0;
779     m_tabs_total_width_minimum = 0;
780     m_tab_margin_left = 50;
781     m_tab_margin_right = 20;
782     if ( m_flags & wxRIBBON_BAR_SHOW_TOGGLE_BUTTON )
783         m_tab_margin_right += 20;
784     if ( m_flags & wxRIBBON_BAR_SHOW_HELP_BUTTON )
785         m_tab_margin_right += 20;
786     m_tab_height = 20; // initial guess
787     m_tab_scroll_amount = 0;
788     m_current_page = -1;
789     m_current_hovered_page = -1;
790     m_tab_scroll_left_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
791     m_tab_scroll_right_button_state = wxRIBBON_SCROLL_BTN_NORMAL;
792     m_tab_scroll_buttons_shown = false;
793     m_arePanelsShown = true;
794 
795     if(m_art == NULL)
796     {
797         SetArtProvider(new wxRibbonDefaultArtProvider);
798     }
799     SetBackgroundStyle(wxBG_STYLE_PAINT);
800 
801     m_toggle_button_hovered = false;
802     m_bar_hovered = false;
803 
804     m_ribbon_state = wxRIBBON_BAR_PINNED;
805 }
806 
GetButtonImageList(wxSize size)807 wxImageList* wxRibbonBar::GetButtonImageList(wxSize size)
808 {
809     for ( size_t n = 0; n < m_image_lists.size(); ++n )
810     {
811         if ( m_image_lists[n]->GetSize() == size )
812             return m_image_lists[n];
813     }
814 
815     wxImageList* const
816         il = new wxImageList(size.GetWidth(), size.GetHeight(), /*mask*/false);
817     m_image_lists.push_back(il);
818 
819     return il;
820 }
821 
SetArtProvider(wxRibbonArtProvider * art)822 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider* art)
823 {
824     wxRibbonArtProvider *old = m_art;
825     m_art = art;
826 
827     if(art)
828     {
829         art->SetFlags(m_flags);
830     }
831     size_t numpages = m_pages.GetCount();
832     size_t i;
833     for(i = 0; i < numpages; ++i)
834     {
835         wxRibbonPage *page = m_pages.Item(i).page;
836         if(page->GetArtProvider() != art)
837         {
838             page->SetArtProvider(art);
839         }
840     }
841 
842     delete old;
843 }
844 
OnPaint(wxPaintEvent & WXUNUSED (evt))845 void wxRibbonBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
846 {
847     wxAutoBufferedPaintDC dc(this);
848 
849     if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height) == wxOutRegion)
850     {
851         // Nothing to do in the tab area, and the page area is handled by the active page
852         return;
853     }
854 
855     DoEraseBackground(dc);
856 
857     if ( m_flags & wxRIBBON_BAR_SHOW_HELP_BUTTON  )
858         m_help_button_rect = m_art->GetRibbonHelpButtonArea(GetSize());
859     if ( m_flags & wxRIBBON_BAR_SHOW_TOGGLE_BUTTON  )
860         m_toggle_button_rect = m_art->GetBarToggleButtonArea(GetSize());
861 
862     size_t numtabs = m_pages.GetCount();
863     double sep_visibility = 0.0;
864     bool draw_sep = false;
865     wxRect tabs_rect(m_tab_margin_left, 0, GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right, m_tab_height);
866     if(m_tab_scroll_buttons_shown)
867     {
868         tabs_rect.x += m_tab_scroll_left_button_rect.GetWidth();
869         tabs_rect.width -= m_tab_scroll_left_button_rect.GetWidth() + m_tab_scroll_right_button_rect.GetWidth();
870     }
871     size_t i;
872     for(i = 0; i < numtabs; ++i)
873     {
874         wxRibbonPageTabInfo& info = m_pages.Item(i);
875         if (!info.shown)
876             continue;
877 
878         dc.DestroyClippingRegion();
879         if(m_tab_scroll_buttons_shown)
880         {
881             if(!tabs_rect.Intersects(info.rect))
882                 continue;
883             dc.SetClippingRegion(tabs_rect);
884         }
885         dc.SetClippingRegion(info.rect);
886         m_art->DrawTab(dc, this, info);
887 
888         if(info.rect.width < info.small_begin_need_separator_width)
889         {
890             draw_sep = true;
891             if(info.rect.width < info.small_must_have_separator_width)
892             {
893                 sep_visibility += 1.0;
894             }
895             else
896             {
897                 sep_visibility += (double)(info.small_begin_need_separator_width - info.rect.width) / (double)(info.small_begin_need_separator_width - info.small_must_have_separator_width);
898             }
899         }
900     }
901     if(draw_sep)
902     {
903         wxRect rect = m_pages.Item(0).rect;
904         rect.width = m_art->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE);
905         sep_visibility /= (double)numtabs;
906         for(i = 0; i < numtabs - 1; ++i)
907         {
908             wxRibbonPageTabInfo& info = m_pages.Item(i);
909             if (!info.shown)
910                 continue;
911             rect.x = info.rect.x + info.rect.width;
912 
913             if(m_tab_scroll_buttons_shown && !tabs_rect.Intersects(rect))
914             {
915                 continue;
916             }
917 
918             dc.DestroyClippingRegion();
919             dc.SetClippingRegion(rect);
920             m_art->DrawTabSeparator(dc, this, rect, sep_visibility);
921         }
922     }
923     if(m_tab_scroll_buttons_shown)
924     {
925         if(m_tab_scroll_left_button_rect.GetWidth() != 0)
926         {
927             dc.DestroyClippingRegion();
928             dc.SetClippingRegion(m_tab_scroll_left_button_rect);
929             m_art->DrawScrollButton(dc, this, m_tab_scroll_left_button_rect, wxRIBBON_SCROLL_BTN_LEFT | m_tab_scroll_left_button_state | wxRIBBON_SCROLL_BTN_FOR_TABS);
930         }
931         if(m_tab_scroll_right_button_rect.GetWidth() != 0)
932         {
933             dc.DestroyClippingRegion();
934             dc.SetClippingRegion(m_tab_scroll_right_button_rect);
935             m_art->DrawScrollButton(dc, this, m_tab_scroll_right_button_rect, wxRIBBON_SCROLL_BTN_RIGHT | m_tab_scroll_right_button_state | wxRIBBON_SCROLL_BTN_FOR_TABS);
936         }
937     }
938 
939     if ( m_flags & wxRIBBON_BAR_SHOW_HELP_BUTTON  )
940         m_art->DrawHelpButton(dc, this, m_help_button_rect);
941     if ( m_flags & wxRIBBON_BAR_SHOW_TOGGLE_BUTTON  )
942         m_art->DrawToggleButton(dc, this, m_toggle_button_rect, m_ribbon_state);
943 
944 }
945 
OnEraseBackground(wxEraseEvent & WXUNUSED (evt))946 void wxRibbonBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
947 {
948     // Background painting done in main paint handler to reduce screen flicker
949 }
950 
DoEraseBackground(wxDC & dc)951 void wxRibbonBar::DoEraseBackground(wxDC& dc)
952 {
953     wxRect tabs(GetSize());
954     tabs.height = m_tab_height;
955     m_art->DrawTabCtrlBackground(dc, this, tabs);
956 }
957 
OnSize(wxSizeEvent & evt)958 void wxRibbonBar::OnSize(wxSizeEvent& evt)
959 {
960     RecalculateTabSizes();
961     if(m_current_page != -1)
962     {
963         RepositionPage(m_pages.Item(m_current_page).page);
964     }
965     RefreshTabBar();
966 
967     evt.Skip();
968 }
969 
RepositionPage(wxRibbonPage * page)970 void wxRibbonBar::RepositionPage(wxRibbonPage *page)
971 {
972     int w, h;
973     GetSize(&w, &h);
974     page->SetSizeWithScrollButtonAdjustment(0, m_tab_height, w, h - m_tab_height);
975 }
976 
HitTestTabs(wxPoint position,int * index)977 wxRibbonPageTabInfo* wxRibbonBar::HitTestTabs(wxPoint position, int* index)
978 {
979     wxRect tabs_rect(m_tab_margin_left, 0, GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right, m_tab_height);
980     if(m_tab_scroll_buttons_shown)
981     {
982         tabs_rect.SetX(tabs_rect.GetX() + m_tab_scroll_left_button_rect.GetWidth());
983         tabs_rect.SetWidth(tabs_rect.GetWidth() - m_tab_scroll_left_button_rect.GetWidth() - m_tab_scroll_right_button_rect.GetWidth());
984     }
985     if(tabs_rect.Contains(position))
986     {
987         size_t numtabs = m_pages.GetCount();
988         size_t i;
989         for(i = 0; i < numtabs; ++i)
990         {
991             wxRibbonPageTabInfo& info = m_pages.Item(i);
992             if (!info.shown)
993                 continue;
994             if(info.rect.Contains(position))
995             {
996                 if(index != NULL)
997                 {
998                     *index = (int)i;
999                 }
1000                 return &info;
1001             }
1002         }
1003     }
1004     if(index != NULL)
1005     {
1006         *index = -1;
1007     }
1008     return NULL;
1009 }
1010 
OnMouseLeftDown(wxMouseEvent & evt)1011 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent& evt)
1012 {
1013     wxRibbonPageTabInfo *tab = HitTestTabs(evt.GetPosition());
1014     SetFocus();
1015     if ( tab )
1016     {
1017         if ( m_ribbon_state == wxRIBBON_BAR_MINIMIZED )
1018         {
1019             ShowPanels(wxRIBBON_BAR_EXPANDED);
1020         }
1021         else if ( (tab == &m_pages.Item(m_current_page)) && (m_ribbon_state == wxRIBBON_BAR_EXPANDED) )
1022         {
1023             HidePanels();
1024         }
1025     }
1026     else
1027     {
1028         if ( m_ribbon_state == wxRIBBON_BAR_EXPANDED )
1029         {
1030             HidePanels();
1031         }
1032     }
1033     if(tab && tab != &m_pages.Item(m_current_page))
1034     {
1035         wxRibbonBarEvent query(wxEVT_RIBBONBAR_PAGE_CHANGING, GetId(), tab->page);
1036         query.SetEventObject(this);
1037         ProcessWindowEvent(query);
1038         if(query.IsAllowed())
1039         {
1040             SetActivePage(query.GetPage());
1041 
1042             wxRibbonBarEvent notification(wxEVT_RIBBONBAR_PAGE_CHANGED, GetId(), m_pages.Item(m_current_page).page);
1043             notification.SetEventObject(this);
1044             ProcessWindowEvent(notification);
1045         }
1046     }
1047     else if(tab == NULL)
1048     {
1049         if(m_tab_scroll_left_button_rect.Contains(evt.GetPosition()))
1050         {
1051             m_tab_scroll_left_button_state |= wxRIBBON_SCROLL_BTN_ACTIVE | wxRIBBON_SCROLL_BTN_HOVERED;
1052             RefreshTabBar();
1053         }
1054         else if(m_tab_scroll_right_button_rect.Contains(evt.GetPosition()))
1055         {
1056             m_tab_scroll_right_button_state |= wxRIBBON_SCROLL_BTN_ACTIVE | wxRIBBON_SCROLL_BTN_HOVERED;
1057             RefreshTabBar();
1058         }
1059     }
1060 
1061     wxPoint position = evt.GetPosition();
1062 
1063     if(position.x >= 0 && position.y >= 0)
1064     {
1065         wxSize size = GetSize();
1066         if(position.x < size.GetWidth() && position.y < size.GetHeight())
1067         {
1068             if(m_toggle_button_rect.Contains(position))
1069             {
1070                 ShowPanels(ArePanelsShown() ? wxRIBBON_BAR_MINIMIZED : wxRIBBON_BAR_PINNED);
1071                 wxRibbonBarEvent event(wxEVT_RIBBONBAR_TOGGLED, GetId());
1072                 event.SetEventObject(this);
1073                 ProcessWindowEvent(event);
1074             }
1075             if ( m_help_button_rect.Contains(position) )
1076             {
1077                 wxRibbonBarEvent event(wxEVT_RIBBONBAR_HELP_CLICK, GetId());
1078                 event.SetEventObject(this);
1079                 ProcessWindowEvent(event);
1080             }
1081         }
1082     }
1083 }
1084 
OnMouseLeftUp(wxMouseEvent & WXUNUSED (evt))1085 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent& WXUNUSED(evt))
1086 {
1087     if(!m_tab_scroll_buttons_shown)
1088     {
1089         return;
1090     }
1091 
1092     int amount = 0;
1093     if(m_tab_scroll_left_button_state & wxRIBBON_SCROLL_BTN_ACTIVE)
1094     {
1095         amount = -1;
1096     }
1097     else if(m_tab_scroll_right_button_state & wxRIBBON_SCROLL_BTN_ACTIVE)
1098     {
1099         amount = 1;
1100     }
1101     if(amount != 0)
1102     {
1103         m_tab_scroll_left_button_state &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
1104         m_tab_scroll_right_button_state &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
1105         ScrollTabBar(amount * 8);
1106     }
1107 }
1108 
ScrollTabBar(int amount)1109 void wxRibbonBar::ScrollTabBar(int amount)
1110 {
1111     bool show_left = true;
1112     bool show_right = true;
1113     if(m_tab_scroll_amount + amount <= 0)
1114     {
1115         amount = -m_tab_scroll_amount;
1116         show_left = false;
1117     }
1118     else if(m_tab_scroll_amount + amount + (GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right) >= m_tabs_total_width_minimum)
1119     {
1120         amount = m_tabs_total_width_minimum - m_tab_scroll_amount - (GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right);
1121         show_right = false;
1122     }
1123     if(amount == 0)
1124     {
1125         return;
1126     }
1127     m_tab_scroll_amount += amount;
1128     size_t numtabs = m_pages.GetCount();
1129     size_t i;
1130     for(i = 0; i < numtabs; ++i)
1131     {
1132         wxRibbonPageTabInfo& info = m_pages.Item(i);
1133         if (!info.shown)
1134             continue;
1135         info.rect.SetX(info.rect.GetX() - amount);
1136     }
1137     if(show_right != (m_tab_scroll_right_button_rect.GetWidth() != 0) ||
1138         show_left != (m_tab_scroll_left_button_rect.GetWidth() != 0))
1139     {
1140         wxClientDC temp_dc(this);
1141         if(show_left)
1142         {
1143             m_tab_scroll_left_button_rect.SetWidth(m_art->GetScrollButtonMinimumSize(temp_dc, this, wxRIBBON_SCROLL_BTN_LEFT | wxRIBBON_SCROLL_BTN_NORMAL | wxRIBBON_SCROLL_BTN_FOR_TABS).GetWidth());
1144         }
1145         else
1146         {
1147             m_tab_scroll_left_button_rect.SetWidth(0);
1148         }
1149 
1150         if(show_right)
1151         {
1152             if(m_tab_scroll_right_button_rect.GetWidth() == 0)
1153             {
1154                 m_tab_scroll_right_button_rect.SetWidth(m_art->GetScrollButtonMinimumSize(temp_dc, this, wxRIBBON_SCROLL_BTN_RIGHT | wxRIBBON_SCROLL_BTN_NORMAL | wxRIBBON_SCROLL_BTN_FOR_TABS).GetWidth());
1155                 m_tab_scroll_right_button_rect.SetX(m_tab_scroll_right_button_rect.GetX() - m_tab_scroll_right_button_rect.GetWidth());
1156             }
1157         }
1158         else
1159         {
1160             if(m_tab_scroll_right_button_rect.GetWidth() != 0)
1161             {
1162                 m_tab_scroll_right_button_rect.SetX(m_tab_scroll_right_button_rect.GetX() + m_tab_scroll_right_button_rect.GetWidth());
1163                 m_tab_scroll_right_button_rect.SetWidth(0);
1164             }
1165         }
1166     }
1167 
1168     RefreshTabBar();
1169 }
1170 
RefreshTabBar()1171 void wxRibbonBar::RefreshTabBar()
1172 {
1173     wxRect tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height);
1174     Refresh(false, &tab_rect);
1175 }
1176 
OnMouseMiddleDown(wxMouseEvent & evt)1177 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent& evt)
1178 {
1179     DoMouseButtonCommon(evt, wxEVT_RIBBONBAR_TAB_MIDDLE_DOWN);
1180 }
1181 
OnMouseMiddleUp(wxMouseEvent & evt)1182 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent& evt)
1183 {
1184     DoMouseButtonCommon(evt, wxEVT_RIBBONBAR_TAB_MIDDLE_UP);
1185 }
1186 
OnMouseRightDown(wxMouseEvent & evt)1187 void wxRibbonBar::OnMouseRightDown(wxMouseEvent& evt)
1188 {
1189     DoMouseButtonCommon(evt, wxEVT_RIBBONBAR_TAB_RIGHT_DOWN);
1190 }
1191 
OnMouseRightUp(wxMouseEvent & evt)1192 void wxRibbonBar::OnMouseRightUp(wxMouseEvent& evt)
1193 {
1194     DoMouseButtonCommon(evt, wxEVT_RIBBONBAR_TAB_RIGHT_UP);
1195 }
1196 
OnMouseDoubleClick(wxMouseEvent & evt)1197 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent& evt)
1198 {
1199     wxRibbonPageTabInfo *tab = HitTestTabs(evt.GetPosition());
1200     SetFocus();
1201     if ( tab && tab == &m_pages.Item(m_current_page) )
1202     {
1203         if ( m_ribbon_state == wxRIBBON_BAR_PINNED )
1204         {
1205             HidePanels();
1206         }
1207         else
1208         {
1209             ShowPanels(wxRIBBON_BAR_PINNED);
1210         }
1211     }
1212 }
1213 
DoMouseButtonCommon(wxMouseEvent & evt,wxEventType tab_event_type)1214 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent& evt, wxEventType tab_event_type)
1215 {
1216     wxRibbonPageTabInfo *tab = HitTestTabs(evt.GetPosition());
1217     if(tab)
1218     {
1219         wxRibbonBarEvent notification(tab_event_type, GetId(), tab->page);
1220         notification.SetEventObject(this);
1221         ProcessWindowEvent(notification);
1222     }
1223 }
1224 
RecalculateMinSize()1225 void wxRibbonBar::RecalculateMinSize()
1226 {
1227     wxSize min_size(wxDefaultCoord, wxDefaultCoord);
1228     size_t numtabs = m_pages.GetCount();
1229     if(numtabs != 0)
1230     {
1231         min_size = m_pages.Item(0).page->GetMinSize();
1232 
1233         size_t i;
1234         for(i = 1; i < numtabs; ++i)
1235         {
1236             wxRibbonPageTabInfo& info = m_pages.Item(i);
1237             if (!info.shown)
1238                 continue;
1239             wxSize page_min = info.page->GetMinSize();
1240 
1241             min_size.x = wxMax(min_size.x, page_min.x);
1242             min_size.y = wxMax(min_size.y, page_min.y);
1243         }
1244     }
1245     if(min_size.y != wxDefaultCoord)
1246     {
1247         // TODO: Decide on best course of action when min height is unspecified
1248         // - should we specify it to the tab minimum, or leave it unspecified?
1249         min_size.IncBy(0, m_tab_height);
1250     }
1251 
1252     m_minWidth = min_size.GetWidth();
1253     m_minHeight = m_arePanelsShown ? min_size.GetHeight() : m_tab_height;
1254 }
1255 
DoGetBestSize() const1256 wxSize wxRibbonBar::DoGetBestSize() const
1257 {
1258     wxSize best(0, 0);
1259     if(m_current_page != -1)
1260     {
1261         best = m_pages.Item(m_current_page).page->GetBestSize();
1262     }
1263     if(best.GetHeight() == wxDefaultCoord)
1264     {
1265         best.SetHeight(m_tab_height);
1266     }
1267     else
1268     {
1269         best.IncBy(0, m_tab_height);
1270     }
1271     if(!m_arePanelsShown)
1272     {
1273         best.SetHeight(m_tab_height);
1274     }
1275     return best;
1276 }
1277 
HitTestRibbonButton(const wxRect & rect,const wxPoint & position,bool & hover_flag)1278 void wxRibbonBar::HitTestRibbonButton(const wxRect& rect, const wxPoint& position, bool &hover_flag)
1279 {
1280     bool hovered = false;
1281     if(position.x >= 0 && position.y >= 0)
1282     {
1283         wxSize size = GetSize();
1284         if(position.x < size.GetWidth() && position.y < size.GetHeight())
1285         {
1286             hovered = true;
1287         }
1288     }
1289     if(hovered)
1290     {
1291         bool toggle_button_hovered;
1292         toggle_button_hovered = rect.Contains(position);
1293 
1294         if ( hovered != m_bar_hovered || toggle_button_hovered != hover_flag )
1295         {
1296             m_bar_hovered = hovered;
1297             hover_flag = toggle_button_hovered;
1298             Refresh(false);
1299         }
1300     }
1301 }
1302 
HideIfExpanded()1303 void wxRibbonBar::HideIfExpanded()
1304 {
1305     if ( m_ribbon_state == wxRIBBON_BAR_EXPANDED)
1306         HidePanels();
1307 }
1308 
OnKillFocus(wxFocusEvent & WXUNUSED (evt))1309 void wxRibbonBar::OnKillFocus(wxFocusEvent& WXUNUSED(evt))
1310 {
1311     HideIfExpanded();
1312 }
1313 
1314 #endif // wxUSE_RIBBON
1315