Audacity 3.2.0
ExpandingToolBar.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ExpandingToolBar.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//****************************************************************//****************************************************************//****************************************************************//****************************************************************//****************************************************************//*******************************************************************/
65
66#include "ExpandingToolBar.h"
67
68// For compilers that support precompilation, includes "wx/wx.h".
69#include <wx/wxprec.h>
70
71#ifndef WX_PRECOMP
72#include <wx/window.h>
73#endif
74
75#include <wx/wx.h>
76#include <wx/dcmemory.h>
77#include <wx/log.h>
78#include <wx/dialog.h>
79
80#include "AButton.h"
81#include "AllThemeResources.h"
82
83const int kToggleButtonHeight = 8;
84const int kMyTimerInterval = 50; // every 50 ms -> ~20 updates per second
85const wxRect kDummyRect = wxRect(-9999, -9999, 0, 0);
86
87enum {
90};
91
93{
94public:
95 std::vector<ExpandingToolBar*> childArray;
96 std::vector<wxRect> rectArray;
97 std::vector<int> rowArray;
98};
99
100//
101// ExpandingToolBar
102//
103
104BEGIN_EVENT_TABLE(ExpandingToolBar, wxPanelWrapper)
109
111
112//static
113int ExpandingToolBar::msNoAutoExpandStack = 0;
114
116 wxWindowID id,
117 const wxPoint& pos,
118 const wxSize& size):
119 wxPanelWrapper(parent, id, pos, size),
120 mIsAutoExpanded(false),
121 mIsManualExpanded(false),
122 mIsExpanded(false),
123 mAutoExpand(true),
124 mFirstTime(true),
125 mFrameParent(NULL),
126 mDialogParent(NULL),
127 mAreaParent(NULL),
128 mSavedArrangement{},
129 mTopLevelParent(NULL)
130{
131 mMainPanel = safenew wxPanelWrapper(this, -1,
132 wxDefaultPosition, wxSize(1, 1));
133 mExtraPanel = safenew wxPanelWrapper(this, -1,
134 wxDefaultPosition, wxSize(1, 1));
135
136 mGrabber = NULL;
137
138 ToolBarArea *toolBarParent =
139 dynamic_cast<ToolBarArea *>(GetParent());
140 if (toolBarParent)
141 mGrabber = safenew ToolBarGrabber(this, -1, this);
142
144 //wxImage hbar = theTheme.Image(bmpToolBarToggle);
145 //wxColour magicColor = wxColour(0, 255, 255);
146 //ImageArray fourStates = ImageRoll::SplitV(hbar, magicColor);
147/*
148 mToggleButton = safenew AButton(this, kToggleButtonID,
149 wxDefaultPosition, wxDefaultSize,
150 ImageRoll(ImageRoll::HorizontalRoll,
151 fourStates[0], magicColor),
152 ImageRoll(ImageRoll::HorizontalRoll,
153 fourStates[1], magicColor),
154 ImageRoll(ImageRoll::HorizontalRoll,
155 fourStates[2], magicColor),
156 ImageRoll(ImageRoll::HorizontalRoll,
157 fourStates[3], magicColor),
158 true);
159 mToggleButton->UseDisabledAsDownHiliteImage(true);
160*/
161 SetAutoLayout(true);
162 mTimer.SetOwner(this, kTimerID);
163}
164
166{
167}
168
169void ExpandingToolBar::OnSize(wxSizeEvent & WXUNUSED(event))
170{
172 return;
173
174 // At the time of construction, it wasn't "safe" to tell
175 // our parent that we've just joined the window, so we check
176 // for it during our first OnSize event.
177
178 if (!mFrameParent) {
179 ToolBarFrame *toolBarParent =
180 dynamic_cast<ToolBarFrame *>(GetParent());
181 if (toolBarParent) {
182 // We were placed into a floating window
183 mFrameParent = toolBarParent;
184 toolBarParent->SetChild(this);
185 }
186 }
187
188 if (!mDialogParent) {
189 ToolBarDialog *toolBarParent =
190 dynamic_cast<ToolBarDialog *>(GetParent());
191 if (toolBarParent) {
192 // We were placed into a dialog
193 mDialogParent = toolBarParent;
194 toolBarParent->SetChild(this);
195 }
196 }
197
198 if (!mAreaParent) {
199 ToolBarArea *toolBarParent =
200 dynamic_cast<ToolBarArea *>(GetParent());
201 if (toolBarParent) {
202 // We were placed into an area full of other toolbars
203 mAreaParent = toolBarParent;
204 toolBarParent->AddChild(this);
205 }
206 }
207}
208
209void ExpandingToolBar::OnToggle(wxCommandEvent & WXUNUSED(event))
210{
211 if (mIsExpanded)
212 Collapse();
213 else
214 Expand();
215}
216
218{
219 // We set both mIsManualExpanded and mIsAutoExpanded to true;
220 // that way if the user manually collapses the toolbar we set
221 // mIsManualExpanded to false but keep mIsAutoExpanded to true
222 // to prevent it from being auto-expanded again until the user
223 // actually moves the mouse completely away and back again later.
224
226 mIsManualExpanded = true;
227 mIsAutoExpanded = true;
228 Fit();
229}
230
231void ExpandingToolBar::Collapse(bool now /* = false */)
232{
233 // After being manually collapsed, we set mIsAutoExpanded back to
234 // true, which prevents it from being immediately auto-expanded
235 // again until after the mouse actually moves away and then
236 // back again later.
237
239 mIsManualExpanded = false;
240 mIsAutoExpanded = false;
241 Fit();
242 mIsAutoExpanded = true;
243
244 if (now) {
246
247 MoveDrawer(wxSize(0, 0));
248 }
249}
250
252{
254 mIsManualExpanded == false && mIsAutoExpanded == false) {
256 mIsAutoExpanded = true;
257 Fit();
258 }
259}
260
262{
263#ifdef EXPERIMENTAL_ROLL_UP_DIALOG
264 if (mIsAutoExpanded == true && mIsManualExpanded == false) {
266 mIsAutoExpanded = false;
267 Fit();
268 }
269#endif
270}
271
272class ExpandingToolBarEvtHandler final : public wxEvtHandler
273{
274 public:
276 wxWindow *window,
277 wxEvtHandler *inheritedEvtHandler)
278 {
279 mToolBar = toolbar;
280 mWindow = window;
281 mInheritedEvtHandler = inheritedEvtHandler;
282
283 window->PushEventHandler(this);
284}
285
286 bool ProcessEvent(wxEvent& evt) override
287 {
288// if (mToolBar->IsCursorInWindow())
290// else
291// mToolBar->TryAutoExpand();
292
293 return mInheritedEvtHandler->ProcessEvent(evt);
294 }
295
297 {
298 mWindow->RemoveEventHandler(this);
299 }
300
301protected:
303 wxWindow *mWindow;
304 wxEvtHandler *mInheritedEvtHandler;
305
306 DECLARE_NO_COPY_CLASS(ExpandingToolBarEvtHandler)
307};
308
310{
311 if (!mWindowHash[win]) {
312 mHandlers.push_back(std::make_unique<ExpandingToolBarEvtHandler>
313 (this, win, win->GetEventHandler()));
314 mWindowHash[win] = 1;
315 }
316
317 wxWindowList children = win->GetChildren();
318 for(auto child : children)
320}
321
323{
324 mMainSize = mMainPanel->GetBestSize();
325 mExtraSize = mExtraPanel->GetBestSize();
326 mButtonSize = wxSize(wxMax(mMainSize.x, mExtraSize.x),
328
329 int left = 0;
330
331 if (mGrabber) {
332 mGrabberSize = mGrabber->GetMinSize();
333 left += mGrabberSize.x;
334 }
335 else
336 mGrabberSize = wxSize(0, 0);
337
338 mMainPanel->SetSize(left, 0, mMainSize.x, mMainSize.y);
339 mToggleButton->SetSize(left, mMainSize.y, mButtonSize.x, mButtonSize.y);
340 mExtraPanel->SetSize(left, mMainSize.y + mButtonSize.y,
342
343 if (mGrabber)
344 mGrabber->SetSize(0, 0, left, mMainSize.y + mButtonSize.y);
345
346 // Add event handlers to all children
347 //RecursivelyPushEventHandlers(this);
348
349 return true;
350}
351
353{
354#ifdef EXPERIMENTAL_ROLL_UP_DIALOG
356#else
357 mIsExpanded = true;// JKC - Wedge it open at all times.
358#endif
359
360 int width = mButtonSize.x + mGrabberSize.x;
361
362 wxSize baseWindowSize = wxSize(width,
363 mMainSize.y + mButtonSize.y);
364
365 mTargetDrawerSize = wxSize(mButtonSize.x, 0);
366
367 if (mIsExpanded)
369
371
372 // The first time, we always update the size. Otherwise, we set
373 // a target size, and the actual size changes during a timer
374 // event.
375
376 if (mFirstTime) {
377 mFirstTime = false;
378 mCurrentDrawerSize = wxSize(mExtraSize.x, 0);
379 mCurrentTotalSize = baseWindowSize;
380
381 SetMinSize(mCurrentTotalSize);
382 SetMaxSize(mCurrentTotalSize);
383 SetSize(mCurrentTotalSize);
384 }
385
386 // wxTimers seem to be a little unreliable - sometimes they stop for
387 // no good reason, so this "primes" it every now and then...
388 mTimer.Stop();
390}
391
393{
394 wxPoint globalMouse = ::wxGetMousePosition();
395 wxPoint localMouse = ScreenToClient(globalMouse);
396
397 bool result = (localMouse.x >= 0 && localMouse.y >= 0 &&
398 localMouse.x < mCurrentTotalSize.x &&
399 localMouse.y < mCurrentTotalSize.y);
400
401 // The grabber doesn't count!
402 if (mGrabber && mGrabber->GetRect().Contains(localMouse))
403 result = false;
404
405 return result;
406}
407
409{
410 // This is how we make sure the extra panel, which slides out
411 // like a drawer, appears on top of everything else in the window...
412
413 wxPoint pos;
414 pos.x = mGrabberSize.x;
415 pos.y = mMainSize.y + mButtonSize.y;
416 wxWindow *frame = this;
417 while(!frame->IsTopLevel()) {
418 pos += frame->GetPosition();
419 frame = frame->GetParent();
420 }
421
422 mExtraPanel->Reparent(frame);
423 mExtraPanel->SetPosition(pos);
424}
425
426void ExpandingToolBar::MoveDrawer(wxSize prevSize)
427{
429 mMainSize.y +
430 mButtonSize.y +
432
433 if (mFrameParent) {
434 // If we're in a tool window
435
436 SetMinSize(mCurrentTotalSize);
437 SetMaxSize(mCurrentTotalSize);
438 SetSize(mCurrentTotalSize);
439
440 GetParent()->Fit();
441 }
442
443 if (mDialogParent) {
444 // If we're in a dialog
445
446 SetMinSize(mCurrentTotalSize);
447 SetMaxSize(mCurrentTotalSize);
448 SetSize(mCurrentTotalSize);
449
450 GetParent()->Fit();
451 }
452
453 if (mAreaParent) {
454 // If we're in a tool area
455
456 if (mCurrentDrawerSize.y > 0 && prevSize.y == 0) {
458 mExtraPanel->Show();
459 }
460
461 mExtraPanel->SetMinSize(mCurrentDrawerSize);
462 mExtraPanel->SetMaxSize(mCurrentDrawerSize);
464
465 if (mCurrentDrawerSize.y == 0)
466 mExtraPanel->Hide();
467 }
468}
469
470void ExpandingToolBar::OnTimer(wxTimerEvent & WXUNUSED(event))
471{
475 else if (!IsCursorInWindow())
477
479 return;
480
481 // This accelerates the current size towards the target size;
482 // it's a neat way for the window to roll open, but in such a
483 // way that it
484
485 wxSize prevSize = mCurrentDrawerSize;
487 if (abs((mCurrentDrawerSize-mTargetDrawerSize).x)<2 &&
490
491 MoveDrawer(prevSize);
492}
493
495{
496 wxSize size = GetClientSize();
497 wxBitmap bitmap(size.x, size.y);
498 wxClientDC winDC(this);
499 wxMemoryDC memDC;
500 memDC.SelectObject(bitmap);
501 memDC.Blit(0, 0, size.x, size.y,
502 &winDC, 0, 0);
503 return bitmap;
504}
505
507{
508 if (!mAreaParent)
509 return;
510
511 int j;
512
514
515 mTimer.Stop();
516
517 // This gives time for wx to finish redrawing the window that way.
518 // HACK: why do we need to do it so many times???
519 for(j=0; j<500; j++)
520 ::wxSafeYield();
521
522 wxBitmap toolbarBitmap = GetToolbarBitmap();
523
527
528 mAreaParent->Refresh(true);
529
530 mTopLevelParent = this;
531 while(!mTopLevelParent->IsTopLevel())
532 mTopLevelParent = mTopLevelParent->GetParent();
533
534 wxPoint hotSpot = ScreenToClient(wxGetMousePosition());
535
536 hotSpot -= (ClientToScreen(wxPoint(0, 0)) -
537 mAreaParent->ClientToScreen(wxPoint(0, 0)));
538
541
542 wxColour magicColor = wxColour(0, 255, 255);
543// wxImage tgtImage = theTheme.Image(bmpToolBarTarget);
544// ImageRoll tgtImageRoll = ImageRoll(ImageRoll::VerticalRoll,
545// tgtImage,
546// magicColor);
547 mTargetPanel = safenew ImageRollPanel(mAreaParent, -1, //tgtImageRoll,
548 wxDefaultPosition,
549 wxDefaultSize,
550 wxTRANSPARENT_WINDOW);
552 mTargetPanel->SetSize(mDropTarget);
553
554 // This gives time for wx to finish redrawing the window that way.
555 // HACK: why do we need to do it several times???
556 for(j=0; j<500; j++)
557 ::wxSafeYield();
558
560
561 mDragImage = std::make_unique<wxDragImage>(toolbarBitmap);
562 mDragImage->BeginDrag(hotSpot, mAreaParent, mTopLevelParent);
563 mDragImage->Show();
564 mDragImage->Move(ScreenToClient(wxGetMousePosition()));
565}
566
568{
570 return;
571
572 wxPoint cursorPos = mAreaParent->ScreenToClient(wxGetMousePosition());
573 wxRect prevTarget = mDropTarget;
574 int best_dist_sq = 99999;
575 int i;
576
577 for(i = 0; i < (int)mDropTargets.size(); i++) {
578 int x = (mDropTargets[i].x + (mDropTargets[i].width/2))-cursorPos.x;
579 int y = (mDropTargets[i].y + (mDropTargets[i].height/2))-cursorPos.y;
580 int dist_sq = (x*x) + (y*y);
581
582 if (dist_sq < best_dist_sq) {
583 best_dist_sq = dist_sq;
585 }
586 }
587
588 if (!mAreaParent->GetRect().Contains(cursorPos))
590
591 if (mDropTarget != prevTarget) {
592 mDragImage->Hide();
593
594 wxRect r = mDropTarget;
595 r.Inflate(4, 4);
596 mTargetPanel->SetSize(r);
597
598 #if 0
599 wxClientDC dc(mAreaParent);
600 dc.DestroyClippingRegion();
601 dc.SetLogicalFunction(wxINVERT);
602 wxRect r = prevTarget;
603 r.Inflate(4, 4);
604 dc.DrawRectangle(r);
605 r = mDropTarget;
606 r.Inflate(4, 4);
607 dc.DrawRectangle(r);
608 #endif
609
610 // This gives time for wx to finish redrawing the window that way.
611 // HACK: why do we need to do it so many times???
612 for(i=0; i<500; i++)
613 ::wxSafeYield();
614
615 mDragImage->Show();
616 mDragImage->Move(ScreenToClient(wxGetMousePosition()));
617 }
618 else
619 mDragImage->Move(ScreenToClient(wxGetMousePosition()));
620}
621
623{
625 return;
626
627 // DELETE mTargetPanel; // I think this is not needed, but unreachable anyway -- PRL
628
630
631 mDragImage->Hide();
632 mDragImage->EndDrag();
633
635
636 if (mDropTarget == kDummyRect) {
638 }
639 else {
640 mSavedArrangement.reset();
642 }
643
644 // Keep all drawers closed until the user moves specifically to a
645 // different window
647
648 mTopLevelParent->Refresh(true);
649
651}
652
653//
654// ToolBarGrabber
655//
656
657BEGIN_EVENT_TABLE(ToolBarGrabber, wxPanelWrapper)
658 EVT_PAINT(ToolBarGrabber::OnPaint)
659 EVT_SIZE(ToolBarGrabber::OnSize)
660 EVT_MOUSE_EVENTS(ToolBarGrabber::OnMouse)
662
664
666 wxWindowID id,
667 ExpandingToolBar *ownerToolbar,
668 const wxPoint& pos,
669 const wxSize& size):
670 wxPanelWrapper(parent, id, pos, size),
671 mOwnerToolBar(ownerToolbar)
672{
673#if 0
674 wxImage grabberImages = theTheme.Image(bmpToolBarGrabber);
675 wxColour magicColor = wxColour(0, 255, 255);
676 ImageArray images = ImageRoll::SplitH(grabberImages, magicColor);
677
678 mImageRoll[0] = ImageRoll(ImageRoll::VerticalRoll,
679 images[0],
680 magicColor);
681 mImageRoll[1] = ImageRoll(ImageRoll::VerticalRoll,
682 images[1],
683 magicColor);
684
685 SetMinSize(mImageRoll[0].GetMinSize());
686 SetMaxSize(mImageRoll[1].GetMaxSize());
687#endif
688 mState = 0;
689}
690
691void ToolBarGrabber::OnMouse(wxMouseEvent &event)
692{
693 int prevState = mState;
694
695 // Handle highlighting the image if the mouse is over it
696
697 if (event.Entering())
698 mState = 1;
699 else if (event.Leaving())
700 mState = 0;
701 else {
702 wxSize clientSize = GetClientSize();
703
704 if (event.m_x >= 0 && event.m_y >= 0 &&
705 event.m_x < clientSize.x && event.m_y < clientSize.y)
706 mState = 1;
707 else
708 mState = 0;
709 }
710
711 if (event.ButtonDown())
713
714 if (mState != prevState)
715 Refresh(false);
716}
717
718void ToolBarGrabber::OnPaint(wxPaintEvent & WXUNUSED(event))
719{
720 wxPaintDC dc(this);
721
722 // mImageRoll[mState].Draw(dc, GetClientRect());
723}
724
725void ToolBarGrabber::OnSize(wxSizeEvent & WXUNUSED(event))
726{
727 Refresh(false);
728}
729
730//
731// ToolBarDialog
732//
733
734BEGIN_EVENT_TABLE(ToolBarDialog, wxDialogWrapper)
736
738
739ToolBarDialog::ToolBarDialog(wxWindow* parent,
740 wxWindowID id,
742 const wxPoint& pos):
743 wxDialogWrapper(parent, id, name, pos, wxSize(1, 1),
744// Workaround for bug in __WXMSW__. No close box on a wxDialog unless wxSYSTEM_MENU is used.
745#ifdef __WXMSW__
746 wxSYSTEM_MENU |
747#endif
748 wxCAPTION|wxCLOSE_BOX),
749 mChild(NULL)
750{
751}
752
754{
755}
756
758{
759 mChild = child;
760 if (mChild && mChild->GetParent() != this)
761 mChild->Reparent(this);
762
763 Fit();
764}
765
767{
768 if (mChild) {
769 wxSize childSize = mChild->GetBestSize();
770
771 // Take into account the difference between the content
772 // size and the frame size
773 wxSize curContentSize = GetClientSize();
774 wxSize curFrameSize = GetSize();
775 wxSize newFrameSize = childSize + (curFrameSize - curContentSize);
776
777 SetSizeHints(newFrameSize, newFrameSize);
778 SetSize(newFrameSize);
779 }
780}
781
782//
783// ToolBarFrame
784//
785
786BEGIN_EVENT_TABLE(ToolBarFrame, wxMiniFrame)
788
789IMPLEMENT_CLASS(ToolBarFrame, wxMiniFrame)
790
791ToolBarFrame::ToolBarFrame(wxWindow* parent,
792 wxWindowID id,
793 const wxString& name,
794 const wxPoint& pos):
795 wxMiniFrame(parent, id, name, pos, wxSize(1, 1),
796// Workaround for bug in __WXMSW__. No close box on a miniframe unless wxSYSTEM_MENU is used.
797#ifdef __WXMSW__
798 wxSYSTEM_MENU |
799#endif
800 wxCAPTION|wxCLOSE_BOX),
801 mChild(NULL)
802{
803}
804
806{
807}
808
810{
811 mChild = child;
812 if (mChild && mChild->GetParent() != this)
813 mChild->Reparent(this);
814
815 Fit();
816}
817
819{
820 if (mChild) {
821 wxSize childSize = mChild->GetBestSize();
822
823 // Take into account the difference between the content
824 // size and the frame size
825 wxSize curContentSize = GetClientSize();
826 wxSize curFrameSize = GetSize();
827 wxSize newFrameSize = childSize + (curFrameSize - curContentSize);
828
829 SetSizeHints(newFrameSize, newFrameSize);
830 SetSize(newFrameSize);
831 }
832}
833
834//
835// ToolBarArea
836//
837
838BEGIN_EVENT_TABLE(ToolBarArea, wxPanelWrapper)
839 EVT_SIZE(ToolBarArea::OnSize)
840 EVT_MOUSE_EVENTS(ToolBarArea::OnMouse)
842
844
845ToolBarArea::ToolBarArea(wxWindow* parent,
846 wxWindowID id,
847 const wxPoint& pos,
848 const wxSize& size):
849 wxPanelWrapper(parent, id, pos, size),
850 mInOnSize(false),
851 mCapturedChild(NULL)
852{
853
854}
855
857{
858}
859
860void ToolBarArea::ContractRow(int rowIndex)
861{
862 // Contract all of the toolbars in a given row to their
863 // minimum size. This is an intermediate step in layout.
864
865 int i;
866 int x = 0;
867
868 for(i = 0; i < (int)mChildArray.size(); i++)
869 if (mRowArray[i] == rowIndex) {
870 wxPoint childPos = mChildArray[i]->GetPosition();
871 wxSize childMin = mChildArray[i]->GetMinSize();
872
873 mChildArray[i]->SetSize(x, childPos.y,
874 childMin.x, childMin.y);
875 x += childMin.x;
876 }
877}
878
879bool ToolBarArea::ExpandRow(int rowIndex)
880{
881 // Expand all of the toolbars in a given row so that the
882 // whole width is filled, if possible. This is the last
883 // step after laying out as many toolbars as possible in
884 // that row. Returns false if it's not possible to fit
885 // all of these toolbars in one row anymore.
886
887 wxSize area = GetClientSize();
888 int i, j, x;
889 int minWidth = 0;
890 int leftoverSpace = 0;
891 int expandableCount = 0;
892 int toolbarCount = 0;
893
894 for(i = 0; i < (int)mChildArray.size(); i++)
895 if (mRowArray[i] == rowIndex) {
896 ExpandingToolBar *child = mChildArray[i];
897 wxSize childMin = child->GetMinSize();
898 wxSize childMax = child->GetMaxSize();
899
900 minWidth += childMin.x;
901
902 toolbarCount++;
903 if (childMax.x > childMin.x)
904 expandableCount++;
905 }
906
907 leftoverSpace = area.x - minWidth;
908
909 if (leftoverSpace <= 0) {
910 if (toolbarCount > 1)
911 return false; // not possible to fit all in one row
912 else
913 return true; // there's only one, so it doesn't matter
914 }
915
916 j = 0;
917 x = 0;
918 for(i = 0; i < (int)mChildArray.size(); i++)
919 if (mRowArray[i] == rowIndex) {
920 ExpandingToolBar *child = mChildArray[i];
921 wxPoint childPos = child->GetPosition();
922 wxSize childMin = child->GetMinSize();
923 wxSize childMax = child->GetMaxSize();
924
925 int width = childMin.x;
926
927 if (childMax.x > childMin.x)
928 width +=
929 (leftoverSpace * (j+1) / expandableCount) -
930 (leftoverSpace * (j) / expandableCount);
931
932 mChildArray[i]->SetSize(x, childPos.y,
933 width, childMin.y);
934 x += width;
935 j++;
936 }
937
938 return true; // success
939}
940
941void ToolBarArea::LayoutOne(int childIndex)
942{
943 wxSize area = GetClientSize();
944 ExpandingToolBar *child = mChildArray[childIndex];
945 wxSize childMin = child->GetMinSize();
946
947 if (childIndex == 0) {
948 mRowArray[childIndex] = 0;
949 mChildArray[childIndex]->SetSize(0, 0, childMin.x, childMin.y);
950 ExpandRow(0);
951
952 #if 0
953 wxPoint p = mChildArray[childIndex]->GetPosition();
954 wxSize s = mChildArray[childIndex]->GetSize();
955
956 wxPrintf("ToolBar %d moved to row %d at (%d, %d), size (%d x %d)\n",
957 childIndex, mRowArray[childIndex],
958 p.x, p.y, s.x, s.y);
959 #endif
960
961 mLastLayoutSize = area;
962
963 return;
964 }
965
966 int prevRow = mRowArray[childIndex-1];
967 ContractRow(prevRow);
968 wxPoint prevPos = mChildArray[childIndex-1]->GetPosition();
969 wxSize prevSize = mChildArray[childIndex-1]->GetSize();
970
971 int prevX = prevPos.x + prevSize.x;
972 int availableWidth = area.x - prevX;
973
974 if (childMin.x <= availableWidth) {
975 // It fits into the same row
976 mRowArray[childIndex] = prevRow;
977 mChildArray[childIndex]->SetSize(prevX, prevPos.y,
978 childMin.x, childMin.y);
979 ExpandRow(prevRow);
980 }
981 else {
982 // Go to the next row
983 ExpandRow(prevRow);
984 mRowArray[childIndex] = prevRow + 1;
985
986 int i;
987 int maxRowHeight = 0;
988 for(i=0; i<childIndex; i++)
989 if (mRowArray[i] == prevRow &&
990 mChildArray[i]->GetSize().y > maxRowHeight)
991 maxRowHeight = mChildArray[i]->GetSize().y;
992
993 mChildArray[childIndex]->SetSize(0, prevPos.y + maxRowHeight,
994 childMin.x, childMin.y);
995 ExpandRow(prevRow+1);
996 }
997
998 // Save the size of the window the last time we moved one of the
999 // toolbars around. If the user does a minor resize, we try to
1000 // preserve the layout. If the user does a major resize, we're
1001 // allowed to redo the layout.
1002 mLastLayoutSize = area;
1003
1004 #if 0
1005 wxPoint p = mChildArray[childIndex]->GetPosition();
1006 wxSize s = mChildArray[childIndex]->GetSize();
1007
1008 wxPrintf("ToolBar %d moved to row %d at (%d, %d), size (%d x %d)\n",
1009 childIndex, mRowArray[childIndex],
1010 p.x, p.y, s.x, s.y);
1011 #endif
1012}
1013
1015{
1016 // Redo the layout from scratch, preserving only the order of
1017 // the children
1018
1019 int i;
1020
1021 for(i = 0; i < (int)mChildArray.size(); i++)
1022 mRowArray[i] = -1;
1023
1024 for(i = 0; i < (int)mChildArray.size(); i++)
1025 LayoutOne(i);
1026
1027 Refresh(true);
1028
1029 return true;
1030}
1031
1033{
1034 // Try to modify the layout as little as possible - but if that's
1035 // impossible, redo the layout as necessary.
1036
1037 int row = -1;
1038 int i, j;
1039
1040 for(i = 0; i < (int)mChildArray.size(); i++) {
1041 if (mRowArray[i] > row) {
1042 row = mRowArray[i];
1043 bool success = ExpandRow(row);
1044 if (!success) {
1045 // Re-layout all toolbars from this row on
1046 for(j = i; j < (int)mChildArray.size(); j++)
1047 LayoutOne(j);
1048 return;
1049 }
1050 }
1051 }
1052}
1053
1055{
1056 Fit(true, true);
1057}
1058
1059void ToolBarArea::Fit(bool horizontal, bool vertical)
1060{
1061 wxSize clientSize = GetClientSize();
1062 wxSize minSize;
1063 wxSize maxSize;
1064 wxSize actualSize;
1065 int i;
1066
1067 minSize.x = 0;
1068 minSize.y = 0;
1069 maxSize.x = 9999;
1070 maxSize.y = 0;
1071 for(i = 0; i < (int)mChildArray.size(); i++) {
1072 wxPoint childPos = mChildArray[i]->GetPosition();
1073 wxSize childSize = mChildArray[i]->GetSize();
1074
1075 if (childPos.x + childSize.x > actualSize.x) {
1076 actualSize.x = childPos.x + childSize.x;
1077 }
1078
1079 if (childSize.x > minSize.x) {
1080 minSize.x = childSize.x;
1081 }
1082
1083 if (childPos.y + childSize.y > maxSize.y) {
1084 maxSize.y = childPos.y + childSize.y;
1085 minSize.y = maxSize.y;
1086 actualSize.y = maxSize.y;
1087 }
1088 }
1089
1090 if (!horizontal && actualSize.x < clientSize.x)
1091 actualSize.x = clientSize.x;
1092 if (!vertical && actualSize.y < clientSize.y)
1093 actualSize.y = clientSize.y;
1094
1095 if (minSize != mMinSize ||
1096 maxSize != mMaxSize) {
1097 mMinSize = minSize;
1098 mMaxSize = maxSize;
1099 SetMinSize(mMinSize);
1100 SetMaxSize(mMaxSize);
1101 }
1102 if (actualSize != mActualSize) {
1103 mActualSize = actualSize;
1104 SetSize(mActualSize);
1105 }
1106}
1107
1108void ToolBarArea::OnSize(wxSizeEvent & WXUNUSED(event))
1109{
1110 if (mInOnSize)
1111 return;
1112
1113 mInOnSize = true;
1114
1115 wxSize currentSize = GetClientSize();
1116
1117 if (abs(currentSize.x - mLastLayoutSize.x) >= 100) {
1118 // If they resize by more than 100 pixels (horizontally),
1119 // we totally redo the layout, preserving the order of the
1120 // toolbars but not the exact position.
1121 Layout();
1122 }
1123 else {
1124 // If it was a minor resize, we try to preserve the positions of
1125 // the toolbars. If this is impossible, we still redo the layout,
1126 // of course.
1127 AdjustLayout();
1128 }
1129
1130 Fit(false, true);
1131
1132 mInOnSize = false;
1133}
1134
1135void ToolBarArea::OnMouse(wxMouseEvent &evt)
1136{
1137 if (mCapturedChild) {
1138 if (evt.ButtonUp())
1140 else if (evt.Moving() || evt.Dragging())
1142 }
1143 else {
1144 evt.Skip();
1145 }
1146}
1147
1149{
1150 int i;
1151
1152 for(i = 0; i < (int)mChildArray.size(); i++)
1153 mChildArray[i]->Collapse(now);
1154}
1155
1157{
1158 mChildArray.push_back(child);
1159 mRowArray.push_back(-1); // unknown row
1160 LayoutOne(mChildArray.size() - 1);
1161 Fit(false, true);
1162}
1163
1165{
1166 int i, j;
1167
1168 for(i = 0; i < (int)mChildArray.size(); i++) {
1169 if (mChildArray[i] == child) {
1170 child->Hide();
1171
1172 mChildArray.erase(mChildArray.begin() + i);
1173 mRowArray.erase(mRowArray.begin() + i);
1174
1175 for(j = i; j < (int)mChildArray.size(); j++)
1176 mRowArray[j] = -1;
1177
1178 for(j = i; j < (int)mChildArray.size(); j++)
1179 LayoutOne(j);
1180
1181 Fit(false, true);
1182 }
1183 }
1184}
1185
1186std::unique_ptr<ToolBarArrangement> ToolBarArea::SaveArrangement()
1187{
1188 auto arrangement = std::make_unique<ToolBarArrangement>();
1189 int i;
1190
1191 arrangement->childArray = mChildArray;
1192 arrangement->rowArray = mRowArray;
1193
1194 for(i = 0; i < (int)mChildArray.size(); i++)
1195 arrangement->rectArray.push_back(mChildArray[i]->GetRect());
1196
1197 return arrangement;
1198}
1199
1200void ToolBarArea::RestoreArrangement(std::unique_ptr<ToolBarArrangement>&& arrangement)
1201{
1202 int i;
1203
1204 mChildArray = arrangement->childArray;
1205 mRowArray = arrangement->rowArray;
1206
1207 for(i = 0; i < (int)mChildArray.size(); i++) {
1208 mChildArray[i]->SetSize(arrangement->rectArray[i]);
1209 mChildArray[i]->Show();
1210 }
1211
1212 Fit(false, true);
1213
1214 arrangement.reset();
1215}
1216
1217std::vector<wxRect> ToolBarArea::GetDropTargets()
1218{
1219 mDropTargets.clear();
1220 mDropTargetIndices.clear();
1221 mDropTargetRows.clear();
1222
1223 int numChildren = (int)mChildArray.size();
1224 int i;
1225 int row = -1;
1226
1227 if (numChildren == 0)
1228 return mDropTargets;
1229
1230 for(i=0; i<numChildren; i++) {
1231 int childRow = mRowArray[i];
1232 wxRect childRect = mChildArray[i]->GetRect();
1233
1234 if (childRow != row) {
1235 // Add a target before this child (at beginning of row only)
1236 row = childRow;
1237 mDropTargetIndices.push_back(i);
1238 mDropTargetRows.push_back(row);
1239 mDropTargets.push_back(wxRect(childRect.x, childRect.y,
1240 0, childRect.height));
1241 }
1242
1243 // Add a target after this child (always)
1244 mDropTargetIndices.push_back(i+1);
1245 mDropTargetRows.push_back(row);
1246 mDropTargets.push_back(wxRect(childRect.x+childRect.width, childRect.y,
1247 0, childRect.height));
1248 }
1249
1250 return mDropTargets;
1251}
1252
1253void ToolBarArea::MoveChild(ExpandingToolBar *toolBar, wxRect dropTarget)
1254{
1255 int i, j;
1256
1257 for(i = 0; i < (int)mDropTargets.size(); i++) {
1258 if (dropTarget == mDropTargets[i]) {
1259 int newIndex = mDropTargetIndices[i];
1260 int newRow = mDropTargetRows[i];
1261
1262 mChildArray.insert(mChildArray.begin() + newIndex, toolBar);
1263 mRowArray.insert(mRowArray.begin() + newIndex, newRow);
1264
1265 for(j = newIndex+1; j < (int)mChildArray.size(); j++)
1266 mRowArray[j] = -1;
1267
1268 ContractRow(newRow);
1269
1270 mChildArray[newIndex]->Show();
1271
1272 for(j = newIndex; j < (int)mChildArray.size(); j++)
1273 LayoutOne(j);
1274
1275 Fit(false, true);
1276
1277 return;
1278 }
1279 }
1280}
1281
1283{
1284 mCapturedChild = child;
1285}
END_EVENT_TABLE()
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:82
struct State mState
const int kToggleButtonHeight
const int kMyTimerInterval
const wxRect kDummyRect
@ kTimerID
@ kToggleButtonID
std::vector< wxImage > ImageArray
Definition: ImageRoll.h:25
#define safenew
Definition: MemoryX.h:10
THEME_API Theme theTheme
Definition: Theme.cpp:82
void PushDown()
Definition: AButton.cpp:597
void PopUp()
Definition: AButton.cpp:605
A custom event handler for ExpandingToolBar.
ExpandingToolBarEvtHandler(ExpandingToolBar *toolbar, wxWindow *window, wxEvtHandler *inheritedEvtHandler)
bool ProcessEvent(wxEvent &evt) override
A smart ToolBar class that has a "MainPanel" which is always displayed, and an "ExtraPanel" that can ...
wxWindow * mTopLevelParent
std::unique_ptr< wxDragImage > mDragImage
void OnTimer(wxTimerEvent &evt)
void OnSize(wxSizeEvent &evt)
void RecursivelyPushEventHandlers(wxWindow *win)
void MoveDrawer(wxSize prevSize)
ToolBarArea * mAreaParent
WindowHash mWindowHash
static int msNoAutoExpandStack
std::vector< wxRect > mDropTargets
std::vector< std::unique_ptr< ExpandingToolBarEvtHandler > > mHandlers
void OnToggle(wxCommandEvent &evt)
std::unique_ptr< ToolBarArrangement > mSavedArrangement
bool Layout() override
ToolBarGrabber * mGrabber
ToolBarFrame * mFrameParent
ImageRollPanel * mTargetPanel
AButton * mToggleButton
ToolBarDialog * mDialogParent
void Fit() override
wxBitmap GetToolbarBitmap()
void Collapse(bool now=false)
An ImageRoll is an image that can be expanded to an arbitrary size; it is made up of both fixed piece...
Definition: ImageRoll.h:28
@ VerticalRoll
Definition: ImageRoll.h:34
static ImageArray SplitH(const wxImage &src, wxColour magicColor)
Definition: ImageRoll.cpp:110
A wxPanel which displays an ImageRoll.
Definition: ImageRoll.h:75
void SetLogicalFunction(int func)
Definition: ImageRoll.cpp:446
wxImage & Image(int iIndex)
An alternative to ToolBarFrame which can contain an ExpandingToolBar. ToolBarArea is used for a 'dock...
wxSize mLastLayoutSize
ExpandingToolBar * mCapturedChild
bool ExpandRow(int rowIndex)
void SetCapturedChild(ExpandingToolBar *child)
void ContractRow(int rowIndex)
std::vector< int > mDropTargetIndices
std::unique_ptr< ToolBarArrangement > SaveArrangement()
std::vector< int > mRowArray
std::vector< ExpandingToolBar * > mChildArray
std::vector< wxRect > mDropTargets
void CollapseAll(bool now=false)
std::vector< wxRect > GetDropTargets()
void OnMouse(wxMouseEvent &evt)
void AddChild(ExpandingToolBar *child)
void Fit() override
void OnSize(wxSizeEvent &evt)
std::vector< int > mDropTargetRows
void LayoutOne(int childIndex)
void MoveChild(ExpandingToolBar *child, wxRect dropTarget)
void RestoreArrangement(std::unique_ptr< ToolBarArrangement > &&arrangement)
void RemoveChild(ExpandingToolBar *child)
bool Layout() override
Small class that holds some layout information for an ExpandingToolBar.
std::vector< ExpandingToolBar * > childArray
std::vector< int > rowArray
std::vector< wxRect > rectArray
A dialog based container for ExpandingToolBars providing modal based operations.
void Fit() override
ExpandingToolBar * mChild
void SetChild(ExpandingToolBar *child)
A miniframe based container for ExpandingToolBars providing modeless presentation.
ExpandingToolBar * mChild
void SetChild(ExpandingToolBar *child)
void Fit() override
Draws the grabber for an ExpandingToolBar.
void OnPaint(wxPaintEvent &evt)
void OnSize(wxSizeEvent &evt)
void OnMouse(wxMouseEvent &evt)
ExpandingToolBar * mOwnerToolBar
Holds a msgid for the translation catalog; may also bind format arguments.