1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/ribbon/bar.cpp 
   3 // Purpose:     Top-level component of the ribbon-bar-style interface 
   4 // Author:      Peter Cawley 
   8 // Copyright:   (C) Peter Cawley 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  20 #include "wx/ribbon/bar.h" 
  21 #include "wx/ribbon/art.h" 
  22 #include "wx/dcbuffer.h" 
  28 #include "wx/msw/private.h" 
  31 #include "wx/arrimpl.cpp" 
  33 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray
) 
  35 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, wxRibbonBarEvent
); 
  36 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, wxRibbonBarEvent
); 
  37 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
, wxRibbonBarEvent
); 
  38 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
, wxRibbonBarEvent
); 
  39 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
, wxRibbonBarEvent
); 
  40 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
, wxRibbonBarEvent
); 
  42 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
) 
  43 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
) 
  45 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
) 
  46   EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
) 
  47   EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
) 
  48   EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
) 
  49   EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
) 
  50   EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
) 
  51   EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
) 
  52   EVT_MOTION(wxRibbonBar::OnMouseMove
) 
  53   EVT_PAINT(wxRibbonBar::OnPaint
) 
  54   EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
) 
  55   EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
) 
  56   EVT_SIZE(wxRibbonBar::OnSize
) 
  59 void wxRibbonBar::AddPage(wxRibbonPage 
*page
) 
  61     wxRibbonPageTabInfo info
; 
  66     // info.rect not set (intentional) 
  68     wxClientDC 
dcTemp(this); 
  69     wxString label 
= wxEmptyString
; 
  70     if(m_flags 
& wxRIBBON_BAR_SHOW_PAGE_LABELS
) 
  71         label 
= page
->GetLabel(); 
  72     wxBitmap icon 
= wxNullBitmap
; 
  73     if(m_flags 
& wxRIBBON_BAR_SHOW_PAGE_ICONS
) 
  74         icon 
= page
->GetIcon(); 
  75     m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
, 
  77                           &info
.small_begin_need_separator_width
, 
  78                           &info
.small_must_have_separator_width
, 
  83         m_tabs_total_width_ideal 
= info
.ideal_width
; 
  84         m_tabs_total_width_minimum 
= info
.minimum_width
; 
  88         int sep 
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
); 
  89         m_tabs_total_width_ideal 
+= sep 
+ info
.ideal_width
; 
  90         m_tabs_total_width_minimum 
+= sep 
+ info
.minimum_width
; 
  94     page
->Hide(); // Most likely case is that this new page is not the active tab 
  95     page
->SetArtProvider(m_art
); 
  97     if(m_pages
.GetCount() == 1) 
  99         SetActivePage((size_t)0); 
 103 bool wxRibbonBar::DismissExpandedPanel() 
 105     if(m_current_page 
== -1) 
 107     return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel(); 
 110 void wxRibbonBar::SetWindowStyleFlag(long style
) 
 114         m_art
->SetFlags(style
); 
 117 long wxRibbonBar::GetWindowStyleFlag() const 
 122 bool wxRibbonBar::Realize() 
 126     wxClientDC 
dcTemp(this); 
 127     int sep 
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
); 
 128     size_t numtabs 
= m_pages
.GetCount(); 
 130     for(i 
= 0; i 
< numtabs
; ++i
) 
 132         wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 133         RepositionPage(info
.page
); 
 134         if(!info
.page
->Realize()) 
 138         wxString label 
= wxEmptyString
; 
 139         if(m_flags 
& wxRIBBON_BAR_SHOW_PAGE_LABELS
) 
 140             label 
= info
.page
->GetLabel(); 
 141         wxBitmap icon 
= wxNullBitmap
; 
 142         if(m_flags 
& wxRIBBON_BAR_SHOW_PAGE_ICONS
) 
 143             icon 
= info
.page
->GetIcon(); 
 144         m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
, 
 146                               &info
.small_begin_need_separator_width
, 
 147                               &info
.small_must_have_separator_width
, 
 148                               &info
.minimum_width
); 
 152             m_tabs_total_width_ideal 
= info
.ideal_width
; 
 153             m_tabs_total_width_minimum 
= info
.minimum_width
; 
 157             m_tabs_total_width_ideal 
+= sep 
+ info
.ideal_width
; 
 158             m_tabs_total_width_minimum 
