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