1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/page.cpp
3 // Purpose: Container for ribbon-bar-style interface panels
4 // Author: Peter Cawley
5 // Modified by:
6 // Created: 2009-05-25
7 // Copyright: (C) Peter Cawley
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 #include "wx/wxprec.h"
11
12 #ifdef __BORLANDC__
13 #pragma hdrstop
14 #endif
15
16 #if wxUSE_RIBBON
17
18 #include "wx/ribbon/page.h"
19 #include "wx/ribbon/art.h"
20 #include "wx/ribbon/bar.h"
21 #include "wx/dcbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #endif
25
26 #ifdef __WXMSW__
27 #include "wx/msw/private.h"
28 #endif
29
30 static int GetSizeInOrientation(wxSize size, wxOrientation orientation);
31
32 // As scroll buttons need to be rendered on top of a page's child windows, the
33 // buttons themselves have to be proper child windows (rather than just painted
34 // onto the page). In order to get proper clipping of a page's children (with
35 // regard to the scroll button), the scroll buttons are created as children of
36 // the ribbon bar rather than children of the page. This could not have been
37 // achieved by creating buttons as children of the page and then doing some Z-order
38 // manipulation, as this causes problems on win32 due to ribbon panels having the
39 // transparent flag set.
40 class wxRibbonPageScrollButton : public wxRibbonControl
41 {
42 public:
43 wxRibbonPageScrollButton(wxRibbonPage* sibling,
44 wxWindowID id = wxID_ANY,
45 const wxPoint& pos = wxDefaultPosition,
46 const wxSize& size = wxDefaultSize,
47 long style = 0);
48
49 virtual ~wxRibbonPageScrollButton();
50
51 protected:
GetDefaultBorder() const52 virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
53
54 void OnEraseBackground(wxEraseEvent& evt);
55 void OnPaint(wxPaintEvent& evt);
56 void OnMouseEnter(wxMouseEvent& evt);
57 void OnMouseLeave(wxMouseEvent& evt);
58 void OnMouseDown(wxMouseEvent& evt);
59 void OnMouseUp(wxMouseEvent& evt);
60
61 wxRibbonPage* m_sibling;
62 long m_flags;
63
64 DECLARE_CLASS(wxRibbonPageScrollButton)
65 DECLARE_EVENT_TABLE()
66 };
67
IMPLEMENT_CLASS(wxRibbonPageScrollButton,wxRibbonControl)68 IMPLEMENT_CLASS(wxRibbonPageScrollButton, wxRibbonControl)
69
70 BEGIN_EVENT_TABLE(wxRibbonPageScrollButton, wxRibbonControl)
71 EVT_ENTER_WINDOW(wxRibbonPageScrollButton::OnMouseEnter)
72 EVT_ERASE_BACKGROUND(wxRibbonPageScrollButton::OnEraseBackground)
73 EVT_LEAVE_WINDOW(wxRibbonPageScrollButton::OnMouseLeave)
74 EVT_LEFT_DOWN(wxRibbonPageScrollButton::OnMouseDown)
75 EVT_LEFT_UP(wxRibbonPageScrollButton::OnMouseUp)
76 EVT_PAINT(wxRibbonPageScrollButton::OnPaint)
77 END_EVENT_TABLE()
78
79 wxRibbonPageScrollButton::wxRibbonPageScrollButton(wxRibbonPage* sibling,
80 wxWindowID id,
81 const wxPoint& pos,
82 const wxSize& size,
83 long style) : wxRibbonControl(sibling->GetParent(), id, pos, size, wxBORDER_NONE)
84 {
85 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
86 m_sibling = sibling;
87 m_flags = (style & wxRIBBON_SCROLL_BTN_DIRECTION_MASK) | wxRIBBON_SCROLL_BTN_FOR_PAGE;
88 }
89
~wxRibbonPageScrollButton()90 wxRibbonPageScrollButton::~wxRibbonPageScrollButton()
91 {
92 }
93
OnEraseBackground(wxEraseEvent & WXUNUSED (evt))94 void wxRibbonPageScrollButton::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
95 {
96 // Do nothing - all painting done in main paint handler
97 }
98
OnPaint(wxPaintEvent & WXUNUSED (evt))99 void wxRibbonPageScrollButton::OnPaint(wxPaintEvent& WXUNUSED(evt))
100 {
101 wxAutoBufferedPaintDC dc(this);
102 if(m_art)
103 {
104 m_art->DrawScrollButton(dc, this, GetSize(), m_flags);
105 }
106 }
107
OnMouseEnter(wxMouseEvent & WXUNUSED (evt))108 void wxRibbonPageScrollButton::OnMouseEnter(wxMouseEvent& WXUNUSED(evt))
109 {
110 m_flags |= wxRIBBON_SCROLL_BTN_HOVERED;
111 Refresh(false);
112 }
113
OnMouseLeave(wxMouseEvent & WXUNUSED (evt))114 void wxRibbonPageScrollButton::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
115 {
116 m_flags &= ~wxRIBBON_SCROLL_BTN_HOVERED;
117 m_flags &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
118 Refresh(false);
119 }
120
OnMouseDown(wxMouseEvent & WXUNUSED (evt))121 void wxRibbonPageScrollButton::OnMouseDown(wxMouseEvent& WXUNUSED(evt))
122 {
123 m_flags |= wxRIBBON_SCROLL_BTN_ACTIVE;
124 Refresh(false);
125 }
126
OnMouseUp(wxMouseEvent & WXUNUSED (evt))127 void wxRibbonPageScrollButton::OnMouseUp(wxMouseEvent& WXUNUSED(evt))
128 {
129 if(m_flags & wxRIBBON_SCROLL_BTN_ACTIVE)
130 {
131 m_flags &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
132 Refresh(false);
133 switch(m_flags & wxRIBBON_SCROLL_BTN_DIRECTION_MASK)
134 {
135 case wxRIBBON_SCROLL_BTN_DOWN:
136 case wxRIBBON_SCROLL_BTN_RIGHT:
137 m_sibling->ScrollSections(1);
138 break;
139 case wxRIBBON_SCROLL_BTN_UP:
140 case wxRIBBON_SCROLL_BTN_LEFT:
141 m_sibling->ScrollSections(-1);
142 break;
143 default:
144 break;
145 }
146 }
147 }
148
IMPLEMENT_CLASS(wxRibbonPage,wxRibbonControl)149 IMPLEMENT_CLASS(wxRibbonPage, wxRibbonControl)
150
151 BEGIN_EVENT_TABLE(wxRibbonPage, wxRibbonControl)
152 EVT_ERASE_BACKGROUND(wxRibbonPage::OnEraseBackground)
153 EVT_PAINT(wxRibbonPage::OnPaint)
154 EVT_SIZE(wxRibbonPage::OnSize)
155 END_EVENT_TABLE()
156
157 wxRibbonPage::wxRibbonPage()
158 {
159 m_scroll_left_btn = NULL;
160 m_scroll_right_btn = NULL;
161 m_scroll_amount = 0;
162 m_scroll_buttons_visible = false;
163 }
164
wxRibbonPage(wxRibbonBar * parent,wxWindowID id,const wxString & label,const wxBitmap & icon,long WXUNUSED (style))165 wxRibbonPage::wxRibbonPage(wxRibbonBar* parent,
166 wxWindowID id,
167 const wxString& label,
168 const wxBitmap& icon,
169 long WXUNUSED(style))
170 : wxRibbonControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
171 {
172 CommonInit(label, icon);
173 }
174
~wxRibbonPage()175 wxRibbonPage::~wxRibbonPage()
176 {
177 delete[] m_size_calc_array;
178 delete m_scroll_left_btn;
179 delete m_scroll_right_btn;
180 }
181
Create(wxRibbonBar * parent,wxWindowID id,const wxString & label,const wxBitmap & icon,long WXUNUSED (style))182 bool wxRibbonPage::Create(wxRibbonBar* parent,
183 wxWindowID id,
184 const wxString& label,
185 const wxBitmap& icon,
186 long WXUNUSED(style))
187 {
188 if(!wxRibbonControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE))
189 return false;
190
191 CommonInit(label, icon);
192
193 return true;
194 }
195
CommonInit(const wxString & label,const wxBitmap & icon)196 void wxRibbonPage::CommonInit(const wxString& label, const wxBitmap& icon)
197 {
198 SetName(label);
199
200 SetLabel(label);
201 m_old_size = wxSize(0, 0);
202 m_icon = icon;
203 m_scroll_left_btn = NULL;
204 m_scroll_right_btn = NULL;
205 m_size_calc_array = NULL;
206 m_size_calc_array_size = 0;
207 m_scroll_amount = 0;
208 m_scroll_buttons_visible = false;
209
210 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
211
212 wxDynamicCast(GetParent(), wxRibbonBar)->AddPage(this);
213 }
214
SetArtProvider(wxRibbonArtProvider * art)215 void wxRibbonPage::SetArtProvider(wxRibbonArtProvider* art)
216 {
217 m_art = art;
218 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
219 node;
220 node = node->GetNext() )
221 {
222 wxWindow* child = node->GetData();
223 wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
224 if(ribbon_child)
225 {
226 ribbon_child->SetArtProvider(art);
227 }
228 }
229
230 // The scroll buttons are children of the parent ribbon control, not the
231 // page, so they're not taken into account by the loop above, but they
232 // still use the same art provider, so we need to update them too.
233 if ( m_scroll_left_btn )
234 m_scroll_left_btn->SetArtProvider(art);
235 if ( m_scroll_right_btn )
236 m_scroll_right_btn->SetArtProvider(art);
237 }
238
AdjustRectToIncludeScrollButtons(wxRect * rect) const239 void wxRibbonPage::AdjustRectToIncludeScrollButtons(wxRect* rect) const
240 {
241 if(m_scroll_buttons_visible)
242 {
243 if(GetMajorAxis() == wxVERTICAL)
244 {
245 if(m_scroll_left_btn)
246 {
247 rect->SetY(rect->GetY() -
248 m_scroll_left_btn->GetSize().GetHeight());
249 rect->SetHeight(rect->GetHeight() +
250 m_scroll_left_btn->GetSize().GetHeight());
251 }
252 if(m_scroll_right_btn)
253 {
254 rect->SetHeight(rect->GetHeight() +
255 m_scroll_right_btn->GetSize().GetHeight());
256 }
257 }
258 else
259 {
260 if(m_scroll_left_btn)
261 {
262 rect->SetX(rect->GetX() -
263 m_scroll_left_btn->GetSize().GetWidth());
264 rect->SetWidth(rect->GetWidth() +
265 m_scroll_left_btn->GetSize().GetWidth());
266 }
267 if(m_scroll_right_btn)
268 {
269 rect->SetWidth(rect->GetWidth() +
270 m_scroll_right_btn->GetSize().GetWidth());
271 }
272 }
273 }
274 }
275
OnEraseBackground(wxEraseEvent & WXUNUSED (evt))276 void wxRibbonPage::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
277 {
278 // All painting done in main paint handler to minimise flicker
279 }
280
OnPaint(wxPaintEvent & WXUNUSED (evt))281 void wxRibbonPage::OnPaint(wxPaintEvent& WXUNUSED(evt))
282 {
283 // No foreground painting done by the page itself, but a paint DC
284 // must be created anyway.
285 wxAutoBufferedPaintDC dc(this);
286 wxRect rect(GetSize());
287 AdjustRectToIncludeScrollButtons(&rect);
288 m_art->DrawPageBackground(dc, this, rect);
289 }
290
GetMajorAxis() const291 wxOrientation wxRibbonPage::GetMajorAxis() const
292 {
293 if(m_art && (m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL))
294 {
295 return wxVERTICAL;
296 }
297 else
298 {
299 return wxHORIZONTAL;
300 }
301 }
302
ScrollLines(int lines)303 bool wxRibbonPage::ScrollLines(int lines)
304 {
305 return ScrollPixels(lines * 8);
306 }
307
ScrollPixels(int pixels)308 bool wxRibbonPage::ScrollPixels(int pixels)
309 {
310 if(pixels < 0)
311 {
312 if(m_scroll_amount == 0)
313 return false;
314 if(m_scroll_amount < -pixels)
315 pixels = -m_scroll_amount;
316 }
317 else if(pixels > 0)
318 {
319 if(m_scroll_amount == m_scroll_amount_limit)
320 return false;
321 if(m_scroll_amount + pixels > m_scroll_amount_limit)
322 pixels = m_scroll_amount_limit - m_scroll_amount;
323 }
324 else
325 return false;
326
327 m_scroll_amount += pixels;
328
329 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
330 node;
331 node = node->GetNext() )
332 {
333 wxWindow* child = node->GetData();
334 int x, y;
335 child->GetPosition(&x, &y);
336 if(GetMajorAxis() == wxHORIZONTAL)
337 x -= pixels;
338 else
339 y -= pixels;
340 child->SetPosition(wxPoint(x, y));
341 }
342
343 if (ShowScrollButtons1())
344 DoActualLayout();
345 Refresh();
346 return true;
347 }
348
ScrollSections(int sections)349 bool wxRibbonPage::ScrollSections(int sections)
350 {
351 // Currently the only valid values are -1 and 1 for scrolling left and
352 // right, respectively.
353 const bool scrollForward = sections >= 1;
354
355 // Determine by how many pixels to scroll. If something on the page
356 // is partially visible, scroll to make it fully visible. Otherwise
357 // find the next item that will become visible and scroll to make it
358 // fully visible. The ScrollPixel call will correct if we scroll too
359 // much if the available width is smaller than the items.
360
361 // Scroll at minimum the same amount as ScrollLines(1):
362 int minscroll = sections * 8;
363 // How many pixels to scroll:
364 int pixels = 0;
365
366 // Determine the scroll position, that is, the page border where items
367 // are appearing.
368 int scrollpos = 0;
369
370 wxOrientation major_axis = GetMajorAxis();
371 int gap = 0;
372
373 int width = 0;
374 int height = 0;
375 int x = 0;
376 int y = 0;
377 GetSize(&width, &height);
378 GetPosition(&x, &y);
379 if(major_axis == wxHORIZONTAL)
380 {
381 gap = m_art->GetMetric(wxRIBBON_ART_PANEL_X_SEPARATION_SIZE);
382 if (scrollForward)
383 {
384 scrollpos = width - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
385 }
386 else
387 {
388 scrollpos = m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE);
389 }
390 }
391 else
392 {
393 gap = m_art->GetMetric(wxRIBBON_ART_PANEL_Y_SEPARATION_SIZE);
394 if (scrollForward)
395 {
396 scrollpos = width - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
397 }
398 else
399 {
400 scrollpos = m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE);
401 }
402 }
403
404 // Find the child that is partially shown or just beyond the scroll position
405 for(wxWindowList::compatibility_iterator
406 node = scrollForward ? GetChildren().GetFirst()
407 : GetChildren().GetLast();
408 node;
409 node = scrollForward ? node->GetNext()
410 : node->GetPrevious())
411 {
412 wxWindow* child = node->GetData();
413 child->GetSize(&width, &height);
414 child->GetPosition(&x, &y);
415 int pos0 = 0;
416 int pos1 = 0;
417 if (major_axis == wxHORIZONTAL)
418 {
419 pos0 = x;
420 pos1 = x + width + gap;
421 }
422 else
423 {
424 pos0 = y;
425 pos1 = y + height + gap;
426 }
427 if (scrollpos >= pos0 && scrollpos <= pos1)
428 {
429 // This section is partially visible, scroll to make it fully visible.
430 if (scrollForward)
431 {
432 pixels += pos1 - scrollpos;
433 }
434 else
435 {
436 pixels += pos0 - scrollpos;
437 }
438 if (abs(pixels) >= abs(minscroll))
439 break;
440 }
441 if (scrollpos <= pos0 && scrollForward)
442 {
443 // This section is next, scroll the entire section width
444 pixels += (pos1 - pos0);
445 break;
446 }
447 if (scrollpos >= pos1 && !scrollForward)
448 {
449 // This section is next, scroll the entire section width
450 pixels += (pos0 - pos1);
451 break;
452 }
453 }
454 // Do a final safety sanity check, should not be necessary, but will not hurt either.
455 if (pixels == 0)
456 {
457 pixels = minscroll;
458 }
459 if (pixels * minscroll < 0)
460 {
461 pixels = -pixels;
462 }
463
464 return ScrollPixels(pixels);
465 }
466
SetSizeWithScrollButtonAdjustment(int x,int y,int width,int height)467 void wxRibbonPage::SetSizeWithScrollButtonAdjustment(int x, int y, int width, int height)
468 {
469 if(m_scroll_buttons_visible)
470 {
471 if(GetMajorAxis() == wxHORIZONTAL)
472 {
473 if(m_scroll_left_btn)
474 {
475 int w = m_scroll_left_btn->GetSize().GetWidth();
476 m_scroll_left_btn->SetPosition(wxPoint(x, y));
477 x += w;
478 width -= w;
479 }
480 if(m_scroll_right_btn)
481 {
482 int w = m_scroll_right_btn->GetSize().GetWidth();
483 width -= w;
484 m_scroll_right_btn->SetPosition(wxPoint(x + width, y));
485 }
486 }
487 else
488 {
489 if(m_scroll_left_btn)
490 {
491 int h = m_scroll_left_btn->GetSize().GetHeight();
492 m_scroll_left_btn->SetPosition(wxPoint(x, y));
493 y += h;
494 height -= h;
495 }
496 if(m_scroll_right_btn)
497 {
498 int h = m_scroll_right_btn->GetSize().GetHeight();
499 height -= h;
500 m_scroll_right_btn->SetPosition(wxPoint(x, y + height));
501 }
502 }
503 }
504 if (width < 0) width = 0;
505 if (height < 0) height = 0;
506 SetSize(x, y, width, height);
507 }
508
DoSetSize(int x,int y,int width,int height,int sizeFlags)509 void wxRibbonPage::DoSetSize(int x, int y, int width, int height, int sizeFlags)
510 {
511 // When a resize triggers the scroll buttons to become visible, the page is resized.
512 // This resize from within a resize event can cause (MSW) wxWidgets some confusion,
513 // and report the 1st size to the 2nd size event. Hence the most recent size is
514 // remembered internally and used in Layout() where appropriate.
515
516 if(GetMajorAxis() == wxHORIZONTAL)
517 {
518 m_size_in_major_axis_for_children = width;
519 if(m_scroll_buttons_visible)
520 {
521 if(m_scroll_left_btn)
522 m_size_in_major_axis_for_children += m_scroll_left_btn->GetSize().GetWidth();
523 if(m_scroll_right_btn)
524 m_size_in_major_axis_for_children += m_scroll_right_btn->GetSize().GetWidth();
525 }
526 }
527 else
528 {
529 m_size_in_major_axis_for_children = height;
530 if(m_scroll_buttons_visible)
531 {
532 if(m_scroll_left_btn)
533 m_size_in_major_axis_for_children += m_scroll_left_btn->GetSize().GetHeight();
534 if(m_scroll_right_btn)
535 m_size_in_major_axis_for_children += m_scroll_right_btn->GetSize().GetHeight();
536 }
537 }
538
539 wxRibbonControl::DoSetSize(x, y, width, height, sizeFlags);
540 }
541
OnSize(wxSizeEvent & evt)542 void wxRibbonPage::OnSize(wxSizeEvent& evt)
543 {
544 wxSize new_size = evt.GetSize();
545
546 if (m_art)
547 {
548 wxMemoryDC temp_dc;
549 wxRect invalid_rect = m_art->GetPageBackgroundRedrawArea(temp_dc, this, m_old_size, new_size);
550 Refresh(true, &invalid_rect);
551 }
552
553 m_old_size = new_size;
554
555 if(new_size.GetX() > 0 && new_size.GetY() > 0)
556 {
557 Layout();
558 }
559 else
560 {
561 // Simplify other calculations by pretending new size is zero in both
562 // X and Y
563 new_size.Set(0, 0);
564 // When size == 0, no point in doing any layout
565 }
566
567 evt.Skip();
568 }
569
RemoveChild(wxWindowBase * child)570 void wxRibbonPage::RemoveChild(wxWindowBase *child)
571 {
572 // Remove all references to the child from the collapse stack
573 size_t count = m_collapse_stack.GetCount();
574 size_t src, dst;
575 for(src = 0, dst = 0; src < count; ++src, ++dst)
576 {
577 wxRibbonControl *item = m_collapse_stack.Item(src);
578 if(item == child)
579 {
580 ++src;
581 if(src == count)
582 {
583 break;
584 }
585 }
586 if(src != dst)
587 {
588 m_collapse_stack.Item(dst) = item;
589 }
590 }
591 if(src > dst)
592 {
593 m_collapse_stack.RemoveAt(dst, src - dst);
594 }
595
596 // ... and then proceed as normal
597 wxRibbonControl::RemoveChild(child);
598 }
599
Realize()600 bool wxRibbonPage::Realize()
601 {
602 bool status = true;
603
604 m_collapse_stack.Clear();
605 for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
606 node;
607 node = node->GetNext())
608 {
609 wxRibbonControl* child = wxDynamicCast(node->GetData(), wxRibbonControl);
610 if(child == NULL)
611 {
612 continue;
613 }
614 if(!child->Realize())
615 {
616 status = false;
617 }
618 }
619 PopulateSizeCalcArray(&wxWindow::GetMinSize);
620
621 return DoActualLayout() && status;
622 }
623
PopulateSizeCalcArray(wxSize (wxWindow::* get_size)(void)const)624 void wxRibbonPage::PopulateSizeCalcArray(wxSize (wxWindow::*get_size)(void) const)
625 {
626 wxSize parentSize = GetSize();
627 parentSize.x -= m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE);
628 parentSize.x -= m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
629 parentSize.y -= m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE);
630 parentSize.y -= m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
631
632 if(m_size_calc_array_size != GetChildren().GetCount())
633 {
634 delete[] m_size_calc_array;
635 m_size_calc_array_size = GetChildren().GetCount();
636 m_size_calc_array = new wxSize[m_size_calc_array_size];
637 }
638
639 wxSize* node_size = m_size_calc_array;
640 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
641 node;
642 node = node->GetNext(), ++node_size )
643 {
644 wxWindow* child = node->GetData();
645 wxRibbonPanel* panel = wxDynamicCast(child, wxRibbonPanel);
646 if (panel && panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE)
647 *node_size = panel->GetBestSizeForParentSize(parentSize);
648 else
649 *node_size = (child->*get_size)();
650 }
651 }
652
Layout()653 bool wxRibbonPage::Layout()
654 {
655 if(GetChildren().GetCount() == 0)
656 {
657 return true;
658 }
659 else
660 {
661 PopulateSizeCalcArray(&wxWindow::GetSize);
662 return DoActualLayout();
663 }
664 }
665
DoActualLayout()666 bool wxRibbonPage::DoActualLayout()
667 {
668 wxPoint origin(m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE), m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE));
669 wxOrientation major_axis = GetMajorAxis();
670 int gap;
671 int minor_axis_size;
672 int available_space;
673 if(major_axis == wxHORIZONTAL)
674 {
675 gap = m_art->GetMetric(wxRIBBON_ART_PANEL_X_SEPARATION_SIZE);
676 minor_axis_size = GetSize().GetHeight() - origin.y - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
677 available_space = m_size_in_major_axis_for_children - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE) - origin.x;
678 }
679 else
680 {
681 gap = m_art->GetMetric(wxRIBBON_ART_PANEL_Y_SEPARATION_SIZE);
682 minor_axis_size = GetSize().GetWidth() - origin.x - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
683 available_space = m_size_in_major_axis_for_children - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE) - origin.y;
684 }
685 if (minor_axis_size < 0) minor_axis_size = 0;
686 size_t size_index;
687 for(size_index = 0; size_index < m_size_calc_array_size; ++size_index)
688 {
689 if(major_axis == wxHORIZONTAL)
690 {
691 available_space -= m_size_calc_array[size_index].GetWidth();
692 m_size_calc_array[size_index].SetHeight(minor_axis_size);
693 }
694 else
695 {
696 available_space -= m_size_calc_array[size_index].GetHeight();
697 m_size_calc_array[size_index].SetWidth(minor_axis_size);
698 }
699 if(size_index != 0)
700 available_space -= gap;
701 }
702 bool todo_hide_scroll_buttons = false;
703 bool todo_show_scroll_buttons = false;
704 if(available_space >= 0)
705 {
706 if(m_scroll_buttons_visible)
707 todo_hide_scroll_buttons = true;
708 if(available_space > 0)
709 ExpandPanels(major_axis, available_space);
710 }
711 else
712 {
713 if(m_scroll_buttons_visible)
714 {
715 // Scroll buttons already visible - not going to be able to downsize any more
716 m_scroll_amount_limit = -available_space;
717 if(m_scroll_amount > m_scroll_amount_limit)
718 {
719 m_scroll_amount = m_scroll_amount_limit;
720 todo_show_scroll_buttons = true;
721 }
722 }
723 else
724 {
725 if(!CollapsePanels(major_axis, -available_space))
726 {
727 m_scroll_amount = 0;
728 m_scroll_amount_limit = -available_space;
729 todo_show_scroll_buttons = true;
730 }
731 }
732 }
733 if(m_scroll_buttons_visible)
734 {
735 if(major_axis == wxHORIZONTAL)
736 {
737 origin.x -= m_scroll_amount;
738 if(m_scroll_left_btn)
739 origin.x -= m_scroll_left_btn->GetSize().GetWidth();
740 }
741 else
742 {
743 origin.y -= m_scroll_amount;
744 if(m_scroll_left_btn)
745 origin.y -= m_scroll_left_btn->GetSize().GetHeight();
746 }
747 }
748 size_index = 0;
749 for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
750 node;
751 node = node->GetNext(), ++size_index )
752 {
753 wxWindow* child = node->GetData();
754 int w = m_size_calc_array[size_index].GetWidth();
755 int h = m_size_calc_array[size_index].GetHeight();
756 child->SetSize(origin.x, origin.y, w, h);
757 if(major_axis == wxHORIZONTAL)
758 {
759 origin.x += w + gap;
760 }
761 else
762 {
763 origin.y += h + gap;
764 }
765 }
766
767 if(todo_show_scroll_buttons)
768 ShowScrollButtons();
769 else if(todo_hide_scroll_buttons)
770 HideScrollButtons();
771 else if(m_scroll_buttons_visible)
772 ShowScrollButtons();
773
774 Refresh();
775 return true;
776 }
777
Show(bool show)778 bool wxRibbonPage::Show(bool show)
779 {
780 if(m_scroll_left_btn)
781 m_scroll_left_btn->Show(show);
782 if(m_scroll_right_btn)
783 m_scroll_right_btn->Show(show);
784 return wxRibbonControl::Show(show);
785 }
786
HideScrollButtons()787 void wxRibbonPage::HideScrollButtons()
788 {
789 m_scroll_amount = 0;
790 m_scroll_amount_limit = 0;
791 ShowScrollButtons();
792 }
793
ShowScrollButtons()794 void wxRibbonPage::ShowScrollButtons()
795 {
796 ShowScrollButtons1();
797 }
798
ShowScrollButtons1()799 bool wxRibbonPage::ShowScrollButtons1()
800 {
801 bool show_left = true;
802 bool show_right = true;
803 bool reposition = false;
804 if(m_scroll_amount == 0)
805 {
806 show_left = false;
807 }
808 if(m_scroll_amount >= m_scroll_amount_limit)
809 {
810 show_right = false;
811 m_scroll_amount = m_scroll_amount_limit;
812 }
813 m_scroll_buttons_visible = show_left || show_right;
814
815 if(show_left)
816 {
817 wxMemoryDC temp_dc;
818 wxSize size;
819 long direction;
820 if(GetMajorAxis() == wxHORIZONTAL)
821 {
822 direction = wxRIBBON_SCROLL_BTN_LEFT;
823 size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
824 size.SetHeight(GetSize().GetHeight());
825 }
826 else
827 {
828 direction = wxRIBBON_SCROLL_BTN_UP;
829 size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
830 size.SetWidth(GetSize().GetWidth());
831 }
832 if (m_scroll_left_btn)
833 {
834 m_scroll_left_btn->SetSize(size);
835 }
836 else
837 {
838 m_scroll_left_btn = new wxRibbonPageScrollButton(this, wxID_ANY, GetPosition(), size, direction);
839 reposition = true;
840 }
841 if(!IsShown())
842 {
843 m_scroll_left_btn->Hide();
844 }
845 }
846 else
847 {
848 if(m_scroll_left_btn != NULL)
849 {
850 m_scroll_left_btn->Destroy();
851 m_scroll_left_btn = NULL;
852 reposition = true;
853 }
854 }
855
856 if(show_right)
857 {
858 wxMemoryDC temp_dc;
859 wxSize size;
860 long direction;
861 if(GetMajorAxis() == wxHORIZONTAL)
862 {
863 direction = wxRIBBON_SCROLL_BTN_RIGHT;
864 size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
865 size.SetHeight(GetSize().GetHeight());
866 }
867 else
868 {
869 direction = wxRIBBON_SCROLL_BTN_DOWN;
870 size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
871 size.SetWidth(GetSize().GetWidth());
872 }
873 wxPoint initial_pos = GetPosition() + GetSize() - size;
874 if (m_scroll_right_btn)
875 {
876 m_scroll_right_btn->SetSize(size);
877 }
878 else
879 {
880 m_scroll_right_btn = new wxRibbonPageScrollButton(this, wxID_ANY, initial_pos, size, direction);
881 reposition = true;
882 }
883 if(!IsShown())
884 {
885 m_scroll_right_btn->Hide();
886 }
887 }
888 else
889 {
890 if(m_scroll_right_btn != NULL)
891 {
892 m_scroll_right_btn->Destroy();
893 m_scroll_right_btn = NULL;
894 reposition = true;
895 }
896 }
897
898 if(reposition)
899 {
900 wxDynamicCast(GetParent(), wxRibbonBar)->RepositionPage(this);
901 }
902
903 return reposition;
904 }
905
GetSizeInOrientation(wxSize size,wxOrientation orientation)906 static int GetSizeInOrientation(wxSize size, wxOrientation orientation)
907 {
908 switch(orientation)
909 {
910 case wxHORIZONTAL: return size.GetWidth();
911 case wxVERTICAL: return size.GetHeight();
912 case wxBOTH: return size.GetWidth() * size.GetHeight();
913 default: return 0;
914 }
915 }
916
ExpandPanels(wxOrientation direction,int maximum_amount)917 bool wxRibbonPage::ExpandPanels(wxOrientation direction, int maximum_amount)
918 {
919 bool expanded_something = false;
920 while(maximum_amount > 0)
921 {
922 int smallest_size = INT_MAX;
923 wxRibbonPanel* smallest_panel = NULL;
924 wxSize* smallest_panel_size = NULL;
925 wxSize* panel_size = m_size_calc_array;
926 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
927 node;
928 node = node->GetNext(), ++panel_size )
929 {
930 wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
931 if(panel == NULL)
932 {
933 continue;
934 }
935 if (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE)
936 {
937 // Don't change if it's flexible since we already calculated the
938 // correct size for the panel.
939 }
940 else if(panel->IsSizingContinuous())
941 {
942 int size = GetSizeInOrientation(*panel_size, direction);
943 if(size < smallest_size)
944 {
945 smallest_size = size;
946 smallest_panel = panel;
947 smallest_panel_size = panel_size;
948 }
949 }
950 else
951 {
952 int size = GetSizeInOrientation(*panel_size, direction);
953 if(size < smallest_size)
954 {
955 wxSize larger = panel->GetNextLargerSize(direction, *panel_size);
956 if(larger != (*panel_size) && GetSizeInOrientation(larger, direction) > size)
957 {
958 smallest_size = size;
959 smallest_panel = panel;
960 smallest_panel_size = panel_size;
961 }
962 }
963 }
964 }
965 if(smallest_panel != NULL)
966 {
967 if(smallest_panel->IsSizingContinuous())
968 {
969 int amount = maximum_amount;
970 if(amount > 32)
971 {
972 // For "large" growth, grow this panel a bit, and then re-allocate
973 // the remainder (which may come to this panel again anyway)
974 amount = 32;
975 }
976 if(direction & wxHORIZONTAL)
977 {
978 smallest_panel_size->x += amount;
979 }
980 if(direction & wxVERTICAL)
981 {
982 smallest_panel_size->y += amount;
983 }
984 maximum_amount -= amount;
985 m_collapse_stack.Add(smallest_panel);
986 expanded_something = true;
987 }
988 else
989 {
990 wxSize larger = smallest_panel->GetNextLargerSize(direction, *smallest_panel_size);
991 wxSize delta = larger - (*smallest_panel_size);
992 if(GetSizeInOrientation(delta, direction) <= maximum_amount)
993 {
994 *smallest_panel_size = larger;
995 maximum_amount -= GetSizeInOrientation(delta, direction);
996 m_collapse_stack.Add(smallest_panel);
997 expanded_something = true;
998 }
999 else
1000 {
1001 break;
1002 }
1003 }
1004 }
1005 else
1006 {
1007 break;
1008 }
1009 }
1010 return expanded_something;
1011 }
1012
CollapsePanels(wxOrientation direction,int minimum_amount)1013 bool wxRibbonPage::CollapsePanels(wxOrientation direction, int minimum_amount)
1014 {
1015 while(minimum_amount > 0)
1016 {
1017 int largest_size = 0;
1018 wxRibbonPanel* largest_panel = NULL;
1019 wxSize* largest_panel_size = NULL;
1020 wxSize* panel_size = m_size_calc_array;
1021 if(!m_collapse_stack.IsEmpty())
1022 {
1023 // For a more consistent panel layout, try to collapse panels which
1024 // were recently expanded.
1025 largest_panel = wxDynamicCast(m_collapse_stack.Last(), wxRibbonPanel);
1026 m_collapse_stack.RemoveAt(m_collapse_stack.GetCount() - 1);
1027 for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1028 node;
1029 node = node->GetNext(), ++panel_size )
1030 {
1031 wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
1032 if(panel == largest_panel)
1033 {
1034 largest_panel_size = panel_size;
1035 break;
1036 }
1037 }
1038 }
1039 else
1040 {
1041 for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1042 node;
1043 node = node->GetNext(), ++panel_size )
1044 {
1045 wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
1046 if(panel == NULL)
1047 {
1048 continue;
1049 }
1050 if(panel->IsSizingContinuous())
1051 {
1052 int size = GetSizeInOrientation(*panel_size, direction);
1053 if(size > largest_size)
1054 {
1055 largest_size = size;
1056 largest_panel = panel;
1057 largest_panel_size = panel_size;
1058 }
1059 }
1060 else
1061 {
1062 int size = GetSizeInOrientation(*panel_size, direction);
1063 if(size > largest_size)
1064 {
1065 wxSize smaller = panel->GetNextSmallerSize(direction, *panel_size);
1066 if(smaller != (*panel_size) &&
1067 GetSizeInOrientation(smaller, direction) < size)
1068 {
1069 largest_size = size;
1070 largest_panel = panel;
1071 largest_panel_size = panel_size;
1072 }
1073 }
1074 }
1075 }
1076 }
1077 if(largest_panel != NULL)
1078 {
1079 if(largest_panel->IsSizingContinuous())
1080 {
1081 int amount = minimum_amount;
1082 if(amount > 32)
1083 {
1084 // For "large" contraction, reduce this panel a bit, and
1085 // then re-allocate the remainder of the quota (which may
1086 // come to this panel again anyway)
1087 amount = 32;
1088 }
1089 if(direction & wxHORIZONTAL)
1090 {
1091 largest_panel_size->x -= amount;
1092 }
1093 if(direction & wxVERTICAL)
1094 {
1095 largest_panel_size->y -= amount;
1096 }
1097 minimum_amount -= amount;
1098 }
1099 else
1100 {
1101 wxSize smaller = largest_panel->GetNextSmallerSize(direction, *largest_panel_size);
1102 wxSize delta = (*largest_panel_size) - smaller;
1103 *largest_panel_size = smaller;
1104 minimum_amount -= GetSizeInOrientation(delta, direction);
1105 }
1106 }
1107 else
1108 {
1109 break;
1110 }
1111 }
1112 return minimum_amount <= 0;
1113 }
1114
DismissExpandedPanel()1115 bool wxRibbonPage::DismissExpandedPanel()
1116 {
1117 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1118 node;
1119 node = node->GetNext() )
1120 {
1121 wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
1122 if(panel == NULL)
1123 {
1124 continue;
1125 }
1126 if(panel->GetExpandedPanel() != NULL)
1127 {
1128 return panel->HideExpanded();
1129 }
1130 }
1131 return false;
1132 }
1133
GetMinSize() const1134 wxSize wxRibbonPage::GetMinSize() const
1135 {
1136 wxSize min(wxDefaultCoord, wxDefaultCoord);
1137
1138 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1139 node;
1140 node = node->GetNext() )
1141 {
1142 wxWindow* child = node->GetData();
1143 wxSize child_min(child->GetMinSize());
1144
1145 min.x = wxMax(min.x, child_min.x);
1146 min.y = wxMax(min.y, child_min.y);
1147 }
1148
1149 if(GetMajorAxis() == wxHORIZONTAL)
1150 {
1151 min.x = wxDefaultCoord;
1152 if(min.y != wxDefaultCoord)
1153 {
1154 min.y += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
1155 }
1156 }
1157 else
1158 {
1159 if(min.x != wxDefaultCoord)
1160 {
1161 min.x += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
1162 }
1163 min.y = wxDefaultCoord;
1164 }
1165
1166 return min;
1167 }
1168
DoGetBestSize() const1169 wxSize wxRibbonPage::DoGetBestSize() const
1170 {
1171 wxSize best(0, 0);
1172 size_t count = 0;
1173
1174 if(GetMajorAxis() == wxHORIZONTAL)
1175 {
1176 best.y = wxDefaultCoord;
1177
1178 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1179 node;
1180 node = node->GetNext() )
1181 {
1182 wxWindow* child = node->GetData();
1183 wxSize child_best(child->GetBestSize());
1184
1185 if(child_best.x != wxDefaultCoord)
1186 {
1187 best.IncBy(child_best.x, 0);
1188 }
1189 best.y = wxMax(best.y, child_best.y);
1190
1191 ++count;
1192 }
1193
1194 if(count > 1)
1195 {
1196 best.IncBy((count - 1) * m_art->GetMetric(wxRIBBON_ART_PANEL_X_SEPARATION_SIZE), 0);
1197 }
1198 }
1199 else
1200 {
1201 best.x = wxDefaultCoord;
1202
1203 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1204 node;
1205 node = node->GetNext() )
1206 {
1207 wxWindow* child = node->GetData();
1208 wxSize child_best(child->GetBestSize());
1209
1210 best.x = wxMax(best.x, child_best.x);
1211 if(child_best.y != wxDefaultCoord)
1212 {
1213 best.IncBy(0, child_best.y);
1214 }
1215
1216 ++count;
1217 }
1218
1219 if(count > 1)
1220 {
1221 best.IncBy(0, (count - 1) * m_art->GetMetric(wxRIBBON_ART_PANEL_Y_SEPARATION_SIZE));
1222 }
1223 }
1224
1225 if(best.x != wxDefaultCoord)
1226 {
1227 best.x += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
1228 }
1229 if(best.y != wxDefaultCoord)
1230 {
1231 best.y += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
1232 }
1233 return best;
1234 }
1235
HideIfExpanded()1236 void wxRibbonPage::HideIfExpanded()
1237 {
1238 wxStaticCast(m_parent, wxRibbonBar)->HideIfExpanded();
1239 }
1240
1241 #endif // wxUSE_RIBBON
1242