forked from audacity/audacity
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathToolDock.cpp
More file actions
949 lines (819 loc) · 26 KB
/
Copy pathToolDock.cpp
File metadata and controls
949 lines (819 loc) · 26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
/**********************************************************************
Audacity: A Digital Audio Editor
ToolDock.cpp
Dominic Mazzoni
Shane T. Mueller
Leland Lucius
*******************************************************************//**
\file ToolDock.cpp
Implements ToolDock
*//*******************************************************************//**
\class ToolDock
\brief A dynamic panel where a ToolBar can be docked.
*//**********************************************************************/
#include "../Audacity.h"
#include "ToolDock.h"
#include <wx/tokenzr.h>
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/dcclient.h>
#include <wx/defs.h>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <wx/intl.h>
#include <wx/panel.h>
#include <wx/settings.h>
#include <wx/window.h>
#endif /* */
#include <algorithm>
#include "../AColor.h"
#include "../AllThemeResources.h"
#include "../ImageManipulation.h"
#include "../Prefs.h"
#include "../widgets/Grabber.h"
const ToolBarConfiguration::Position
ToolBarConfiguration::UnspecifiedPosition { false };
auto ToolBarConfiguration::FindPlace(const ToolBar *bar) const
-> Iterator
{
auto This = const_cast<ToolBarConfiguration*>(this);
return std::find_if(This->begin(), This->end(),
[=](const Place &place){
return place.pTree->pBar == bar;
});
}
auto ToolBarConfiguration::FindPeers(const ToolBar *bar)
-> std::pair<Forest*, Forest::iterator>
{
auto findTree = [=](Forest &forest){
return std::find_if(forest.begin(), forest.end(),
[=](const Tree &tree){ return tree.pBar == bar; });
};
auto iter1 = findTree(mForest);
if (iter1 != mForest.end())
return { &mForest, iter1 };
Forest::iterator result;
auto iter = std::find_if(begin(), end(),
[&](const Place &place){
auto &children = place.pTree->children;
return (result = findTree(children)) != children.end();
}
);
if (iter != end())
return { &iter->pTree->children, result };
return { nullptr, Forest::iterator{} };
}
auto ToolBarConfiguration::Find(const ToolBar *bar) const -> Position
{
auto iter = FindPlace(bar);
if (iter == end())
return UnspecifiedPosition;
else
return iter->position;
}
void ToolBarConfiguration::Insert(ToolBar *bar, Position position)
{
if (position == UnspecifiedPosition) {
// Add at the "end" of the layout
// bottommost and rightmost
Forest *pForest = &mForest;
while (!pForest->empty())
pForest = &pForest->back().children;
pForest->push_back( Tree {} );
pForest->back().pBar = bar;
}
else {
// Insert at what depth?
auto pForest = &mForest;
if (position.rightOf) {
const auto parent = FindPlace(position.rightOf);
if (parent != end())
// Insert among children of some node
pForest = &parent->pTree->children;
}
else {
// Insert a new root in the top forest
}
// Insert at what breadth?
const auto begin = pForest->begin();
auto iter = begin;
const auto end = pForest->end();
bool adopt = false;
if (position.below) {
iter = std::find_if(begin, end,
[=](const Tree &tree){ return tree.pBar == position.below; }
);
if (iter != end) {
++iter;
if (iter != end)
adopt = true;
}
else
// Not found, default to topmost
iter = begin;
}
else
// No previous sibling specified, so insert as first
adopt = (iter != end);
// Insert as a leaf, or as an internal node?
if (adopt && position.adopt) {
// Existing children of parent become grandchildren
// Make NEW node
Tree tree;
tree.pBar = bar;
// Do adoption
const auto barHeight = bar->GetSize().GetY() + toolbarGap;
auto totalHeight = 0;
while (iter != pForest->end() &&
barHeight >=
(totalHeight += (iter->pBar->GetSize().GetY() + toolbarGap))) {
tree.children.push_back(Tree{});
auto &child = tree.children.back();
child.pBar = iter->pBar;
child.children.swap(iter->children);
iter = pForest->erase(iter);
}
// Put the node in the tree
iter = pForest->insert(iter, Tree{});
(*iter).swap(tree);
}
else
// Insert as a leaf
pForest->insert(iter, Tree {})->pBar = bar;
}
}
void ToolBarConfiguration::InsertAtPath
(ToolBar *bar, const std::vector<int> &path)
{
auto pForest = &mForest;
Tree *pTree {};
// Guarantee the existence of nodes
for (auto ii : path) {
Forest::size_type uu = std::max(0, ii);
// This may make more than one default-constructed tree, which we
// will fill in with some other call to InsertAtPath, or else cleanup
// with RemoveNulls
pForest->resize(std::max(uu + 1, pForest->size()));
pTree = &(*pForest)[uu];
pForest = &pTree->children;
}
if (pTree)
pTree->pBar = bar;
}
void ToolBarConfiguration::Remove(Forest &forest, Forest::iterator iter)
{
// Reparent all of the children of the deleted node
Tree tree;
tree.swap(*iter);
iter = forest.erase(iter);
auto &children = tree.children;
auto cIter = children.rbegin(), cEnd = children.rend();
while (cIter != cEnd) {
iter = forest.insert(iter, Tree{});
(*iter).swap(*cIter);
++cIter;
}
}
void ToolBarConfiguration::Remove(const ToolBar *bar)
{
auto results = FindPeers(bar);
auto pForest = results.first;
if (pForest) {
// Reparent all of the children of the deleted node
auto iter = results.second;
wxASSERT(iter->pBar == bar);
Remove(*pForest, iter);
}
}
void ToolBarConfiguration::Show(ToolBar *bar)
{
// Do not assume the bar is absent, though in practice that is always so
if (!Contains(bar))
Insert(bar);
}
void ToolBarConfiguration::Hide(ToolBar *bar)
{
// Future: might hide a bar without eliminating it from the configuration
Remove(bar);
}
bool ToolBarConfiguration::IsRightmost(const ToolBar *bar) const
{
auto iter = FindPlace(bar);
auto endit = end();
if (iter == endit)
// not present
return true;
if (++iter == endit)
// Last of all
return true;
if (bar->GetRect().y != iter->pTree->pBar->GetRect().y)
// Next step in preorder traversal is not rightward to a child drawn at
// the same height
return true;
return false;
}
bool ToolBarConfiguration::Read
(ToolBarConfiguration *pConfiguration,
Legacy *pLegacy,
ToolBar *bar, bool &visible, bool defaultVisible)
{
bool result = true;
// Future: might remember visibility in the configuration, not forgetting
// positions of hidden bars.
gPrefs->Read( wxT("Show"), &visible, defaultVisible);
if (pConfiguration && visible) {
int ord;
gPrefs->Read( wxT("Order"), &ord, -1 );
// Index was written 1-based
--ord;
if (ord >= ToolBarCount)
result = false;
else if (ord >= 0)
{
// Legacy preferences
while (pLegacy->bars.size() <= size_t(ord))
pLegacy->bars.push_back(nullptr);
pLegacy->bars[ord] = bar;
}
else {
wxString strPath;
gPrefs->Read( wxT("Path"), &strPath );
if (!strPath.empty()) {
wxStringTokenizer toker { strPath, wxT(",") };
std::vector<int> path;
while(toker.HasMoreTokens()) {
auto token = toker.GetNextToken();
auto ii = wxAtoi(token);
path.push_back(ii);
}
pConfiguration->InsertAtPath(bar, path);
}
}
}
return result;
}
void ToolBarConfiguration::RemoveNulls(Forest &forest)
{
for (size_t ii = 0; ii < forest.size(); ++ii) {
if(forest[ii].pBar == nullptr)
Remove(forest, forest.begin() + ii--);
}
// Now do the same recursively
for (auto &tree : forest)
RemoveNulls(tree.children);
}
void ToolBarConfiguration::PostRead(Legacy &legacy)
{
// Be sure no nodes contain NULL,
// against the case of obsolete preferences, perhaps
RemoveNulls(mForest);
// Interpret what was saved in old .cfg files under "order"
// which specified toolbar configuration simply as a sequence, not a tree
ToolBar *prev {};
for (auto pBar : legacy.bars) {
if (!pBar)
continue;
Position position{ prev };
Insert(pBar, position);
prev = pBar;
}
}
void ToolBarConfiguration::Write
(const ToolBarConfiguration *pConfiguration, const ToolBar *bar)
{
// Assume a path has been set in gPrefs suitable for bar
if (pConfiguration) {
// Write comma-separated list of numbers specifying position in the tree
wxString strPath;
const auto cIter = pConfiguration->FindPlace(bar);
const auto path = cIter.GetPath();
if (!path.empty()) {
auto iter = path.begin(), end = path.end();
strPath += wxString::Format(wxT("%d"), *iter++);
while (iter != end)
strPath += wxString::Format(wxT(",%d"), *iter++);
}
gPrefs->Write(wxT("Path"), strPath);
// Remove any legacy configuration info.
// Note: this causes Audacity 2.1.2 and earlier to create toolbars
// always in default position when reading a .cfg saved by Audacity
// 2.1.3 or later
gPrefs->DeleteEntry(wxT("Order"));
}
gPrefs->Write( wxT("Show"), bar->IsVisible() );
}
IMPLEMENT_CLASS( ToolDock, wxPanelWrapper );
////////////////////////////////////////////////////////////
/// Methods for ToolDock
////////////////////////////////////////////////////////////
//
// Custom event
//
//DEFINE_EVENT_TYPE( EVT_TOOLBAR_FLOAT );
BEGIN_EVENT_TABLE( ToolDock, wxPanelWrapper )
EVT_GRABBER( wxID_ANY, ToolDock::OnGrabber )
EVT_ERASE_BACKGROUND( ToolDock::OnErase )
EVT_PAINT( ToolDock::OnPaint )
EVT_SIZE( ToolDock::OnSize )
EVT_MOUSE_EVENTS( ToolDock::OnMouseEvents )
END_EVENT_TABLE()
//
// Constructor
//
ToolDock::ToolDock( wxEvtHandler *manager, wxWindow *parent, int dockid ):
wxPanelWrapper( parent, dockid, wxDefaultPosition, parent->GetSize() )
{
SetLabel( XO( "ToolDock" ) );
SetName( XO( "ToolDock" ) );
// Init
mManager = manager;
memset(mBars, 0, sizeof(mBars)); // otherwise uninitialized
SetBackgroundColour(theTheme.Colour( clrMedium ));
SetLayoutDirection(wxLayout_LeftToRight);
// Use for testing gaps
// SetOwnBackgroundColour( wxColour( 255, 0, 0 ) );
}
//
// Destructor
//
ToolDock::~ToolDock()
{
}
//
// Remove the toolbar from our control
//
void ToolDock::Undock( ToolBar *bar )
{
if( mConfiguration.Contains( bar ) )
{
mConfiguration.Remove( bar );
}
mBars[ bar->GetId() ] = nullptr;
}
//
// Handle ToolDock events
//
void ToolDock::Dock( ToolBar *bar, bool deflate, ToolBarConfiguration::Position position )
{
#ifndef __WXMAC__
// Apply the deflate fix only on Mac, else you introduce the opposite bug on others
deflate = false;
#endif
// Adopt the toolbar into our family
bar->Reparent( this );
mBars[ bar->GetId() ] = bar;
// Reset size
bar->SetSize(
// Undo the expansion that was applied when un-docking
bar->GetSize().x - (deflate ? 2 * ToolBarFloatMargin : 0),
// Don't need to adjust y the same way.
bar->GetDockedSize().y
);
// Park the NEW bar in the correct berth
if (!mConfiguration.Contains(bar) && bar->IsVisible())
mConfiguration.Insert( bar, position );
// Inform toolbar of change
bar->SetDocked( this, false );
}
// Initial docking of bars
void ToolDock::LoadConfig()
{
// Add all ordered toolbars
for(const auto &place : GetConfiguration()) {
auto bar = place.pTree->pBar;
this->Dock(bar, false);
// Show it -- hidden bars are not (yet) ever saved as part of a
// configuration
Expose( bar->GetId(), true );
}
Updated();
}
// A policy object for the skeleton routine below
class ToolDock::LayoutVisitor
{
public:
virtual void ModifySize
(ToolBar *,
const wxRect &,
ToolBarConfiguration::Position,
ToolBarConfiguration::Position,
wxSize &)
{
}
virtual void Visit
(ToolBar *ct, wxPoint point) = 0;
virtual bool ShouldVisitSpaces() = 0;
virtual void FinalRect
(const wxRect &, ToolBarConfiguration::Position)
{
}
};
// Skeleton routine common to insertion of a toolbar, and figuring out
// width-constrained layout of toolbars
void ToolDock::VisitLayout(LayoutVisitor &visitor,
ToolBarConfiguration *pWrappedConfiguration)
{
if (pWrappedConfiguration)
pWrappedConfiguration->Clear();
// Get size of our parent since we haven't been sized yet
int width, height;
GetParent()->GetClientSize( &width, &height );
width -= toolbarGap;
height -= toolbarGap;
// Rectangle of space to allocate
wxRect main{ toolbarGap, toolbarGap,
// Allow limited width, but arbitrary height, for the root rectangle
width, std::numeric_limits<int>::max() };
// For recording the nested subdivisions of the rectangle
struct Item {
int myBarID { NoBarID };
int parentBarID { NoBarID };
ToolBar *lastSib {};
ToolBar *lastWrappedChild {};
wxRect rect;
} layout[ ToolBarCount ];
ToolBar *lastRoot {};
ToolBar *lastWrappedRoot {};
// Process all docked and visible toolbars
for ( const auto &place : this->GetConfiguration() )
{
// Cache toolbar pointer
const auto ct = place.pTree->pBar;
// set up the chain of ancestors.
const auto parent = place.position.rightOf;
const auto type = ct->GetType();
auto &newItem = layout[ type ];
newItem.parentBarID = parent ? parent->GetType() : NoBarID;
// Mark the slots that really were visited, for final pass through
// the spaces.
newItem.myBarID = type;
const auto parentItem = parent ? &layout[ parent->GetType() ] : nullptr;
ToolBar *prevSib;
if (!parent) {
prevSib = lastRoot;
lastRoot = ct;
}
else {
auto &sib = parentItem->lastSib;
prevSib = sib;
sib = ct;
}
auto prevPosition = ToolBarConfiguration::Position{ parent, prevSib };
// Determine the size of the toolbar to fit, with advice from
// the visitor object
wxSize sz = ct->GetSize();
{
wxRect temp;
temp.SetPosition(ct->GetParent()->ClientToScreen(ct->GetPosition()));
temp.SetSize(sz);
visitor.ModifySize(ct, temp, prevPosition, place.position, sz);
}
// Inflate the size to leave margins
int tw = sz.GetWidth() + toolbarGap;
int th = sz.GetHeight() + toolbarGap;
// Choose the rectangle to subdivide
// Find a box that we fit in by going up the tree as needed --
// thus when parent space is exhausted, fall back on ancestors --
// so if the tree has too much depth for the width of the
// window, the toolbars may "wrap."
// Can always fall back to the main rectangle even if the bar is too
// wide.
auto pItem = parentItem;
auto pRect = pItem ? &pItem->rect : &main;
while (pRect != &main)
{
// Get out if it will fit
bool bTooWide = tw > pRect->GetWidth();
// We'd like to be able to add a tall toolbar in at the start of a row,
// even if there isn't enough height for it.
// If so, we'd have to at least change how we calculate 'bTooHigh'.
bool bTooHigh = th > pRect->GetHeight();
//bTooHigh &= stack[stkcnt].GetWidth() < (width - toolbarGap);
//bTooHigh = false;
if (!bTooWide && !bTooHigh)
break;
if (pItem->parentBarID == NoBarID) {
pItem = nullptr;
pRect = &main;
}
else {
pItem = &layout[ pItem->parentBarID ];
pRect = &pItem->rect;
}
}
// Record where the toolbar wrapped
ToolBar *& sib = pItem ? pItem->lastWrappedChild : lastWrappedRoot;
ToolBarConfiguration::Position newPosition {
pItem ? this->mBars[ pItem->myBarID ] : nullptr,
sib
};
sib = ct;
if (pWrappedConfiguration)
pWrappedConfiguration->Insert(ct, newPosition);
// Place the toolbar at the upper left part of the rectangle.
const auto cpos = pRect->GetPosition();
visitor.Visit(ct, cpos);
// Allocate an upper portion of the rectangle to this bar.
pRect->y += th;
pRect->height -= th;
// A right portion of that upper portion remains available for
// descendant bars and is remembered in the layout array.
int x = cpos.x + tw;
newItem.rect = wxRect{ x, cpos.y, width - x, th };
}
if (visitor.ShouldVisitSpaces()) {
// Visit the fringe where NEW leaves of the tree could go
// Find the items with leftover spaces
const auto end = std::remove_if(layout, layout + ToolBarCount,
[](const Item &item){
return item.myBarID == NoBarID || item.rect.IsEmpty();
}
);
// Sort top to bottom for definiteness, though perhaps not really needed
std::sort(layout, end,
[](const Item &lhs, const Item &rhs){
return lhs.rect.y < rhs.rect.y;
}
);
for (auto iter = layout; iter != end; ++iter) {
const auto &item = *iter;
const auto &rect = item.rect;
auto globalRect = rect;
globalRect.SetPosition( this->ClientToScreen(rect.GetPosition()) );
// Let the visitor determine size
wxSize sz {};
ToolBarConfiguration::Position
position { this->mBars[ item.myBarID ], item.lastWrappedChild },
prevPosition {};
visitor.ModifySize(nullptr, globalRect, prevPosition, position, sz);
int tw = sz.GetWidth() + toolbarGap;
int th = sz.GetHeight() + toolbarGap;
// Test fit
bool bTooWide = tw > rect.GetWidth();
bool bTooHigh = th > rect.GetHeight();
if (!bTooWide && !bTooHigh) {
// Call visitor again to confirm the placement
const auto cpos = rect.GetPosition();
visitor.Visit(nullptr, cpos);
}
}
}
// Report the final bounding box of all the bars, and a position where
// you can insert a NEW bar at bottom left.
ToolBarConfiguration::Position finalPosition { nullptr, lastRoot };
visitor.FinalRect(
wxRect { toolbarGap, toolbarGap, main.width, main.y }, finalPosition
);
}
//
// Layout the toolbars
//
void ToolDock::LayoutToolBars()
{
struct SizeSetter final : public LayoutVisitor
{
SizeSetter (ToolDock *d) : dock{ d } {}
void Visit
(ToolBar *bar, wxPoint point)
override
{
// Place the toolbar
if(bar)
bar->SetPosition( point );
}
bool ShouldVisitSpaces() override
{
return false;
}
virtual void FinalRect
(const wxRect &rect, ToolBarConfiguration::Position)
override
{
// Set the final size of the dock window
dock->SetMinSize( rect.GetSize() );
}
ToolDock *dock;
} sizeSetter {
this
};
VisitLayout(sizeSetter, &mWrappedConfiguration);
// Set tab order and layout internal controls.
{
ToolBar *lt{};
for ( const auto &place : GetConfiguration() ) {
auto ct = place.pTree->pBar;
if( lt ){
ct->MoveAfterInTabOrder( lt );
}
lt = ct;
// Bug 1371.
// After a dock size change, the toolbars may need relaying inside.
lt->Layout();
}
}
// Clean things up
Refresh( false );
}
// Determine the position where a NEW bar would be placed
//
// 'rect' will be the rectangle for the dock marker (black triangle)
ToolBarConfiguration::Position
ToolDock::PositionBar( ToolBar *t, const wxPoint & pos, wxRect & rect )
{
// Set width and size, but we must still find x and y.
rect = t->GetRect();
using Position = ToolBarConfiguration::Position;
Position result { ToolBarConfiguration::UnspecifiedPosition };
struct Inserter : public LayoutVisitor
{
struct Stop {};
Inserter(Position &p, wxRect &r, const wxPoint &pt, ToolBar *t)
: result(p), rect(r), point(pt), tb(t)
{}
void ModifySize
(ToolBar *ct,
const wxRect &rectIn,
ToolBarConfiguration::Position prevPosition,
ToolBarConfiguration::Position position,
wxSize &sz)
override
{
// Maybe insert the NEW bar if it hasn't already been done
// and is in the right place.
// Does the location fall within this bar?
if (rectIn.Contains(point))
{
sz = tb->GetDockedSize();
// Choose a position always, if there is a bar to displace.
// Else, only if the fit is possible.
if (ct || (sz.x <= rectIn.width && sz.y <= rectIn.height)) {
// May choose current or previous.
if (ct &&
(sz.y < rectIn.height ||
point.y < (rectIn.GetTop() + rectIn.GetBottom()) / 2))
// "Wedge" the bar into a crack alone, not adopting others,
// if either a short bar displaces a tall one, or else
// the displacing bar is at least at tall, but the pointer is
// in the upper half of the box.
usedPrev = true, result = prevPosition, result.adopt = false;
else
result = position;
}
// Now wait until the other callback below to discover x and y
}
}
void Visit
(ToolBar *, wxPoint pointIn)
override
{
if (result != ToolBarConfiguration::UnspecifiedPosition) {
// If we've placed it, we're done.
rect.x = pointIn.x;
rect.y = pointIn.y;
if (usedPrev)
rect.y -= tb->GetDockedSize().GetHeight() / 2;
throw Stop {};
}
}
bool ShouldVisitSpaces() override
{
return true;
}
void FinalRect
(const wxRect &finalRect, ToolBarConfiguration::Position finalPosition)
override
{
if (result == ToolBarConfiguration::UnspecifiedPosition) {
// Default of all other placements.
result = finalPosition;
wxPoint point1 { finalRect.GetLeft(), finalRect.GetBottom() };
rect.SetPosition(point1);
}
}
Position &result;
wxRect ▭
const wxPoint point;
ToolBar *const tb;
bool usedPrev { false };
} inserter {
result, rect, pos, t
};
try { this->VisitLayout(inserter); } catch (const Inserter::Stop&) {}
// rect is decided
return result;
}
void ToolDock::WrapConfiguration(ToolBarConfiguration &backup)
{
backup.Clear();
backup.Swap(mConfiguration);
mConfiguration.Swap(mWrappedConfiguration);
}
void ToolDock::RestoreConfiguration(ToolBarConfiguration &backup)
{
mWrappedConfiguration.Clear();
mWrappedConfiguration.Swap(mConfiguration);
mConfiguration.Swap(backup);
}
//
// Set the visible/hidden state of a toolbar
//
void ToolDock::Expose( int type, bool show )
{
ToolBar *t = mBars[ type ];
// Maintain the docked array
const auto shown = mConfiguration.Shows( t );
if( show && !shown )
mConfiguration.Show( t );
else if( !show && shown )
mConfiguration.Hide( t );
// Make it (dis)appear
t->Expose( show );
}
//
// Queues an EVT_TOOLBAR_UPDATED command event to notify any
// interested parties of an updated toolbar or dock layout
//
void ToolDock::Updated()
{
// Queue an update event
wxCommandEvent e( EVT_TOOLBAR_UPDATED, GetId() );
GetParent()->GetEventHandler()->AddPendingEvent( e );
}
//
// Handle grabber clicking
//
void ToolDock::OnGrabber( GrabberEvent & event )
{
// auto pos = event.GetPosition();
if (!event.IsEscaping()) {
// Pass it on to the manager since it isn't in the handling hierarchy
mManager->ProcessEvent( event );
}
}
//
// Handle sizing
//
void ToolDock::OnSize( wxSizeEvent & WXUNUSED(event) )
{
// event.Skip();
}
//
// Prevent flicker
//
void ToolDock::OnErase( wxEraseEvent & WXUNUSED(event) )
{
// Ignore it to prevent flashing
}
//
// Repaint toolbar gap lines
//
void ToolDock::OnPaint( wxPaintEvent & WXUNUSED(event) )
{
// Don't use a wxBufferedPaintDC() here. It produces a bogus
// background on Windows and GTK.
wxPaintDC dc( this );
// Start with a clean background
//
// Under GTK, we don't set the toolbar background to the background
// colour in the system theme. Instead we use our own colour.
dc.SetBackground( wxBrush( theTheme.Colour( clrMedium )));
dc.Clear();
// Set the gap color
AColor::Dark( &dc, false );
// Draw the initial horizontal and vertical gaps
wxSize sz = GetClientSize();
AColor::Line(dc, 0, 0, sz.GetWidth(), 0 );
AColor::Line(dc, 0, 0, 0, sz.GetHeight() );
// Draw the gap between each bar
for (const auto &place : GetConfiguration())
{
auto toolbar = place.pTree->pBar;
if (!toolbar)
continue;
wxRect r = toolbar->GetRect();
// Draw a horizontal line under the bar extending to the right edge of
// the dock
AColor::Line( dc,
r.GetLeft(),
r.GetBottom() + 1,
sz.GetWidth(),
r.GetBottom() + 1 );
// For all bars but the last...
// ...and for bars that aren't the last in a row, draw a
// vertical gap line
if (!mConfiguration.IsRightmost(toolbar)) {
AColor::Line(dc,
r.GetRight() + 1,
r.GetTop(),
r.GetRight() + 1,
r.GetBottom() + 1 );
}
}
}
void ToolDock::OnMouseEvents(wxMouseEvent &event)
{
// Do this hack so scrubber can detect mouse drags anywhere
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
event.Skip();
}