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