+= sep 
+ info
.minimum_width
; 
 161     m_tab_height 
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
); 
 163     RecalculateMinSize(); 
 164     RecalculateTabSizes(); 
 170 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
) 
 174     int hovered_page 
= -1; 
 175     bool refresh_tabs 
= false; 
 178         // It is quite likely that the mouse moved a small amount and is still over the same tab 
 179         if(m_current_hovered_page 
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
)) 
 181             hovered_page 
= m_current_hovered_page
; 
 182             // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid 
 183             if(m_tab_scroll_buttons_shown
) 
 185                 if(x 
>= m_tab_scroll_right_button_rect
.GetX() || x 
< m_tab_scroll_left_button_rect
.GetRight()) 
 193             HitTestTabs(evt
.GetPosition(), &hovered_page
); 
 196     if(hovered_page 
!= m_current_hovered_page
) 
 198         if(m_current_hovered_page 
!= -1) 
 200             m_pages
.Item((int)m_current_hovered_page
).hovered 
= false; 
 202         m_current_hovered_page 
= hovered_page
; 
 203         if(m_current_hovered_page 
!= -1) 
 205             m_pages
.Item((int)m_current_hovered_page
).hovered 
= true; 
 209     if(m_tab_scroll_buttons_shown
) 
 211 #define SET_FLAG(variable, flag) \ 
 212     { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }} 
 213 #define UNSET_FLAG(variable, flag) \ 
 214     { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }} 
 216         if(m_tab_scroll_left_button_rect
.Contains(x
, y
)) 
 217             SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
) 
 219             UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
) 
 221         if(m_tab_scroll_right_button_rect
.Contains(x
, y
)) 
 222             SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
) 
 224             UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
) 
 234 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
)) 
 236     // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse 
 237     // can leave the window quickly and leave a tab in the hovered state. 
 238     bool refresh_tabs 
= false; 
 239     if(m_current_hovered_page 
!= -1) 
 241         m_pages
.Item((int)m_current_hovered_page
).hovered 
= false; 
 242         m_current_hovered_page 
= -1; 
 245     if(m_tab_scroll_left_button_state 
& wxRIBBON_SCROLL_BTN_HOVERED
) 
 247         m_tab_scroll_left_button_state 
&= ~wxRIBBON_SCROLL_BTN_HOVERED
; 
 250     if(m_tab_scroll_right_button_state 
& wxRIBBON_SCROLL_BTN_HOVERED
) 
 252         m_tab_scroll_right_button_state 
&= ~wxRIBBON_SCROLL_BTN_HOVERED
; 
 261 wxRibbonPage
* wxRibbonBar::GetPage(int n
) 
 263     if(n 
< 0 || (size_t)n 
>= m_pages
.GetCount()) 
 265     return m_pages
.Item(n
).page
; 
 268 bool wxRibbonBar::SetActivePage(size_t page
) 
 270     if(m_current_page 
== (int)page
) 
 275     if(page 
>= m_pages
.GetCount()) 
 280     if(m_current_page 
!= -1) 
 282         m_pages
.Item((size_t)m_current_page
).active 
= false; 
 283         m_pages
.Item((size_t)m_current_page
).page
->Hide(); 
 285     m_current_page 
= (int)page
; 
 286     m_pages
.Item(page
).active 
= true; 
 288         wxRibbonPage
* wnd 
= m_pages
.Item(page
).page
; 
 298 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
) 
 300     size_t numpages 
= m_pages
.GetCount(); 
 302     for(i 
= 0; i 
< numpages
; ++i
) 
 304         if(m_pages
.Item(i
).page 
== page
) 
 306             return SetActivePage(i
); 
 312 int wxRibbonBar::GetActivePage() const 
 314     return m_current_page
; 
 317 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
) 
 319     m_tab_margin_left 
= left
; 
 320     m_tab_margin_right 
= right
; 
 322     RecalculateTabSizes(); 
 325 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo 
**first
, wxRibbonPageTabInfo 
**second
) 
 327     return (**first
).small_must_have_separator_width 
- (**second
).small_must_have_separator_width
; 
 330 void wxRibbonBar::RecalculateTabSizes() 
 332     size_t numtabs 
= m_pages
.GetCount(); 
 337     int width 
= GetSize().GetWidth() - m_tab_margin_left 
- m_tab_margin_right
; 
 338     int tabsep 
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
); 
 339     int x 
