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