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