= m_tab_margin_left
; 
 342     if(width 
>= m_tabs_total_width_ideal
) 
 344         // Simple case: everything at ideal width 
 346         for(i 
= 0; i 
< numtabs
; ++i
) 
 348             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 351             info
.rect
.width 
= info
.ideal_width
; 
 352             info
.rect
.height 
= m_tab_height
; 
 353             x 
+= info
.rect
.width 
+ tabsep
; 
 355         m_tab_scroll_buttons_shown 
= false; 
 356         m_tab_scroll_left_button_rect
.SetWidth(0); 
 357         m_tab_scroll_right_button_rect
.SetWidth(0); 
 359     else if(width 
< m_tabs_total_width_minimum
) 
 361         // Simple case: everything minimum with scrollbar 
 363         for(i 
= 0; i 
< numtabs
; ++i
) 
 365             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 368             info
.rect
.width 
= info
.minimum_width
; 
 369             info
.rect
.height 
= m_tab_height
; 
 370             x 
+= info
.rect
.width 
+ tabsep
; 
 372         if(!m_tab_scroll_buttons_shown
) 
 374             m_tab_scroll_left_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 375             m_tab_scroll_right_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 376             m_tab_scroll_buttons_shown 
= true; 
 379             wxClientDC 
temp_dc(this); 
 380             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()); 
 381             m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
); 
 382             m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
); 
 383             m_tab_scroll_left_button_rect
.SetY(0); 
 384             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()); 
 385             m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
); 
 386             m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right 
- m_tab_scroll_right_button_rect
.GetWidth()); 
 387             m_tab_scroll_right_button_rect
.SetY(0); 
 389         if(m_tab_scroll_amount 
== 0) 
 391             m_tab_scroll_left_button_rect
.SetWidth(0); 
 393         else if(m_tab_scroll_amount 
+ width 
>= m_tabs_total_width_minimum
) 
 395             m_tab_scroll_amount 
= m_tabs_total_width_minimum 
- width
; 
 396             m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth()); 
 397             m_tab_scroll_right_button_rect
.SetWidth(0); 
 399         for(i 
= 0; i 
< numtabs
; ++i
) 
 401             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 402             info
.rect
.x 
-= m_tab_scroll_amount
; 
 407         m_tab_scroll_buttons_shown 
= false; 
 408         m_tab_scroll_left_button_rect
.SetWidth(0); 
 409         m_tab_scroll_right_button_rect
.SetWidth(0); 
 410         // Complex case: everything sized such that: minimum <= width < ideal 
 413              1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width 
 414              2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum) 
 415              3) Uniformly reduce all tabs down to their minimum width 
 417         int smallest_tab_width 
= INT_MAX
; 
 418         int total_small_width 
= tabsep 
* (numtabs 
- 1); 
 420         for(i 
= 0; i 
< numtabs
; ++i
) 
 422             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 423             if(info
.small_must_have_separator_width 
< smallest_tab_width
) 
 425                 smallest_tab_width 
= info
.small_must_have_separator_width
; 
 427             total_small_width 
+= info
.small_must_have_separator_width
; 
 429         if(width 
>= total_small_width
) 
 432             int total_delta 
= m_tabs_total_width_ideal 
- total_small_width
; 
 433             total_small_width 
-= tabsep 
* (numtabs 
- 1); 
 434             width 
-= tabsep 
* (numtabs 
- 1); 
 435             for(i 
= 0; i 
< numtabs
; ++i
) 
 437                 wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 438                 int delta 
= info
.ideal_width 
- info
.small_must_have_separator_width
; 
 441                 info
.rect
.width 
= info
.small_must_have_separator_width 
+ delta 
* (width 
- total_small_width
) / total_delta
; 
 442                 info
.rect
.height 
= m_tab_height
; 
 444                 x 
+= info
.rect
.width 
+ tabsep
; 
 445                 total_delta 
-= delta
; 
 446                 total_small_width 
-= info
.small_must_have_separator_width
; 
 447                 width 
-= info
.rect
.width
; 
 452             total_small_width 
= tabsep 
* (numtabs 
- 1); 
 453             for(i 
= 0; i 
< numtabs
; ++i
) 
 455                 wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 456                 if(info
.minimum_width 
< smallest_tab_width
) 
 458                     total_small_width 
+= smallest_tab_width
; 
 462                     total_small_width 
+= info
.minimum_width
; 
 465             if(width 
>= total_small_width
) 
 468                 wxRibbonPageTabInfoArray sorted_pages
