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