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