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