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