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