Fix bugs related to two phase creation of wxRibbon classes.
[wxWidgets.git] / src / ribbon / panel.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/panel.cpp
3 // Purpose: Ribbon-style container for a group of related tools / controls
4 // Author: Peter Cawley
5 // Modified by:
6 // Created: 2009-05-25
7 // RCS-ID: $Id$
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_RIBBON
19
20 #include "wx/ribbon/panel.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/ribbon/bar.h"
23 #include "wx/dcbuffer.h"
24 #include "wx/display.h"
25
26 #ifndef WX_PRECOMP
27 #include "wx/frame.h"
28 #endif
29
30 #ifdef __WXMSW__
31 #include "wx/msw/private.h"
32 #endif
33
34 IMPLEMENT_CLASS(wxRibbonPanel, wxRibbonControl)
35
36 BEGIN_EVENT_TABLE(wxRibbonPanel, wxRibbonControl)
37 EVT_ENTER_WINDOW(wxRibbonPanel::OnMouseEnter)
38 EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground)
39 EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus)
40 EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave)
41 EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick)
42 EVT_PAINT(wxRibbonPanel::OnPaint)
43 EVT_SIZE(wxRibbonPanel::OnSize)
44 END_EVENT_TABLE()
45
46 wxRibbonPanel::wxRibbonPanel() : m_expanded_dummy(NULL), m_expanded_panel(NULL)
47 {
48 }
49
50 wxRibbonPanel::wxRibbonPanel(wxWindow* parent,
51 wxWindowID id,
52 const wxString& label,
53 const wxBitmap& minimised_icon,
54 const wxPoint& pos,
55 const wxSize& size,
56 long style)
57 : wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
58 {
59 CommonInit(label, minimised_icon, style);
60 }
61
62 wxRibbonPanel::~wxRibbonPanel()
63 {
64 if(m_expanded_panel)
65 {
66 m_expanded_panel->m_expanded_dummy = NULL;
67 m_expanded_panel->GetParent()->Destroy();
68 }
69 }
70
71 bool wxRibbonPanel::Create(wxWindow* parent,
72 wxWindowID id,
73 const wxString& label,
74 const wxBitmap& icon,
75 const wxPoint& pos,
76 const wxSize& size,
77 long style)
78 {
79 if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
80 {
81 return false;
82 }
83
84 CommonInit(label, icon, style);
85
86 return true;
87 }
88
89 void wxRibbonPanel::SetArtProvider(wxRibbonArtProvider* art)
90 {
91 m_art = art;
92 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
93 node;
94 node = node->GetNext() )
95 {
96 wxWindow* child = node->GetData();
97 wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
98 if(ribbon_child)
99 {
100 ribbon_child->SetArtProvider(art);
101 }
102 }
103 if(m_expanded_panel)
104 m_expanded_panel->SetArtProvider(art);
105 }
106
107 void wxRibbonPanel::CommonInit(const wxString& label, const wxBitmap& icon, long style)
108 {
109 SetName(label);
110 SetLabel(label);
111
112 m_minimised_size = wxDefaultSize; // Unknown / none
113 m_smallest_unminimised_size = wxSize(INT_MAX, INT_MAX); // Unknown / none
114 m_preferred_expand_direction = wxSOUTH;
115 m_expanded_dummy = NULL;
116 m_expanded_panel = NULL;
117 m_flags = style;
118 m_minimised_icon = icon;
119 m_minimised = false;
120 m_hovered = false;
121
122 if(m_art == NULL)
123 {
124 wxRibbonControl* parent = wxDynamicCast(GetParent(), wxRibbonControl);
125 if(parent != NULL)
126 {
127 m_art = parent->GetArtProvider();
128 }
129 }
130
131 SetAutoLayout(true);
132 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
133 SetMinSize(wxSize(20, 20));
134 }
135
136 bool wxRibbonPanel::IsMinimised() const
137 {
138 return m_minimised;
139 }
140
141 bool wxRibbonPanel::IsHovered() const
142 {
143 return m_hovered;
144 }
145
146 void wxRibbonPanel::OnMouseEnter(wxMouseEvent& evt)
147 {
148 TestPositionForHover(evt.GetPosition());
149 }
150
151 void wxRibbonPanel::OnMouseEnterChild(wxMouseEvent& evt)
152 {
153 wxPoint pos = evt.GetPosition();
154 wxWindow *child = wxDynamicCast(evt.GetEventObject(), wxWindow);
155 if(child)
156 {
157 pos += child->GetPosition();
158 TestPositionForHover(pos);
159 }
160 evt.Skip();
161 }
162
163 void wxRibbonPanel::OnMouseLeave(wxMouseEvent& evt)
164 {
165 TestPositionForHover(evt.GetPosition());
166 }
167
168 void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent& evt)
169 {
170 wxPoint pos = evt.GetPosition();
171 wxWindow *child = wxDynamicCast(evt.GetEventObject(), wxWindow);
172 if(child)
173 {
174 pos += child->GetPosition();
175 TestPositionForHover(pos);
176 }
177 evt.Skip();
178 }
179
180 void wxRibbonPanel::TestPositionForHover(const wxPoint& pos)
181 {
182 bool hovered = false;
183 if(pos.x >= 0 && pos.y >= 0)
184 {
185 wxSize size = GetSize();
186 if(pos.x < size.GetWidth() && pos.y < size.GetHeight())
187 {
188 hovered = true;
189 }
190 }
191 if(hovered != m_hovered)
192 {
193 m_hovered = hovered;
194 Refresh(false);
195 }
196 }
197
198 void wxRibbonPanel::AddChild(wxWindowBase *child)
199 {
200 wxRibbonControl::AddChild(child);
201
202 // Window enter / leave events count for only the window in question, not
203 // for children of the window. The panel wants to be in the hovered state
204 // whenever the mouse cursor is within its boundary, so the events need to
205 // be attached to children too.
206 child->Connect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
207 child->Connect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
208 }
209
210 void wxRibbonPanel::RemoveChild(wxWindowBase *child)
211 {
212 child->Disconnect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
213 child->Disconnect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
214
215 wxRibbonControl::RemoveChild(child);
216 }
217
218 void wxRibbonPanel::OnSize(wxSizeEvent& evt)
219 {
220 if(GetAutoLayout())
221 Layout();
222
223 evt.Skip();
224 }
225
226 void wxRibbonPanel::DoSetSize(int x, int y, int width, int height, int sizeFlags)
227 {
228 // At least on MSW, changing the size of a window will cause GetSize() to
229 // report the new size, but a size event may not be handled immediately.
230 // If this minimised check was performed in the OnSize handler, then
231 // GetSize() could return a size much larger than the minimised size while
232 // IsMinimised() returns true. This would then affect layout, as the panel
233 // will refuse to grow any larger while in limbo between minimised and non.
234
235 bool minimised = (m_flags & wxRIBBON_PANEL_NO_AUTO_MINIMISE) == 0 &&
236 IsMinimised(wxSize(width, height));
237 if(minimised != m_minimised)
238 {
239 m_minimised = minimised;
240
241 for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
242 node;
243 node = node->GetNext())
244 {
245 node->GetData()->Show(!minimised);
246 }
247
248 Refresh();
249 }
250
251 wxRibbonControl::DoSetSize(x, y, width, height, sizeFlags);
252 }
253
254 bool wxRibbonPanel::IsMinimised(wxSize at_size) const
255 {
256 if(!m_minimised_size.IsFullySpecified())
257 return false;
258
259 return (at_size.GetX() <= m_minimised_size.GetX() &&
260 at_size.GetY() <= m_minimised_size.GetY()) ||
261 at_size.GetX() < m_smallest_unminimised_size.GetX() ||
262 at_size.GetY() < m_smallest_unminimised_size.GetY();
263 }
264
265 void wxRibbonPanel::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
266 {
267 // All painting done in main paint handler to minimise flicker
268 }
269
270 void wxRibbonPanel::OnPaint(wxPaintEvent& WXUNUSED(evt))
271 {
272 wxAutoBufferedPaintDC dc(this);
273
274 if(m_art != NULL)
275 {
276 if(IsMinimised())
277 {
278 m_art->DrawMinimisedPanel(dc, this, GetSize(), m_minimised_icon_resized);
279 }
280 else
281 {
282 m_art->DrawPanelBackground(dc, this, GetSize());
283 }
284 }
285 }
286
287 bool wxRibbonPanel::IsSizingContinuous() const
288 {
289 // A panel never sizes continuously, even if all of its children can,
290 // as it would appear out of place along side non-continuous panels.
291 return false;
292 }
293
294 wxSize wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction,
295 wxSize relative_to) const
296 {
297 if(m_expanded_panel != NULL)
298 {
299 // Next size depends upon children, who are currently in the
300 // expanded panel
301 return m_expanded_panel->DoGetNextSmallerSize(direction, relative_to);
302 }
303
304 // TODO: Check for, and delegate to, a sizer
305
306 // Simple (and common) case of single ribbon child
307 if(GetChildren().GetCount() == 1)
308 {
309 wxWindow* child = GetChildren().Item(0)->GetData();
310 wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
311 if(m_art != NULL && ribbon_child != NULL)
312 {
313 wxClientDC dc((wxRibbonPanel*) this);
314 wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
315 wxSize smaller = ribbon_child->GetNextSmallerSize(direction, child_relative);
316 if(smaller == child_relative)
317 {
318 if(CanAutoMinimise())
319 {
320 wxSize minimised = m_minimised_size;
321 switch(direction)
322 {
323 case wxHORIZONTAL:
324 minimised.SetHeight(relative_to.GetHeight());
325 break;
326 case wxVERTICAL:
327 minimised.SetWidth(relative_to.GetWidth());
328 break;
329 default:
330 break;
331 }
332 return minimised;
333 }
334 else
335 {
336 return relative_to;
337 }
338 }
339 else
340 {
341 return m_art->GetPanelSize(dc, this, smaller, NULL);
342 }
343 }
344 }
345
346 // Fallback: Decrease by 20% (or minimum size, whichever larger)
347 wxSize current(relative_to);
348 wxSize minimum(GetMinSize());
349 if(direction & wxHORIZONTAL)
350 {
351 current.x = (current.x * 4) / 5;
352 if(current.x < minimum.x)
353 {
354 current.x = minimum.x;
355 }
356 }
357 if(direction & wxVERTICAL)
358 {
359 current.y = (current.y * 4) / 5;
360 if(current.y < minimum.y)
361 {
362 current.y = minimum.y;
363 }
364 }
365 return current;
366 }
367
368 wxSize wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction,
369 wxSize relative_to) const
370 {
371 if(m_expanded_panel != NULL)
372 {
373 // Next size depends upon children, who are currently in the
374 // expanded panel
375 return m_expanded_panel->DoGetNextLargerSize(direction, relative_to);
376 }
377
378 if(IsMinimised(relative_to))
379 {
380 wxSize current = relative_to;
381 wxSize min_size = GetMinNotMinimisedSize();
382 switch(direction)
383 {
384 case wxHORIZONTAL:
385 if(min_size.x > current.x && min_size.y == current.y)
386 return min_size;
387 break;
388 case wxVERTICAL:
389 if(min_size.x == current.x && min_size.y > current.y)
390 return min_size;
391 break;
392 case wxBOTH:
393 if(min_size.x > current.x && min_size.y > current.y)
394 return min_size;
395 break;
396 default:
397 break;
398 }
399 }
400
401 // TODO: Check for, and delegate to, a sizer
402
403 // Simple (and common) case of single ribbon child
404 if(GetChildren().GetCount() == 1)
405 {
406 wxWindow* child = GetChildren().Item(0)->GetData();
407 wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
408 if(ribbon_child != NULL)
409 {
410 wxClientDC dc((wxRibbonPanel*) this);
411 wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
412 wxSize larger = ribbon_child->GetNextLargerSize(direction, child_relative);
413 if(larger == child_relative)
414 {
415 return relative_to;
416 }
417 else
418 {
419 wxClientDC dc((wxRibbonPanel*) this);
420 return m_art->GetPanelSize(dc, this, larger, NULL);
421 }
422 }
423 }
424
425 // Fallback: Increase by 25% (equal to a prior or subsequent 20% decrease)
426 // Note that due to rounding errors, this increase may not exactly equal a
427 // matching decrease - an ideal solution would not have these errors, but
428 // avoiding them is non-trivial unless an increase is by 100% rather than
429 // a fractional amount. This would then be non-ideal as the resizes happen
430 // at very large intervals.
431 wxSize current(relative_to);
432 if(direction & wxHORIZONTAL)
433 {
434 current.x = (current.x * 5 + 3) / 4;
435 }
436 if(direction & wxVERTICAL)
437 {
438 current.y = (current.y * 5 + 3) / 4;
439 }
440 return current;
441 }
442
443 bool wxRibbonPanel::CanAutoMinimise() const
444 {
445 return (m_flags & wxRIBBON_PANEL_NO_AUTO_MINIMISE) == 0
446 && m_minimised_size.IsFullySpecified();
447 }
448
449 wxSize wxRibbonPanel::GetMinSize() const
450 {
451 if(m_expanded_panel != NULL)
452 {
453 // Minimum size depends upon children, who are currently in the
454 // expanded panel
455 return m_expanded_panel->GetMinSize();
456 }
457
458 if(CanAutoMinimise())
459 {
460 return m_minimised_size;
461 }
462 else
463 {
464 return GetMinNotMinimisedSize();
465 }
466 }
467
468 wxSize wxRibbonPanel::GetMinNotMinimisedSize() const
469 {
470 // TODO: Ask sizer
471
472 // Common case of no sizer and single child taking up the entire panel
473 if(GetChildren().GetCount() == 1)
474 {
475 wxWindow* child = GetChildren().Item(0)->GetData();
476 wxClientDC dc((wxRibbonPanel*) this);
477 return m_art->GetPanelSize(dc, this, child->GetMinSize(), NULL);
478 }
479
480 return wxRibbonControl::GetMinSize();
481 }
482
483 wxSize wxRibbonPanel::DoGetBestSize() const
484 {
485 // TODO: Ask sizer
486
487 // Common case of no sizer and single child taking up the entire panel
488 if(GetChildren().GetCount() == 1)
489 {
490 wxWindow* child = GetChildren().Item(0)->GetData();
491 wxClientDC dc((wxRibbonPanel*) this);
492 return m_art->GetPanelSize(dc, this, child->GetBestSize(), NULL);
493 }
494
495 return wxRibbonControl::DoGetBestSize();
496 }
497
498 bool wxRibbonPanel::Realize()
499 {
500 bool status = true;
501
502 for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
503 node;
504 node = node->GetNext())
505 {
506 wxRibbonControl* child = wxDynamicCast(node->GetData(), wxRibbonControl);
507 if(child == NULL)
508 {
509 continue;
510 }
511 if(!child->Realize())
512 {
513 status = false;
514 }
515 }
516
517 wxSize minimum_children_size(0, 0);
518 // TODO: Ask sizer if there is one
519 if(GetChildren().GetCount() == 1)
520 {
521 minimum_children_size = GetChildren().GetFirst()->GetData()->GetMinSize();
522 }
523
524 if(m_art != NULL)
525 {
526 wxClientDC temp_dc(this);
527
528 m_smallest_unminimised_size =
529 m_art->GetPanelSize(temp_dc, this, minimum_children_size, NULL);
530
531 wxSize bitmap_size;
532 wxSize panel_min_size = GetMinNotMinimisedSize();
533 m_minimised_size = m_art->GetMinimisedPanelMinimumSize(temp_dc, this,
534 &bitmap_size, &m_preferred_expand_direction);
535 if(m_minimised_icon.IsOk() && m_minimised_icon.GetSize() != bitmap_size)
536 {
537 wxImage img(m_minimised_icon.ConvertToImage());
538 img.Rescale(bitmap_size.GetWidth(), bitmap_size.GetHeight(), wxIMAGE_QUALITY_HIGH);
539 m_minimised_icon_resized = wxBitmap(img);
540 }
541 else
542 {
543 m_minimised_icon_resized = m_minimised_icon;
544 }
545 if(m_minimised_size.x > panel_min_size.x &&
546 m_minimised_size.y > panel_min_size.y)
547 {
548 // No point in having a minimised size which is larger than the
549 // minimum size which the children can go to.
550 m_minimised_size = wxSize(-1, -1);
551 }
552 else
553 {
554 if(m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL)
555 {
556 m_minimised_size.x = panel_min_size.x;
557 }
558 else
559 {
560 m_minimised_size.y = panel_min_size.y;
561 }
562 }
563 }
564 else
565 {
566 m_minimised_size = wxSize(-1, -1);
567 }
568
569 return Layout() && status;
570 }
571
572 bool wxRibbonPanel::Layout()
573 {
574 if(IsMinimised())
575 {
576 // Children are all invisible when minimised
577 return true;
578 }
579
580 // TODO: Delegate to a sizer
581
582 // Common case of no sizer and single child taking up the entire panel
583 if(GetChildren().GetCount() == 1)
584 {
585 wxWindow* child = GetChildren().Item(0)->GetData();
586 wxPoint position;
587 wxClientDC dc(this);
588 wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
589 child->SetSize(position.x, position.y, size.GetWidth(), size.GetHeight());
590 }
591 return true;
592 }
593
594 void wxRibbonPanel::OnMouseClick(wxMouseEvent& WXUNUSED(evt))
595 {
596 if(IsMinimised())
597 {
598 if(m_expanded_panel != NULL)
599 {
600 HideExpanded();
601 }
602 else
603 {
604 ShowExpanded();
605 }
606 }
607 }
608
609 wxRibbonPanel* wxRibbonPanel::GetExpandedDummy()
610 {
611 return m_expanded_dummy;
612 }
613
614 wxRibbonPanel* wxRibbonPanel::GetExpandedPanel()
615 {
616 return m_expanded_panel;
617 }
618
619 bool wxRibbonPanel::ShowExpanded()
620 {
621 if(!IsMinimised())
622 {
623 return false;
624 }
625 if(m_expanded_dummy != NULL || m_expanded_panel != NULL)
626 {
627 return false;
628 }
629
630 wxSize size = GetBestSize();
631 wxPoint pos = GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
632 size, m_preferred_expand_direction).GetTopLeft();
633
634 // Need a top-level frame to contain the expanded panel
635 wxFrame *container = new wxFrame(NULL, wxID_ANY, GetLabel(),
636 pos, size, wxFRAME_NO_TASKBAR | wxBORDER_NONE);
637
638 m_expanded_panel = new wxRibbonPanel(container, wxID_ANY,
639 GetLabel(), m_minimised_icon, wxPoint(0, 0), size, m_flags);
640
641 m_expanded_panel->SetArtProvider(m_art);
642 m_expanded_panel->m_expanded_dummy = this;
643
644 // Move all children to the new panel.
645 // Conceptually it might be simpler to reparent this entire panel to the
646 // container and create a new panel to sit in its place while expanded.
647 // This approach has a problem though - when the panel is reinserted into
648 // its original parent, it'll be at a different position in the child list
649 // and thus assume a new position.
650 // NB: Children iterators not used as behaviour is not well defined
651 // when iterating over a container which is being emptied
652 while(!GetChildren().IsEmpty())
653 {
654 wxWindow *child = GetChildren().GetFirst()->GetData();
655 child->Reparent(m_expanded_panel);
656 child->Show();
657 }
658
659 // TODO: Move sizer to new panel
660
661 m_expanded_panel->Realize();
662 Refresh();
663 container->Show();
664 m_expanded_panel->SetFocus();
665
666 return true;
667 }
668
669 bool wxRibbonPanel::ShouldSendEventToDummy(wxEvent& evt)
670 {
671 // For an expanded panel, filter events between being sent up to the
672 // floating top level window or to the dummy panel sitting in the ribbon
673 // bar.
674
675 // Child focus events should not be redirected, as the child would not be a
676 // child of the window the event is redirected to. All other command events
677 // seem to be suitable for redirecting.
678 return evt.IsCommandEvent() && evt.GetEventType() != wxEVT_CHILD_FOCUS;
679 }
680
681 bool wxRibbonPanel::TryAfter(wxEvent& evt)
682 {
683 if(m_expanded_dummy && ShouldSendEventToDummy(evt))
684 {
685 wxPropagateOnce propagateOnce(evt);
686 return m_expanded_dummy->GetEventHandler()->ProcessEvent(evt);
687 }
688 else
689 {
690 return wxRibbonControl::TryAfter(evt);
691 }
692 }
693
694 static bool IsAncestorOf(wxWindow *ancestor, wxWindow *window)
695 {
696 while(window != NULL)
697 {
698 wxWindow *parent = window->GetParent();
699 if(parent == ancestor)
700 return true;
701 else
702 window = parent;
703 }
704 return false;
705 }
706
707 void wxRibbonPanel::OnKillFocus(wxFocusEvent& evt)
708 {
709 if(m_expanded_dummy)
710 {
711 wxWindow *receiver = evt.GetWindow();
712 if(IsAncestorOf(this, receiver))
713 {
714 m_child_with_focus = receiver;
715 receiver->Connect(wxEVT_KILL_FOCUS,
716 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus),
717 NULL, this);
718 }
719 else if(receiver == NULL || receiver != m_expanded_dummy)
720 {
721 HideExpanded();
722 }
723 }
724 }
725
726 void wxRibbonPanel::OnChildKillFocus(wxFocusEvent& evt)
727 {
728 if(m_child_with_focus == NULL)
729 return; // Should never happen, but a check can't hurt
730
731 m_child_with_focus->Disconnect(wxEVT_KILL_FOCUS,
732 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus), NULL, this);
733 m_child_with_focus = NULL;
734
735 wxWindow *receiver = evt.GetWindow();
736 if(receiver == this || IsAncestorOf(this, receiver))
737 {
738 m_child_with_focus = receiver;
739 receiver->Connect(wxEVT_KILL_FOCUS,
740 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus), NULL, this);
741 evt.Skip();
742 }
743 else if(receiver == NULL || receiver != m_expanded_dummy)
744 {
745 HideExpanded();
746 // Do not skip event, as the panel has been de-expanded, causing the
747 // child with focus to be reparented (and hidden). If the event
748 // continues propogation then bad things happen.
749 }
750 else
751 {
752 evt.Skip();
753 }
754 }
755
756 bool wxRibbonPanel::HideExpanded()
757 {
758 if(m_expanded_dummy == NULL)
759 {
760 if(m_expanded_panel)
761 {
762 return m_expanded_panel->HideExpanded();
763 }
764 else
765 {
766 return false;
767 }
768 }
769
770 // Move children back to original panel
771 // NB: Children iterators not used as behaviour is not well defined
772 // when iterating over a container which is being emptied
773 while(!GetChildren().IsEmpty())
774 {
775 wxWindow *child = GetChildren().GetFirst()->GetData();
776 child->Reparent(m_expanded_dummy);
777 child->Hide();
778 }
779
780 // TODO: Move sizer back
781
782 m_expanded_dummy->m_expanded_panel = NULL;
783 m_expanded_dummy->Realize();
784 m_expanded_dummy->Refresh();
785 wxWindow *parent = GetParent();
786 Destroy();
787 parent->Destroy();
788
789 return true;
790 }
791
792 wxRect wxRibbonPanel::GetExpandedPosition(wxRect panel,
793 wxSize expanded_size,
794 wxDirection direction)
795 {
796 // Strategy:
797 // 1) Determine primary position based on requested direction
798 // 2) Move the position so that it sits entirely within a display
799 // (for single monitor systems, this moves it into the display region,
800 // but for multiple monitors, it does so without splitting it over
801 // more than one display)
802 // 2.1) Move in the primary axis
803 // 2.2) Move in the secondary axis
804
805 wxPoint pos;
806 bool primary_x = false;
807 int secondary_x = 0;
808 int secondary_y = 0;
809 switch(direction)
810 {
811 case wxNORTH:
812 pos.x = panel.GetX() + (panel.GetWidth() - expanded_size.GetWidth()) / 2;
813 pos.y = panel.GetY() - expanded_size.GetHeight();
814 primary_x = true;
815 secondary_y = 1;
816 break;
817 case wxEAST:
818 pos.x = panel.GetRight();
819 pos.y = panel.GetY() + (panel.GetHeight() - expanded_size.GetHeight()) / 2;
820 secondary_x = -1;
821 break;
822 case wxSOUTH:
823 pos.x = panel.GetX() + (panel.GetWidth() - expanded_size.GetWidth()) / 2;
824 pos.y = panel.GetBottom();
825 primary_x = true;
826 secondary_y = -1;
827 break;
828 case wxWEST:
829 default:
830 pos.x = panel.GetX() - expanded_size.GetWidth();
831 pos.y = panel.GetY() + (panel.GetHeight() - expanded_size.GetHeight()) / 2;
832 secondary_x = 1;
833 break;
834 }
835 wxRect expanded(pos, expanded_size);
836
837 wxRect best(expanded);
838 int best_distance = INT_MAX;
839
840 const unsigned display_n = wxDisplay::GetCount();
841 unsigned display_i;
842 for(display_i = 0; display_i < display_n; ++display_i)
843 {
844 wxRect display = wxDisplay(display_i).GetGeometry();
845
846 if(display.Contains(expanded))
847 {
848 return expanded;
849 }
850 else if(display.Intersects(expanded))
851 {
852 wxRect new_rect(expanded);
853 int distance = 0;
854
855 if(primary_x)
856 {
857 if(expanded.GetRight() > display.GetRight())
858 {
859 distance = expanded.GetRight() - display.GetRight();
860 new_rect.x -= distance;
861 }
862 else if(expanded.GetLeft() < display.GetLeft())
863 {
864 distance = display.GetLeft() - expanded.GetLeft();
865 new_rect.x += distance;
866 }
867 }
868 else
869 {
870 if(expanded.GetBottom() > display.GetBottom())
871 {
872 distance = expanded.GetBottom() - display.GetBottom();
873 new_rect.y -= distance;
874 }
875 else if(expanded.GetTop() < display.GetTop())
876 {
877 distance = display.GetTop() - expanded.GetTop();
878 new_rect.y += distance;
879 }
880 }
881 if(!display.Contains(new_rect))
882 {
883 // Tried moving in primary axis, but failed.
884 // Hence try moving in the secondary axis.
885 int dx = secondary_x * (panel.GetWidth() + expanded_size.GetWidth());
886 int dy = secondary_y * (panel.GetHeight() + expanded_size.GetHeight());
887 new_rect.x += dx;
888 new_rect.y += dy;
889
890 // Squaring makes secondary moves more expensive (and also
891 // prevents a negative cost)
892 distance += dx * dx + dy * dy;
893 }
894 if(display.Contains(new_rect) && distance < best_distance)
895 {
896 best = new_rect;
897 best_distance = distance;
898 }
899 }
900 }
901
902 return best;
903 }
904
905 #endif // wxUSE_RIBBON