; 
 469                 for(i 
= 0; i 
< numtabs
; ++i
) 
 471                     // Sneaky obj array trickery to not copy the tab descriptors 
 472                     sorted_pages
.Add(&m_pages
.Item(i
)); 
 474                 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
); 
 475                 width 
-= tabsep 
* (numtabs 
- 1); 
 476                 for(i 
= 0; i 
< numtabs
; ++i
) 
 478                     wxRibbonPageTabInfo
& info 
= sorted_pages
.Item(i
); 
 479                     if(info
.small_must_have_separator_width 
* (int)(numtabs 
- i
) <= width
) 
 481                         info
.rect
.width 
= info
.small_must_have_separator_width
;; 
 485                         info
.rect
.width 
= width 
/ (numtabs 
- i
); 
 487                     width 
-= info
.rect
.width
; 
 489                 for(i 
= 0; i 
< numtabs
; ++i
) 
 491                     wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 494                     info
.rect
.height 
= m_tab_height
; 
 495                     x 
+= info
.rect
.width 
+ tabsep
; 
 496                     sorted_pages
.Detach(numtabs 
- (i 
+ 1)); 
 502                 total_small_width 
= (smallest_tab_width 
+ tabsep
) * numtabs 
- tabsep
; 
 503                 int total_delta 
= total_small_width 
- m_tabs_total_width_minimum
; 
 504                 total_small_width 
= m_tabs_total_width_minimum 
- tabsep 
* (numtabs 
- 1); 
 505                 width 
-= tabsep 
* (numtabs 
- 1); 
 506                 for(i 
= 0; i 
< numtabs
; ++i
) 
 508                     wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 509                     int delta 
= smallest_tab_width 
- info
.minimum_width
; 
 512                     info
.rect
.width 
= info
.minimum_width 
+ delta 
* (width 
- total_small_width
) / total_delta
; 
 513                     info
.rect
.height 
= m_tab_height
; 
 515                     x 
+= info
.rect
.width 
+ tabsep
; 
 516                     total_delta 
-= delta
; 
 517                     total_small_width 
-= info
.minimum_width
; 
 518                     width 
-= info
.rect
.width
; 
 525 wxRibbonBar::wxRibbonBar() 
 528     m_tabs_total_width_ideal 
= 0; 
 529     m_tabs_total_width_minimum 
= 0; 
 530     m_tab_margin_left 
= 0; 
 531     m_tab_margin_right 
= 0; 
 533     m_tab_scroll_amount 
= 0; 
 535     m_current_hovered_page 
= -1; 
 536     m_tab_scroll_left_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 537     m_tab_scroll_right_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 538     m_tab_scroll_buttons_shown 
= false; 
 541 wxRibbonBar::wxRibbonBar(wxWindow
* parent
, 
 546     : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
) 
 551 wxRibbonBar::~wxRibbonBar() 
 553     SetArtProvider(NULL
); 
 556 bool wxRibbonBar::Create(wxWindow
* parent
, 
 562     if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
)) 
 570 void wxRibbonBar::CommonInit(long style
) 
 572     SetName(wxT("wxRibbonBar")); 
 575     m_tabs_total_width_ideal 
= 0; 
 576     m_tabs_total_width_minimum 
= 0; 
 577     m_tab_margin_left 
= 50; 
 578     m_tab_margin_right 
= 20; 
 579     m_tab_height 
= 20; // initial guess 
 580     m_tab_scroll_amount 
= 0; 
 582     m_current_hovered_page 
= -1; 
 583     m_tab_scroll_left_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 584     m_tab_scroll_right_button_state 
= wxRIBBON_SCROLL_BTN_NORMAL
; 
 585     m_tab_scroll_buttons_shown 
= false; 
 589         SetArtProvider(new wxRibbonDefaultArtProvider
); 
 591     SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
 594 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
) 
 596     wxRibbonArtProvider 
*old 
= m_art
; 
 601         art
->SetFlags(m_flags
); 
 603     size_t numpages 
= m_pages
.GetCount(); 
 605     for(i 
= 0; i 
< numpages
; ++i
) 
 607         wxRibbonPage 
*page 
= m_pages
.Item(i
).page
; 
 608         if(page
->GetArtProvider() != art
) 
 610             page
->SetArtProvider(art
); 
 617 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
)) 
 619     wxAutoBufferedPaintDC 
