1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-02 23:23:53 +01:00

Tool bar configuration stores a tree structure, not a simple sequence

This commit is contained in:
Paul Licameli
2016-06-09 17:51:56 -04:00
parent 7f920ecd0d
commit 65b3d32894
3 changed files with 320 additions and 60 deletions

View File

@@ -33,6 +33,7 @@
#include <wx/intl.h> #include <wx/intl.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/tokenzr.h>
#include <wx/window.h> #include <wx/window.h>
#endif /* */ #endif /* */
@@ -54,9 +55,36 @@ const ToolBarConfiguration::Position
auto ToolBarConfiguration::FindPlace(const ToolBar *bar) const auto ToolBarConfiguration::FindPlace(const ToolBar *bar) const
-> Iterator -> Iterator
{ {
return std::find_if(begin(), end(), auto This = const_cast<ToolBarConfiguration*>(this);
[=](const Place &place){ return place.pBar == bar; } return std::find_if(This->begin(), This->end(),
[=](const Place &place){
return place.pTree->pBar == bar;
});
}
auto ToolBarConfiguration::FindParent(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 ToolBarConfiguration::Find(const ToolBar *bar) const -> Position
@@ -70,20 +98,105 @@ auto ToolBarConfiguration::Find(const ToolBar *bar) const -> Position
void ToolBarConfiguration::Insert(ToolBar *bar, Position position) void ToolBarConfiguration::Insert(ToolBar *bar, Position position)
{ {
if (position == UnspecifiedPosition) if (position == UnspecifiedPosition) {
push_back(bar); // Add at the "end" of the layout
Forest *pForest = &mForest;
while (!pForest->empty())
pForest = &pForest->back().children;
pForest->push_back( Tree {} );
pForest->back().pBar = bar;
}
else { else {
auto index = wxArrayPtrVoid::Index(position.rightOf); auto pForest = &mForest;
if (index == wxNOT_FOUND) if (position.rightOf) {
push_back(bar); const auto parent = FindPlace(position.rightOf);
if (parent != end())
pForest = &parent->pTree->children;
}
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 else
wxArrayPtrVoid::Insert(bar, 1 + index); adopt = (iter != end);
// Adopt the child only if the insertion point specifies that
if (adopt && position.adopt) {
// Make new node with one child
Tree tree;
tree.pBar = bar;
tree.children.push_back(Tree{});
// Do adoption
auto &child = tree.children.back();
child.pBar = iter->pBar;
child.children.swap(iter->children);
// Put the node in the tree
(*iter).swap(tree);
}
else
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);
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)
{
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) void ToolBarConfiguration::Remove(const ToolBar *bar)
{ {
wxArrayPtrVoid::Remove(const_cast<ToolBar*>(bar)); auto results = FindParent(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) void ToolBarConfiguration::Show(ToolBar *bar)
@@ -109,8 +222,8 @@ bool ToolBarConfiguration::IsRightmost(const ToolBar *bar) const
if (++iter == endit) if (++iter == endit)
// Last of all // Last of all
return true; return true;
if (bar->GetRect().y != iter->pBar->GetRect().y) if (bar->GetRect().y != iter->pTree->pBar->GetRect().y)
// Last in its row //
return true; return true;
return false; return false;
} }
@@ -118,7 +231,7 @@ bool ToolBarConfiguration::IsRightmost(const ToolBar *bar) const
bool ToolBarConfiguration::Read bool ToolBarConfiguration::Read
(ToolBarConfiguration *pConfiguration, (ToolBarConfiguration *pConfiguration,
ToolManager *pManager, ToolManager *pManager,
Legacy *, Legacy *pLegacy,
ToolBar *bar, bool &visible, bool defaultVisible) ToolBar *bar, bool &visible, bool defaultVisible)
{ {
bool result = true; bool result = true;
@@ -132,11 +245,27 @@ bool ToolBarConfiguration::Read
result = false; result = false;
else if (ord >= 0) else if (ord >= 0)
{ {
while(pConfiguration->size () <= ord) // Legacy preferences
pConfiguration->push_back(nullptr); while (pLegacy->bars.size() <= ord)
(*pConfiguration)[ord] = bar; 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);
}
} }
} }
// Future: might remember visibility in the configuration, not forgetting // Future: might remember visibility in the configuration, not forgetting
// positions of hidden bars. // positions of hidden bars.
gPrefs->Read( wxT("Show"), &visible, defaultVisible); gPrefs->Read( wxT("Show"), &visible, defaultVisible);
@@ -144,21 +273,53 @@ bool ToolBarConfiguration::Read
return result; return result;
} }
void ToolBarConfiguration::PostRead(Legacy &) void ToolBarConfiguration::RemoveNulls(Forest &forest)
{ {
auto b = wxArrayPtrVoid::begin(); for (int ii = 0; ii < forest.size(); ++ii) {
auto iter = if(forest[ii].pBar == nullptr)
std::remove(b, wxArrayPtrVoid::end(), nullptr); Remove(forest, forest.begin() + ii--);
resize(iter - b); }
// 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);
ToolBar *prev {};
for (auto pBar : legacy.bars) {
if (!pBar)
continue;
Position position{ prev };
Insert(pBar, position);
prev = pBar;
}
} }
void ToolBarConfiguration::Write void ToolBarConfiguration::Write
(const ToolBarConfiguration *pConfiguration, const ToolBar *bar) (const ToolBarConfiguration *pConfiguration, const ToolBar *bar)
{ {
if (pConfiguration) { if (pConfiguration) {
auto index = pConfiguration->Index(const_cast<ToolBar*>(bar)); wxString strPath;
if (index != wxNOT_FOUND) const auto cIter = pConfiguration->FindPlace(bar);
gPrefs->Write( wxT("Order"), 1 + index ); 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.
gPrefs->DeleteEntry(wxT("Order"));
} }
gPrefs->Write( wxT("Show"), bar->IsVisible() ); gPrefs->Write( wxT("Show"), bar->IsVisible() );
} }
@@ -251,7 +412,7 @@ void ToolDock::LoadConfig(ToolBar *bars[])
{ {
// Add all ordered toolbars // Add all ordered toolbars
for(const auto &place : GetConfiguration()) { for(const auto &place : GetConfiguration()) {
auto bar = place.pBar; auto bar = place.pTree->pBar;
this->Dock(bar, false); this->Dock(bar, false);
// Show it -- hidden bars are not (yet) ever saved as part of a // Show it -- hidden bars are not (yet) ever saved as part of a
// configuration // configuration
@@ -286,7 +447,7 @@ void ToolDock::LayoutToolBars()
for (const auto &place : GetConfiguration()) for (const auto &place : GetConfiguration())
{ {
// Cache toolbar pointer // Cache toolbar pointer
ToolBar *ct = place.pBar; ToolBar *ct = place.pTree->pBar;
// Get and cache the toolbar sizes // Get and cache the toolbar sizes
wxSize sz = ct->GetSize(); wxSize sz = ct->GetSize();
@@ -402,7 +563,7 @@ ToolBarConfiguration::Position
else else
{ {
// Cache toolbar pointer // Cache toolbar pointer
ToolBar *ct = iter->pBar; ToolBar *ct = iter->pTree->pBar;
// Remember current bars ' dimensions // Remember current bars ' dimensions
sz = ct->GetSize(); sz = ct->GetSize();
@@ -583,7 +744,7 @@ void ToolDock::OnPaint( wxPaintEvent & WXUNUSED(event) )
// Draw the gap between each bar // Draw the gap between each bar
for (const auto &place : GetConfiguration()) for (const auto &place : GetConfiguration())
{ {
auto toolbar = place.pBar; auto toolbar = place.pTree->pBar;
if (!toolbar) if (!toolbar)
continue; continue;

View File

@@ -13,6 +13,8 @@
#ifndef __AUDACITY_TOOLDOCK__ #ifndef __AUDACITY_TOOLDOCK__
#define __AUDACITY_TOOLDOCK__ #define __AUDACITY_TOOLDOCK__
#include <vector>
#include "../MemoryX.h" // for std::move
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/panel.h> #include <wx/panel.h>
@@ -45,24 +47,32 @@ enum
DockCount = 2 DockCount = 2
}; };
class ToolBarConfiguration : public wxArrayPtrVoid class ToolBarConfiguration
{ {
struct Tree;
using Forest = std::vector<Tree>;
public: public:
void Swap(ToolBarConfiguration &that)
{
mForest.swap(that.mForest);
}
struct Position { struct Position {
ToolBar *rightOf {}; ToolBar *rightOf {};
// ToolBar *below {}; ToolBar *below {};
// bool adopt {true}; bool adopt {true};
bool valid {true}; bool valid {true};
// Default constructor // Default constructor
Position() {} Position() {}
Position( Position(
ToolBar *r /*, ToolBar *r,
ToolBar *b = nullptr, ToolBar *b = nullptr,
bool shouldAdopt = true */ bool shouldAdopt = true
) )
: rightOf{ r } // , below{ b }, adopt{ shouldAdopt } : rightOf{ r }, below{ b }, adopt{ shouldAdopt }
{} {}
// Constructor for the invalid value // Constructor for the invalid value
@@ -73,8 +83,8 @@ public:
{ return lhs.valid == rhs.valid && { return lhs.valid == rhs.valid &&
(!lhs.valid || (!lhs.valid ||
(lhs.rightOf == rhs.rightOf (lhs.rightOf == rhs.rightOf
// && lhs.below == rhs.below && lhs.below == rhs.below
// && lhs.adopt == rhs.adopt && lhs.adopt == rhs.adopt
)); ));
} }
@@ -86,10 +96,12 @@ public:
static const Position UnspecifiedPosition; static const Position UnspecifiedPosition;
struct Place { struct Place {
ToolBar *pBar {}; Tree *pTree {};
Position position; Position position;
}; };
// This iterator visits the nodes of the forest in pre-order, and at each
// stop, makes the parent, previous sibling, and children accessible.
class Iterator class Iterator
: public std::iterator<std::forward_iterator_tag, Place> : public std::iterator<std::forward_iterator_tag, Place>
{ {
@@ -98,24 +110,67 @@ public:
const Place *operator -> () const { return &**this; } const Place *operator -> () const { return &**this; }
Iterator &operator ++ () Iterator &operator ++ ()
{ {
wxASSERT(mIter != mEnd);
// This is a feature: advance position even at the end // This is a feature: advance position even at the end
mPlace.position.rightOf = mPlace.pBar; mPlace.position =
// mPlace.position.below = nullptr; { mPlace.pTree ? mPlace.pTree->pBar : nullptr };
if (!mIters.empty())
{
auto triple = &mIters.back();
auto &children = triple->current->children;
if (children.empty()) {
while (++triple->current == triple->end) {
mIters.pop_back();
if (mIters.empty())
break;
triple = &mIters.back();
}
}
else {
auto b = children.begin();
mIters.push_back( Triple { b, b, children.end() } );
}
}
if (mIters.empty()) {
mPlace.pTree = nullptr;
// Leave mPlace.position as above
}
else {
const auto &triple = mIters.back();
mPlace.pTree = &*triple.current;
if (mIters.size() == 1)
mPlace.position.rightOf = nullptr;
else
mPlace.position.rightOf = (mIters.rbegin() + 1)->current->pBar;
if (triple.begin == triple.current)
mPlace.position.below = nullptr;
else
mPlace.position.below = (triple.current - 1)->pBar;
}
++mIter;
if (mIter != mEnd)
mPlace.pBar = static_cast<ToolBar*>(*mIter);
else
mPlace.pBar = nullptr;
return *this; return *this;
} }
// This may be called on the end iterator, and then returns empty
std::vector<int> GetPath() const
{
std::vector<int> path;
path.reserve(mIters.size());
for (const auto &triple : mIters)
path.push_back(triple.current - triple.begin);
return std::move(path);
}
friend inline bool operator == friend inline bool operator ==
(const Iterator &lhs, const Iterator &rhs) (const Iterator &lhs, const Iterator &rhs)
{ {
return lhs.mIter == rhs.mIter; const auto &li = lhs.mIters;
const auto &ri = rhs.mIters;
return li.size() == ri.size() &&
std::equal(li.begin(), li.end(), ri.begin());
} }
friend inline bool operator != friend inline bool operator !=
@@ -126,23 +181,42 @@ public:
private: private:
friend ToolBarConfiguration; friend ToolBarConfiguration;
using iterator = wxArrayPtrVoid::const_iterator; Iterator () {}
explicit Iterator(iterator iter, iterator end) explicit Iterator(ToolBarConfiguration &conf)
: mIter(iter)
, mEnd(end)
{ {
if (mIter != mEnd) auto &forest = conf.mForest;
mPlace.pBar = static_cast<ToolBar*>(*mIter); if (!forest.empty()) {
auto b = forest.begin();
mIters.push_back( Triple { b, b, forest.end() } );
mPlace.pTree = &*b;
}
} }
iterator mIter, mEnd;
Place mPlace; Place mPlace;
using FIter = Forest::iterator;
struct Triple
{
Triple (FIter b, FIter c, FIter e)
: begin{b}, current{c}, end{e} {}
FIter begin, current, end;
friend inline bool operator ==
(const Triple &lhs, const Triple &rhs)
{
// Really need only to compare current
return
// lhs.begin == rhs.begin &&
lhs.current == rhs.current
// lhs.end == rhs.end
;
}
};
std::vector<Triple> mIters;
}; };
Iterator begin() const Iterator begin() { return Iterator { *this }; }
{ return Iterator { wxArrayPtrVoid::begin(), wxArrayPtrVoid::end() }; } Iterator end() const { return Iterator {}; }
Iterator end() const
{ return Iterator { wxArrayPtrVoid::end(), wxArrayPtrVoid::end() }; }
Position Find(const ToolBar *bar) const; Position Find(const ToolBar *bar) const;
@@ -154,6 +228,7 @@ public:
// Default position inserts at the end // Default position inserts at the end
void Insert(ToolBar *bar, void Insert(ToolBar *bar,
Position position = UnspecifiedPosition); Position position = UnspecifiedPosition);
void InsertAtPath(ToolBar *bar, const std::vector<int> &path);
void Remove(const ToolBar *bar); void Remove(const ToolBar *bar);
// Future: might allow a state that the configuration remembers // Future: might allow a state that the configuration remembers
@@ -166,6 +241,7 @@ public:
bool IsRightmost(const ToolBar *bar) const; bool IsRightmost(const ToolBar *bar) const;
struct Legacy { struct Legacy {
std::vector<ToolBar*> bars;
}; };
static bool Read static bool Read
@@ -179,7 +255,26 @@ public:
(const ToolBarConfiguration *pConfiguration, const ToolBar *bar); (const ToolBarConfiguration *pConfiguration, const ToolBar *bar);
private: private:
void Remove(Forest &forest, Forest::iterator iter);
void RemoveNulls(Forest &forest);
struct Tree
{
ToolBar *pBar {};
Forest children;
void swap(Tree &that)
{
std::swap(pBar, that.pBar);
children.swap(that.children);
}
};
Iterator FindPlace(const ToolBar *bar) const; Iterator FindPlace(const ToolBar *bar) const;
std::pair<Forest*, Forest::iterator> FindParent(const ToolBar *bar);
Forest mForest;
}; };
class ToolDock final : public wxPanel class ToolDock final : public wxPanel

View File

@@ -862,13 +862,17 @@ void ToolManager::WriteConfig()
gPrefs->Write( wxT("W"), sz.x ); gPrefs->Write( wxT("W"), sz.x );
gPrefs->Write( wxT("H"), sz.y ); gPrefs->Write( wxT("H"), sz.y );
// Kill the bar
bar->Destroy();
// Change back to the bar root // Change back to the bar root
gPrefs->SetPath( wxT("..") ); gPrefs->SetPath( wxT("..") );
} }
// Kill the bars
for( ndx = 0; ndx < ToolBarCount; ndx++ )
{
ToolBar *bar = mBars[ ndx ];
bar->Destroy();
}
// Restore original config path // Restore original config path
gPrefs->SetPath( oldpath ); gPrefs->SetPath( oldpath );
gPrefs->Flush(); gPrefs->Flush();