move everything inside wxUSE_RIBBON wrapper
[wxWidgets.git] / src / ribbon / bar.cpp
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 // RCS-ID: $Id$
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_RIBBON
19
20 #include "wx/ribbon/bar.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/dcbuffer.h"
23
24 #ifndef WX_PRECOMP
25 #endif
26
27 #ifdef __WXMSW__
28 #include "wx/msw/private.h"
29 #endif
30
31 #include "wx/arrimpl.cpp"
32
33 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray)
34
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);
41
42 IMPLEMENT_CLASS(wxRibbonBar, wxRibbonControl)
43 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent, wxNotifyEvent)
44
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)
57 END_EVENT_TABLE()
58
59 void wxRibbonBar::AddPage(wxRibbonPage *page)
60 {
61 wxRibbonPageTabInfo info;
62
63 info.page = page;
64 info.active = false;
65 info.hovered = false;
66 // info.rect not set (intentional)
67
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,
76 &info.ideal_width,
77 &info.small_begin_need_separator_width,
78 &info.small_must_have_separator_width,
79 &info.minimum_width);
80
81 if(m_pages.IsEmpty())
82 {
83 m_tabs_total_width_ideal = info.ideal_width;
84 m_tabs_total_width_minimum = info.minimum_width;
85 }
86 else
87 {
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;
91 }
92 m_pages.Add(info);
93
94 page->Hide(); // Most likely case is that this new page is not the active tab
95 page->SetArtProvider(m_art);
96
97 if(m_pages.GetCount() == 1)
98 {
99 SetActivePage((size_t)0);
100 }
101 }
102
103 bool wxRibbonBar::DismissExpandedPanel()
104 {
105 if(m_current_page == -1)
106 return false;
107 return m_pages.Item(m_current_page).page->DismissExpandedPanel();
108 }
109
110 void wxRibbonBar::SetWindowStyleFlag(long style)
111 {
112 m_flags = style;
113 if(m_art)
114 m_art->SetFlags(style);
115 }
116
117 long wxRibbonBar::GetWindowStyleFlag() const
118 {
119 return m_flags;
120 }
121
122 bool wxRibbonBar::Realize()
123 {
124 bool status = true;
125
126 wxClientDC dcTemp(this);
127 int sep = m_art->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE);
128 size_t numtabs = m_pages.GetCount();
129 size_t i;
130 for(i = 0; i < numtabs; ++i)
131 {
132 wxRibbonPageTabInfo& info = m_pages.Item(i);
133 RepositionPage(info.page);
134 if(!info.page->Realize())
135 {
136 status = false;
137 }
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,
145 &info.ideal_width,
146 &info.small_begin_need_separator_width,
147 &info.small_must_have_separator_width,
148 &info.minimum_width);
149
150 if(i == 0)
151 {
152 m_tabs_total_width_ideal = info.ideal_width;
153 m_tabs_total_width_minimum = info.minimum_width;
154 }
155 else
156 {
157 m_tabs_total_width_ideal += sep + info.ideal_width;
158 m_tabs_total_width_minimum += sep + info.minimum_width;
159 }
160 }
161 m_tab_height = m_art->GetTabCtrlHeight(dcTemp, this, m_pages);
162
163 RecalculateMinSize();
164 RecalculateTabSizes();
165 Refresh();
166
167 return status;
168 }
169
170 void wxRibbonBar::OnMouseMove(wxMouseEvent& evt)
171 {
172 int x = evt.GetX();
173 int y = evt.GetY();
174 int hovered_page = -1;
175 bool refresh_tabs = false;
176 if(y < m_tab_height)
177 {
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))
180 {
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)
184 {
185 if(x >= m_tab_scroll_right_button_rect.GetX() || x < m_tab_scroll_left_button_rect.GetRight())
186 {
187 hovered_page = -1;
188 }
189 }
190 }
191 else
192 {
193 HitTestTabs(evt.GetPosition(), &hovered_page);
194 }
195 }
196 if(hovered_page != m_current_hovered_page)
197 {
198 if(m_current_hovered_page != -1)
199 {
200 m_pages.Item((int)m_current_hovered_page).hovered = false;
201 }
202 m_current_hovered_page = hovered_page;
203 if(m_current_hovered_page != -1)
204 {
205 m_pages.Item((int)m_current_hovered_page).hovered = true;
206 }
207 refresh_tabs = true;
208 }
209 if(m_tab_scroll_buttons_shown)
210 {
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; }}
215
216 if(m_tab_scroll_left_button_rect.Contains(x, y))
217 SET_FLAG(m_tab_scroll_left_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
218 else
219 UNSET_FLAG(m_tab_scroll_left_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
220
221 if(m_tab_scroll_right_button_rect.Contains(x, y))
222 SET_FLAG(m_tab_scroll_right_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
223 else
224 UNSET_FLAG(m_tab_scroll_right_button_state, wxRIBBON_SCROLL_BTN_HOVERED)
225 #undef SET_FLAG
226 #undef UNSET_FLAG
227 }
228 if(refresh_tabs)
229 {
230 RefreshTabBar();
231 }
232 }
233
234 void wxRibbonBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
235 {
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)
240 {
241 m_pages.Item((int)m_current_hovered_page).hovered = false;
242 m_current_hovered_page = -1;
243 refresh_tabs = true;
244 }
245 if(m_tab_scroll_left_button_state & wxRIBBON_SCROLL_BTN_HOVERED)
246 {
247 m_tab_scroll_left_button_state &= ~wxRIBBON_SCROLL_BTN_HOVERED;
248 refresh_tabs = true;
249 }
250 if(m_tab_scroll_right_button_state & wxRIBBON_SCROLL_BTN_HOVERED)
251 {
252 m_tab_scroll_right_button_state &= ~wxRIBBON_SCROLL_BTN_HOVERED;
253 refresh_tabs = true;
254 }
255 if(refresh_tabs)
256 {
257 RefreshTabBar();
258 }
259 }
260
261 wxRibbonPage* wxRibbonBar::GetPage(int n)
262 {
263 if(n < 0 || (size_t)n >= m_pages.GetCount())
264 return 0;
265 return m_pages.Item(n).page;
266 }
267
268 bool wxRibbonBar::SetActivePage(size_t page)
269 {
270 if(m_current_page == (int)page)
271 {
272 return true;
273 }
274
275 if(page >= m_pages.GetCount())
276 {
277 return false;
278 }
279
280 if(m_current_page != -1)
281 {
282 m_pages.Item((size_t)m_current_page).active = false;
283 m_pages.Item((size_t)m_current_page).page->Hide();
284 }
285 m_current_page = (int)page;
286 m_pages.Item(page).active = true;
287 {
288 wxRibbonPage* wnd = m_pages.Item(page).page;
289 RepositionPage(wnd);
290 wnd->Layout();
291 wnd->Show();
292 }
293 Refresh();
294
295 return true;
296 }
297
298 bool wxRibbonBar::SetActivePage(wxRibbonPage* page)
299 {
300 size_t numpages = m_pages.GetCount();
301 size_t i;
302 for(i = 0; i < numpages; ++i)
303 {
304 if(m_pages.Item(i).page == page)
305 {
306 return SetActivePage(i);
307 }
308 }
309 return false;
310 }
311
312 int wxRibbonBar::GetActivePage() const
313 {
314 return m_current_page;
315 }
316
317 void wxRibbonBar::SetTabCtrlMargins(int left, int right)
318 {
319 m_tab_margin_left = left;
320 m_tab_margin_right = right;
321
322 RecalculateTabSizes();
323 }
324
325 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo **first, wxRibbonPageTabInfo **second)
326 {
327 return (**first).small_must_have_separator_width - (**second).small_must_have_separator_width;
328 }
329
330 void wxRibbonBar::RecalculateTabSizes()
331 {
332 size_t numtabs = m_pages.GetCount();
333
334 if(numtabs == 0)
335 return;
336
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;
340 const int y = 0;
341
342 if(width >= m_tabs_total_width_ideal)
343 {
344 // Simple case: everything at ideal width
345 size_t i;
346 for(i = 0; i < numtabs; ++i)
347 {
348 wxRibbonPageTabInfo& info = m_pages.Item(i);
349 info.rect.x = x;
350 info.rect.y = y;
351 info.rect.width = info.ideal_width;
352 info.rect.height = m_tab_height;
353 x += info.rect.width + tabsep;
354 }
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);
358 }
359 else if(width < m_tabs_total_width_minimum)
360 {
361 // Simple case: everything minimum with scrollbar
362 size_t i;
363 for(i = 0; i < numtabs; ++i)
364 {
365 wxRibbonPageTabInfo& info = m_pages.Item(i);
366 info.rect.x = x;
367 info.rect.y = y;
368 info.rect.width = info.minimum_width;
369 info.rect.height = m_tab_height;
370 x += info.rect.width + tabsep;
371 }
372 if(!m_tab_scroll_buttons_shown)
373 {
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;
377 }
378 {
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);
388 }
389 if(m_tab_scroll_amount == 0)
390 {
391 m_tab_scroll_left_button_rect.SetWidth(0);
392 }
393 else if(m_tab_scroll_amount + width >= m_tabs_total_width_minimum)
394 {
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);
398 }
399 for(i = 0; i < numtabs; ++i)
400 {
401 wxRibbonPageTabInfo& info = m_pages.Item(i);
402 info.rect.x -= m_tab_scroll_amount;
403 }
404 }
405 else
406 {
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
411 /*
412 Strategy:
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
416 */
417 int smallest_tab_width = INT_MAX;
418 int total_small_width = tabsep * (numtabs - 1);
419 size_t i;
420 for(i = 0; i < numtabs; ++i)
421 {
422 wxRibbonPageTabInfo& info = m_pages.Item(i);
423 if(info.small_must_have_separator_width < smallest_tab_width)
424 {
425 smallest_tab_width = info.small_must_have_separator_width;
426 }
427 total_small_width += info.small_must_have_separator_width;
428 }
429 if(width >= total_small_width)
430 {
431 // Do (1)
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)
436 {
437 wxRibbonPageTabInfo& info = m_pages.Item(i);
438 int delta = info.ideal_width - info.small_must_have_separator_width;
439 info.rect.x = x;
440 info.rect.y = y;
441 info.rect.width = info.small_must_have_separator_width + delta * (width - total_small_width) / total_delta;
442 info.rect.height = m_tab_height;
443
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;
448 }
449 }
450 else
451 {
452 total_small_width = tabsep * (numtabs - 1);
453 for(i = 0; i < numtabs; ++i)
454 {
455 wxRibbonPageTabInfo& info = m_pages.Item(i);
456 if(info.minimum_width < smallest_tab_width)
457 {
458 total_small_width += smallest_tab_width;
459 }
460 else
461 {
462 total_small_width += info.minimum_width;
463 }
464 }
465 if(width >= total_small_width)
466 {
467 // Do (2)
468 wxRibbonPageTabInfoArray sorted_pages;
469 for(i = 0; i < numtabs; ++i)
470 {
471 // Sneaky obj array trickery to not copy the tab descriptors
472 sorted_pages.Add(&m_pages.Item(i));
473 }
474 sorted_pages.Sort(OrderPageTabInfoBySmallWidthAsc);
475 width -= tabsep * (numtabs - 1);
476 for(i = 0; i < numtabs; ++i)
477 {
478 wxRibbonPageTabInfo& info = sorted_pages.Item(i);
479 if(info.small_must_have_separator_width * (int)(numtabs - i) <= width)
480 {
481 info.rect.width = info.small_must_have_separator_width;;
482 }
483 else
484 {
485 info.rect.width = width / (numtabs - i);
486 }
487 width -= info.rect.width;
488 }
489 for(i = 0; i < numtabs; ++i)
490 {
491 wxRibbonPageTabInfo& info = m_pages.Item(i);
492 info.rect.x = x;
493 info.rect.y = y;
494 info.rect.height = m_tab_height;
495 x += info.rect.width + tabsep;
496 sorted_pages.Detach(numtabs - (i + 1));
497 }
498 }
499 else
500 {
501 // Do (3)
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)
507 {
508 wxRibbonPageTabInfo& info = m_pages.Item(i);
509 int delta = smallest_tab_width - info.minimum_width;
510 info.rect.x = x;
511 info.rect.y = y;
512 info.rect.width = info.minimum_width + delta * (width - total_small_width) / total_delta;
513 info.rect.height = m_tab_height;
514
515 x += info.rect.width + tabsep;
516 total_delta -= delta;
517 total_small_width -= info.minimum_width;
518 width -= info.rect.width;
519 }
520 }
521 }
522 }
523 }
524
525 wxRibbonBar::wxRibbonBar()
526 {
527 m_flags = 0;
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;
532 m_tab_height = 0;
533 m_tab_scroll_amount = 0;
534 m_current_page = -1;
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;
539 }
540
541 wxRibbonBar::wxRibbonBar(wxWindow* parent,
542 wxWindowID id,
543 const wxPoint& pos,
544 const wxSize& size,
545 long style)
546 : wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
547 {
548 CommonInit(style);
549 }
550
551 wxRibbonBar::~wxRibbonBar()
552 {
553 SetArtProvider(NULL);
554 }
555
556 bool wxRibbonBar::Create(wxWindow* parent,
557 wxWindowID id,
558 const wxPoint& pos,
559 const wxSize& size,
560 long style)
561 {
562 if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
563 return false;
564
565 CommonInit(style);
566
567 return true;
568 }
569
570 void wxRibbonBar::CommonInit(long style)
571 {
572 SetName(wxT("wxRibbonBar"));
573
574 m_flags = style;
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;
581 m_current_page = -1;
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;
586
587 if(m_art == NULL)
588 {
589 SetArtProvider(new wxRibbonDefaultArtProvider);
590 }
591 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
592 }
593
594 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider* art)
595 {
596 wxRibbonArtProvider *old = m_art;
597 m_art = art;
598
599 if(art)
600 {
601 art->SetFlags(m_flags);
602 }
603 size_t numpages = m_pages.GetCount();
604 size_t i;
605 for(i = 0; i < numpages; ++i)
606 {
607 wxRibbonPage *page = m_pages.Item(i).page;
608 if(page->GetArtProvider() != art)
609 {
610 page->SetArtProvider(art);
611 }
612 }
613
614 delete old;
615 }
616
617 void wxRibbonBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
618 {
619 wxAutoBufferedPaintDC dc(this);
620
621 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height) == wxOutRegion)
622 {
623 // Nothing to do in the tab area, and the page area is handled by the active page
624 return;
625 }
626
627 DoEraseBackground(dc);
628
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)
634 {
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();
637 }
638 size_t i;
639 for(i = 0; i < numtabs; ++i)
640 {
641 wxRibbonPageTabInfo& info = m_pages.Item(i);
642
643 dc.DestroyClippingRegion();
644 if(m_tab_scroll_buttons_shown)
645 {
646 if(!tabs_rect.Intersects(info.rect))
647 continue;
648 dc.SetClippingRegion(tabs_rect);
649 }
650 dc.SetClippingRegion(info.rect);
651 m_art->DrawTab(dc, this, info);
652
653 if(info.rect.width < info.small_begin_need_separator_width)
654 {
655 draw_sep = true;
656 if(info.rect.width < info.small_must_have_separator_width)
657 {
658 sep_visibility += 1.0;
659 }
660 else
661 {
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);
663 }
664 }
665 }
666 if(draw_sep)
667 {
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)
672 {
673 wxRibbonPageTabInfo& info = m_pages.Item(i);
674 rect.x = info.rect.x + info.rect.width;
675
676 if(m_tab_scroll_buttons_shown && !tabs_rect.Intersects(rect))
677 {
678 continue;
679 }
680
681 dc.DestroyClippingRegion();
682 dc.SetClippingRegion(rect);
683 m_art->DrawTabSeparator(dc, this, rect, sep_visibility);
684 }
685 }
686 if(m_tab_scroll_buttons_shown)
687 {
688 dc.DestroyClippingRegion();
689 if(m_tab_scroll_left_button_rect.GetWidth() != 0)
690 {
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);
692 }
693 if(m_tab_scroll_right_button_rect.GetWidth() != 0)
694 {
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);
696 }
697 }
698 }
699
700 void wxRibbonBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
701 {
702 // Background painting done in main paint handler to reduce screen flicker
703 }
704
705 void wxRibbonBar::DoEraseBackground(wxDC& dc)
706 {
707 wxRect tabs(GetSize());
708 tabs.height = m_tab_height;
709 m_art->DrawTabCtrlBackground(dc, this, tabs);
710 }
711
712 void wxRibbonBar::OnSize(wxSizeEvent& evt)
713 {
714 RecalculateTabSizes();
715 if(m_current_page != -1)
716 {
717 RepositionPage(m_pages.Item(m_current_page).page);
718 }
719 RefreshTabBar();
720
721 evt.Skip();
722 }
723
724 void wxRibbonBar::RepositionPage(wxRibbonPage *page)
725 {
726 int w, h;
727 GetSize(&w, &h);
728 page->SetSizeWithScrollButtonAdjustment(0, m_tab_height, w, h - m_tab_height);
729 }
730
731 wxRibbonPageTabInfo* wxRibbonBar::HitTestTabs(wxPoint position, int* index)
732 {
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)
735 {
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());
738 }
739 if(tabs_rect.Contains(position))
740 {
741 size_t numtabs = m_pages.GetCount();
742 size_t i;
743 for(i = 0; i < numtabs; ++i)
744 {
745 wxRibbonPageTabInfo& info = m_pages.Item(i);
746 if(info.rect.Contains(position))
747 {
748 if(index != NULL)
749 {
750 *index = (int)i;
751 }
752 return &info;
753 }
754 }
755 }
756 if(index != NULL)
757 {
758 *index = -1;
759 }
760 return NULL;
761 }
762
763 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent& evt)
764 {
765 wxRibbonPageTabInfo *tab = HitTestTabs(evt.GetPosition());
766 if(tab && tab != &m_pages.Item(m_current_page))
767 {
768 wxRibbonBarEvent query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING, GetId(), tab->page);
769 query.SetEventObject(this);
770 ProcessWindowEvent(query);
771 if(query.IsAllowed())
772 {
773 SetActivePage(query.GetPage());
774
775 wxRibbonBarEvent notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED, GetId(), m_pages.Item(m_current_page).page);
776 notification.SetEventObject(this);
777 ProcessWindowEvent(notification);
778 }
779 }
780 else if(tab == NULL)
781 {
782 if(m_tab_scroll_left_button_rect.Contains(evt.GetPosition()))
783 {
784 m_tab_scroll_left_button_state |= wxRIBBON_SCROLL_BTN_ACTIVE | wxRIBBON_SCROLL_BTN_HOVERED;
785 RefreshTabBar();
786 }
787 else if(m_tab_scroll_right_button_rect.Contains(evt.GetPosition()))
788 {
789 m_tab_scroll_right_button_state |= wxRIBBON_SCROLL_BTN_ACTIVE | wxRIBBON_SCROLL_BTN_HOVERED;
790 RefreshTabBar();
791 }
792 }
793 }
794
795 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent& WXUNUSED(evt))
796 {
797 if(!m_tab_scroll_buttons_shown)
798 {
799 return;
800 }
801
802 int amount = 0;
803 if(m_tab_scroll_left_button_state & wxRIBBON_SCROLL_BTN_ACTIVE)
804 {
805 amount = -1;
806 }
807 else if(m_tab_scroll_right_button_state & wxRIBBON_SCROLL_BTN_ACTIVE)
808 {
809 amount = 1;
810 }
811 if(amount != 0)
812 {
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);
816 }
817 }
818
819 void wxRibbonBar::ScrollTabBar(int amount)
820 {
821 bool show_left = true;
822 bool show_right = true;
823 if(m_tab_scroll_amount + amount <= 0)
824 {
825 amount = -m_tab_scroll_amount;
826 show_left = false;
827 }
828 else if(m_tab_scroll_amount + amount + (GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right) >= m_tabs_total_width_minimum)
829 {
830 amount = m_tabs_total_width_minimum - m_tab_scroll_amount - (GetClientSize().GetWidth() - m_tab_margin_left - m_tab_margin_right);
831 show_right = false;
832 }
833 if(amount == 0)
834 {
835 return;
836 }
837 m_tab_scroll_amount += amount;
838 size_t numtabs = m_pages.GetCount();
839 size_t i;
840 for(i = 0; i < numtabs; ++i)
841 {
842 wxRibbonPageTabInfo& info = m_pages.Item(i);
843 info.rect.SetX(info.rect.GetX() - amount);
844 }
845 if(show_right != (m_tab_scroll_right_button_rect.GetWidth() != 0) ||
846 show_left != (m_tab_scroll_left_button_rect.GetWidth() != 0))
847 {
848 wxClientDC temp_dc(this);
849 if(show_left)
850 {
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());
852 }
853 else
854 {
855 m_tab_scroll_left_button_rect.SetWidth(0);
856 }
857
858 if(show_right)
859 {
860 if(m_tab_scroll_right_button_rect.GetWidth() == 0)
861 {
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());
864 }
865 }
866 else
867 {
868 if(m_tab_scroll_right_button_rect.GetWidth() != 0)
869 {
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);
872 }
873 }
874 }
875
876 RefreshTabBar();
877 }
878
879 void wxRibbonBar::RefreshTabBar()
880 {
881 wxRect tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height);
882 Refresh(false, &tab_rect);
883 }
884
885 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent& evt)
886 {
887 DoMouseButtonCommon(evt, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN);
888 }
889
890 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent& evt)
891 {
892 DoMouseButtonCommon(evt, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP);
893 }
894
895 void wxRibbonBar::OnMouseRightDown(wxMouseEvent& evt)
896 {
897 DoMouseButtonCommon(evt, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN);
898 }
899
900 void wxRibbonBar::OnMouseRightUp(wxMouseEvent& evt)
901 {
902 DoMouseButtonCommon(evt, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP);
903 }
904
905 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent& evt, wxEventType tab_event_type)
906 {
907 wxRibbonPageTabInfo *tab = HitTestTabs(evt.GetPosition());
908 if(tab)
909 {
910 wxRibbonBarEvent notification(tab_event_type, GetId(), tab->page);
911 notification.SetEventObject(this);
912 ProcessWindowEvent(notification);
913 }
914 }
915
916 void wxRibbonBar::RecalculateMinSize()
917 {
918 wxSize min_size(wxDefaultCoord, wxDefaultCoord);
919 size_t numtabs = m_pages.GetCount();
920 if(numtabs != 0)
921 {
922 min_size = m_pages.Item(0).page->GetMinSize();
923
924 size_t i;
925 for(i = 1; i < numtabs; ++i)
926 {
927 wxRibbonPageTabInfo& info = m_pages.Item(i);
928 wxSize page_min = info.page->GetMinSize();
929
930 min_size.x = wxMax(min_size.x, page_min.x);
931 min_size.y = wxMax(min_size.y, page_min.y);
932 }
933 }
934 if(min_size.y != wxDefaultCoord)
935 {
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);
939 }
940
941 m_minWidth = min_size.GetWidth();
942 m_minHeight = min_size.GetHeight();
943 }
944
945 wxSize wxRibbonBar::DoGetBestSize() const
946 {
947 wxSize best(0, 0);
948 if(m_current_page != -1)
949 {
950 best = m_pages.Item(m_current_page).page->GetBestSize();
951 }
952 if(best.GetHeight() == wxDefaultCoord)
953 {
954 best.SetHeight(m_tab_height);
955 }
956 else
957 {
958 best.IncBy(0, m_tab_height);
959 }
960 return best;
961 }
962
963 #endif // wxUSE_RIBBON