dc(this); 
 621     if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
) 
 623         // Nothing to do in the tab area, and the page area is handled by the active page 
 627     DoEraseBackground(dc
); 
 629     size_t numtabs 
= m_pages
.GetCount(); 
 630     double sep_visibility 
= 0.0; 
 631     bool draw_sep 
= false; 
 632     wxRect 
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left 
- m_tab_margin_right
, m_tab_height
); 
 633     if(m_tab_scroll_buttons_shown
) 
 635         tabs_rect
.x 
+= m_tab_scroll_left_button_rect
.GetWidth(); 
 636         tabs_rect
.width 
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth(); 
 639     for(i 
= 0; i 
< numtabs
; ++i
) 
 641         wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 643         dc
.DestroyClippingRegion(); 
 644         if(m_tab_scroll_buttons_shown
) 
 646             if(!tabs_rect
.Intersects(info
.rect
)) 
 648             dc
.SetClippingRegion(tabs_rect
); 
 650         dc
.SetClippingRegion(info
.rect
); 
 651         m_art
->DrawTab(dc
, this, info
); 
 653         if(info
.rect
.width 
< info
.small_begin_need_separator_width
) 
 656             if(info
.rect
.width 
< info
.small_must_have_separator_width
) 
 658                 sep_visibility 
+= 1.0; 
 662                 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
); 
 668         wxRect rect 
= m_pages
.Item(0).rect
; 
 669         rect
.width 
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
); 
 670         sep_visibility 
/= (double)numtabs
; 
 671         for(i 
= 0; i 
< numtabs 
- 1; ++i
) 
 673             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 674             rect
.x 
= info
.rect
.x 
+ info
.rect
.width
; 
 676             if(m_tab_scroll_buttons_shown 
&& !tabs_rect
.Intersects(rect
)) 
 681             dc
.DestroyClippingRegion(); 
 682             dc
.SetClippingRegion(rect
); 
 683             m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
); 
 686     if(m_tab_scroll_buttons_shown
) 
 688         dc
.DestroyClippingRegion(); 
 689         if(m_tab_scroll_left_button_rect
.GetWidth() != 0) 
 691             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
); 
 693         if(m_tab_scroll_right_button_rect
.GetWidth() != 0) 
 695             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
); 
 700 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
)) 
 702     // Background painting done in main paint handler to reduce screen flicker 
 705 void wxRibbonBar::DoEraseBackground(wxDC
& dc
) 
 707     wxRect 
tabs(GetSize()); 
 708     tabs
.height 
= m_tab_height
; 
 709     m_art
->DrawTabCtrlBackground(dc
, this, tabs
); 
 712 void wxRibbonBar::OnSize(wxSizeEvent
& evt
) 
 714     RecalculateTabSizes(); 
 715     if(m_current_page 
!= -1) 
 717         RepositionPage(m_pages
.Item(m_current_page
).page
); 
 724 void wxRibbonBar::RepositionPage(wxRibbonPage 
*page
) 
 728     page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h 
- m_tab_height
); 
 731 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
) 
 733     wxRect 
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left 
- m_tab_margin_right
, m_tab_height
); 
 734     if(m_tab_scroll_buttons_shown
) 
 736         tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth()); 
 737         tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth()); 
 739     if(tabs_rect
.Contains(position
)) 
 741         size_t numtabs 
= m_pages
.GetCount(); 
 743         for(i 
= 0; i 
< numtabs
; ++i
) 
 745             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 746             if(info
.rect
.Contains(position
)) 
 763 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
) 
 765     wxRibbonPageTabInfo 
*tab 
= HitTestTabs(evt
.GetPosition()); 
 766     if(tab 
&& tab 
!= &m_pages
.Item(m_current_page
)) 
 768         wxRibbonBarEvent 
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
); 
 769         query
.SetEventObject(this); 
 770         ProcessWindowEvent(query
); 
 771         if(query
.IsAllowed()) 
 773             SetActivePage(query
.GetPage()); 
 775             wxRibbonBarEvent 
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
); 
 776             notification
.SetEventObject(this); 
 777             ProcessWindowEvent(notification
); 
 782         if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition())) 
 784             m_tab_scroll_left_button_state 
|= wxRIBBON_SCROLL_BTN_ACTIVE 
| wxRIBBON_SCROLL_BTN_HOVERED
; 
 787         else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition())) 
 789             m_tab_scroll_right_button_state 
|= wxRIBBON_SCROLL_BTN_ACTIVE 
| wxRIBBON_SCROLL_BTN_HOVERED
; 
 795 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
)) 
 797     if(!m_tab_scroll_buttons_shown
) 
 803     if(m_tab_scroll_left_button_state 
& wxRIBBON_SCROLL_BTN_ACTIVE
) 
 807     else if(m_tab_scroll_right_button_state 
& wxRIBBON_SCROLL_BTN_ACTIVE
) 
 813         m_tab_scroll_left_button_state 
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
; 
 814         m_tab_scroll_right_button_state 
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
; 
 815         ScrollTabBar(amount 
* 8); 
 819 void wxRibbonBar::ScrollTabBar(int amount
) 
 821     bool show_left 
= true; 
 822     bool show_right 
= true; 
 823     if(m_tab_scroll_amount 
+ amount 
<= 0) 
 825         amount 
= -m_tab_scroll_amount
; 
 828     else if(m_tab_scroll_amount 
+ amount 
+ (GetClientSize().GetWidth() - m_tab_margin_left 
- m_tab_margin_right
) >= m_tabs_total_width_minimum
) 
 830         amount 
= m_tabs_total_width_minimum 
- m_tab_scroll_amount 
- (GetClientSize().GetWidth() - m_tab_margin_left 
- m_tab_margin_right
); 
 837     m_tab_scroll_amount 
+= amount
; 
 838     size_t numtabs 
= m_pages
.GetCount(); 
 840     for(i 
= 0; i 
< numtabs
; ++i
) 
 842         wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 843         info
.rect
.SetX(info
.rect
.GetX() - amount
); 
 845     if(show_right 
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) || 
 846         show_left 
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0)) 
 848         wxClientDC 
temp_dc(this); 
 851             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()); 
 855             m_tab_scroll_left_button_rect
.SetWidth(0); 
 860             if(m_tab_scroll_right_button_rect
.GetWidth() == 0) 
 862                 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()); 
 863                 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth()); 
 868             if(m_tab_scroll_right_button_rect
.GetWidth() != 0) 
 870                 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth()); 
 871                 m_tab_scroll_right_button_rect
.SetWidth(0); 
 879 void wxRibbonBar::RefreshTabBar() 
 881     wxRect 
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
); 
 882     Refresh(false, &tab_rect
); 
 885 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
) 
 887     DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
); 
 890 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
) 
 892     DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
); 
 895 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
) 
 897     DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
); 
 900 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
) 
 902     DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
); 
 905 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
) 
 907     wxRibbonPageTabInfo 
*tab 
= HitTestTabs(evt
.GetPosition()); 
 910         wxRibbonBarEvent 
notification(tab_event_type
, GetId(), tab
->page
); 
 911         notification
.SetEventObject(this); 
 912         ProcessWindowEvent(notification
); 
 916 void wxRibbonBar::RecalculateMinSize() 
 918     wxSize 
min_size(wxDefaultCoord
, wxDefaultCoord
); 
 919     size_t numtabs 
= m_pages
.GetCount(); 
 922         min_size 
= m_pages
.Item(0).page
->GetMinSize(); 
 925         for(i 
= 1; i 
< numtabs
; ++i
) 
 927             wxRibbonPageTabInfo
& info 
= m_pages
.Item(i
); 
 928             wxSize page_min 
= info
.page
->GetMinSize(); 
 930             min_size
.x 
= wxMax(min_size
.x
, page_min
.x
); 
 931             min_size
.y 
= wxMax(min_size
.y
, page_min
.y
); 
 934     if(min_size
.y 
!= wxDefaultCoord
) 
 936         // TODO: Decide on best course of action when min height is unspecified 
 937         // - should we specify it to the tab minimum, or leave it unspecified? 
 938         min_size
.IncBy(0, m_tab_height
); 
 941     m_minWidth 
= min_size
.GetWidth(); 
 942     m_minHeight 
= min_size
.GetHeight(); 
 945 wxSize 
wxRibbonBar::DoGetBestSize() const 
 948     if(m_current_page 
!= -1) 
 950         best 
= m_pages
.Item(m_current_page
).page
->GetBestSize(); 
 952     if(best
.GetHeight() == wxDefaultCoord
) 
 954         best
.SetHeight(m_tab_height
); 
 958         best
.IncBy(0, m_tab_height
); 
 963 #endif // wxUSE_RIBBON