================================================================ https://github.com/FreeCAD/FreeCAD/issues/9208#issuecomment-1567469254 Issue: - Unavailable toolbars default to not visible (as they are unavailable) - However, when made available, they remain not visible. There may be cases when this is indeed the wanted behaviour, but they are not the ones we are facing. - In the latter, the user expects the toolbars to show (and the configuration file to be saved with all toolbars showing. Solution: - Change the default behaviour of Unavailable toolbars when forcing them to be available, so that they are visible when made available. - If then the case arises that we have toolbars where we would prefer then to be left not visible when making them available (so they are listed in the toolbar context menu to make them visible, but not visible per default), then we would need to create another toolbar policy class "UnavailableHidden". - I chose not to add it directly (although it is straightforward) in fear that we will never need it, and it would make the code more complex to understand and thus maintain.
561 lines
18 KiB
C++
561 lines
18 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
# include <QAction>
|
|
# include <QApplication>
|
|
# include <QToolBar>
|
|
# include <QToolButton>
|
|
# include <QPointer>
|
|
#endif
|
|
|
|
#include "ToolBarManager.h"
|
|
#include "Application.h"
|
|
#include "Command.h"
|
|
#include "MainWindow.h"
|
|
|
|
|
|
using namespace Gui;
|
|
|
|
ToolBarItem::ToolBarItem() : visibilityPolicy(DefaultVisibility::Visible)
|
|
{
|
|
}
|
|
|
|
ToolBarItem::ToolBarItem(ToolBarItem* item, DefaultVisibility visibilityPolicy) : visibilityPolicy(visibilityPolicy)
|
|
{
|
|
if (item) {
|
|
item->appendItem(this);
|
|
}
|
|
}
|
|
|
|
ToolBarItem::~ToolBarItem()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void ToolBarItem::setCommand(const std::string& name)
|
|
{
|
|
_name = name;
|
|
}
|
|
|
|
const std::string & ToolBarItem::command() const
|
|
{
|
|
return _name;
|
|
}
|
|
|
|
bool ToolBarItem::hasItems() const
|
|
{
|
|
return _items.count() > 0;
|
|
}
|
|
|
|
ToolBarItem* ToolBarItem::findItem(const std::string& name)
|
|
{
|
|
if ( _name == name ) {
|
|
return this;
|
|
}
|
|
|
|
for (auto it : qAsConst(_items)) {
|
|
if (it->_name == name) {
|
|
return it;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ToolBarItem* ToolBarItem::copy() const
|
|
{
|
|
auto root = new ToolBarItem;
|
|
root->setCommand( command() );
|
|
|
|
QList<ToolBarItem*> items = getItems();
|
|
for (auto it : items) {
|
|
root->appendItem(it->copy());
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
uint ToolBarItem::count() const
|
|
{
|
|
return _items.count();
|
|
}
|
|
|
|
void ToolBarItem::appendItem(ToolBarItem* item)
|
|
{
|
|
_items.push_back( item );
|
|
}
|
|
|
|
bool ToolBarItem::insertItem( ToolBarItem* before, ToolBarItem* item)
|
|
{
|
|
int pos = _items.indexOf(before);
|
|
if (pos != -1) {
|
|
_items.insert(pos, item);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ToolBarItem::removeItem(ToolBarItem* item)
|
|
{
|
|
int pos = _items.indexOf(item);
|
|
if (pos != -1) {
|
|
_items.removeAt(pos);
|
|
}
|
|
}
|
|
|
|
void ToolBarItem::clear()
|
|
{
|
|
for (auto it : qAsConst(_items)) {
|
|
delete it;
|
|
}
|
|
|
|
_items.clear();
|
|
}
|
|
|
|
ToolBarItem& ToolBarItem::operator << (ToolBarItem* item)
|
|
{
|
|
appendItem(item);
|
|
return *this;
|
|
}
|
|
|
|
ToolBarItem& ToolBarItem::operator << (const std::string& command)
|
|
{
|
|
auto item = new ToolBarItem(this);
|
|
item->setCommand(command);
|
|
return *this;
|
|
}
|
|
|
|
QList<ToolBarItem*> ToolBarItem::getItems() const
|
|
{
|
|
return _items;
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
|
|
ToolBarManager* ToolBarManager::_instance=nullptr;
|
|
|
|
ToolBarManager* ToolBarManager::getInstance()
|
|
{
|
|
if ( !_instance )
|
|
_instance = new ToolBarManager;
|
|
return _instance;
|
|
}
|
|
|
|
void ToolBarManager::destruct()
|
|
{
|
|
delete _instance;
|
|
_instance = nullptr;
|
|
}
|
|
|
|
ToolBarManager::ToolBarManager()
|
|
{
|
|
}
|
|
|
|
ToolBarManager::~ToolBarManager()
|
|
{
|
|
}
|
|
|
|
namespace {
|
|
QPointer<QWidget> createActionWidget()
|
|
{
|
|
static QPointer<QWidget> _ActionWidget;
|
|
if (!_ActionWidget) {
|
|
_ActionWidget = new QWidget(getMainWindow());
|
|
_ActionWidget->setObjectName(QStringLiteral("_fc_action_widget_"));
|
|
/* TODO This is a temporary hack until a longterm solution
|
|
is found, thanks to @realthunder for this pointer.
|
|
Although _ActionWidget has zero size, it somehow has a
|
|
'phantom' size without any visible content and will block the top
|
|
left tool buttons and menus of the application main window.
|
|
Therefore it is moved out of the way. */
|
|
_ActionWidget->move(QPoint(-100,-100));
|
|
}
|
|
else {
|
|
auto actions = _ActionWidget->actions();
|
|
for (auto action : actions) {
|
|
_ActionWidget->removeAction(action);
|
|
}
|
|
}
|
|
|
|
return _ActionWidget;
|
|
}
|
|
}
|
|
|
|
void ToolBarManager::setup(ToolBarItem* toolBarItems)
|
|
{
|
|
if (!toolBarItems)
|
|
return; // empty menu bar
|
|
|
|
QPointer<QWidget> _ActionWidget = createActionWidget();
|
|
|
|
saveState();
|
|
this->toolbarNames.clear();
|
|
|
|
int max_width = getMainWindow()->width();
|
|
int top_width = 0;
|
|
|
|
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("MainWindow")->GetGroup("Toolbars");
|
|
bool nameAsToolTip = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("Preferences")->GetGroup("MainWindow")->GetBool("ToolBarNameAsToolTip",true);
|
|
QList<ToolBarItem*> items = toolBarItems->getItems();
|
|
QList<QToolBar*> toolbars = toolBars();
|
|
for (QList<ToolBarItem*>::Iterator it = items.begin(); it != items.end(); ++it) {
|
|
// search for the toolbar
|
|
QString name = QString::fromUtf8((*it)->command().c_str());
|
|
this->toolbarNames << name;
|
|
QToolBar* toolbar = findToolBar(toolbars, name);
|
|
std::string toolbarName = (*it)->command();
|
|
bool toolbar_added = false;
|
|
|
|
if (!toolbar) {
|
|
toolbar = getMainWindow()->addToolBar(
|
|
QApplication::translate("Workbench",
|
|
toolbarName.c_str())); // i18n
|
|
toolbar->setObjectName(name);
|
|
if (nameAsToolTip){
|
|
auto tooltip = QChar::fromLatin1('[')
|
|
+ QApplication::translate("Workbench", toolbarName.c_str())
|
|
+ QChar::fromLatin1(']');
|
|
toolbar->setToolTip(tooltip);
|
|
}
|
|
toolbar_added = true;
|
|
}
|
|
else {
|
|
int index = toolbars.indexOf(toolbar);
|
|
toolbars.removeAt(index);
|
|
}
|
|
|
|
bool visible = false;
|
|
|
|
// If visibility policy is custom, the toolbar is initialised as not visible, and the
|
|
// toggleViewAction to control its visibility is not visible either.
|
|
//
|
|
// Both are managed under the responsibility of the client code
|
|
if((*it)->visibilityPolicy != ToolBarItem::DefaultVisibility::Unavailable) {
|
|
bool defaultvisibility = (*it)->visibilityPolicy == ToolBarItem::DefaultVisibility::Visible;
|
|
|
|
visible = hPref->GetBool(toolbarName.c_str(), defaultvisibility);
|
|
|
|
// Enable automatic handling of visibility via, for example, (contextual) menu
|
|
toolbar->toggleViewAction()->setVisible(true);
|
|
}
|
|
else { // ToolBarItem::DefaultVisibility::Unavailable
|
|
// Prevent that the action to show/hide a toolbar appears on the (contextual) menus.
|
|
// This is also managed by the client code for a toolbar with custom policy
|
|
toolbar->toggleViewAction()->setVisible(false);
|
|
}
|
|
|
|
// Initialise toolbar item visibility
|
|
toolbar->setVisible(visible);
|
|
|
|
// Store item visibility policy within the action
|
|
toolbar->toggleViewAction()->setProperty("DefaultVisibility", static_cast<int>((*it)->visibilityPolicy));
|
|
|
|
// setup the toolbar
|
|
setup(*it, toolbar);
|
|
auto actions = toolbar->actions();
|
|
for (auto action : actions) {
|
|
_ActionWidget->addAction(action);
|
|
}
|
|
|
|
// try to add some breaks to avoid to have all toolbars in one line
|
|
if (toolbar_added) {
|
|
if (top_width > 0 && getMainWindow()->toolBarBreak(toolbar))
|
|
top_width = 0;
|
|
// the width() of a toolbar doesn't return useful results so we estimate
|
|
// its size by the number of buttons and the icon size
|
|
QList<QToolButton*> btns = toolbar->findChildren<QToolButton*>();
|
|
top_width += (btns.size() * toolbar->iconSize().width());
|
|
if (top_width > max_width) {
|
|
top_width = 0;
|
|
getMainWindow()->insertToolBarBreak(toolbar);
|
|
}
|
|
}
|
|
}
|
|
|
|
// hide all unneeded toolbars
|
|
for (QList<QToolBar*>::Iterator it = toolbars.begin(); it != toolbars.end(); ++it) {
|
|
// make sure that the main window has the focus when hiding the toolbar with
|
|
// the combo box inside
|
|
QWidget *fw = QApplication::focusWidget();
|
|
while (fw && !fw->isWindow()) {
|
|
if (fw == *it) {
|
|
getMainWindow()->setFocus();
|
|
break;
|
|
}
|
|
fw = fw->parentWidget();
|
|
}
|
|
// ignore toolbars which do not belong to the previously active workbench
|
|
//QByteArray toolbarName = (*it)->objectName().toUtf8();
|
|
if (!(*it)->toggleViewAction()->isVisible())
|
|
continue;
|
|
//hPref->SetBool(toolbarName.constData(), (*it)->isVisible());
|
|
(*it)->hide();
|
|
(*it)->toggleViewAction()->setVisible(false);
|
|
}
|
|
|
|
hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("Preferences")->GetGroup("General");
|
|
bool lockToolBars = hPref->GetBool("LockToolBars", false);
|
|
setMovable(!lockToolBars);
|
|
}
|
|
|
|
void ToolBarManager::setup(ToolBarItem* item, QToolBar* toolbar) const
|
|
{
|
|
CommandManager& mgr = Application::Instance->commandManager();
|
|
QList<ToolBarItem*> items = item->getItems();
|
|
QList<QAction*> actions = toolbar->actions();
|
|
for (QList<ToolBarItem*>::Iterator it = items.begin(); it != items.end(); ++it) {
|
|
// search for the action item
|
|
QAction* action = findAction(actions, QString::fromLatin1((*it)->command().c_str()));
|
|
if (!action) {
|
|
if ((*it)->command() == "Separator") {
|
|
action = toolbar->addSeparator();
|
|
}
|
|
else {
|
|
// Check if action was added successfully
|
|
if (mgr.addTo((*it)->command().c_str(), toolbar))
|
|
action = toolbar->actions().constLast();
|
|
}
|
|
|
|
// set the tool button user data
|
|
if (action) {
|
|
action->setData(QString::fromLatin1((*it)->command().c_str()));
|
|
}
|
|
}
|
|
else {
|
|
// Note: For toolbars we do not remove and re-add the actions
|
|
// because this causes flicker effects. So, it could happen that the order of
|
|
// buttons doesn't match with the order of commands in the workbench.
|
|
int index = actions.indexOf(action);
|
|
actions.removeAt(index);
|
|
}
|
|
}
|
|
|
|
// remove all tool buttons which we don't need for the moment
|
|
for (QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it) {
|
|
toolbar->removeAction(*it);
|
|
}
|
|
}
|
|
|
|
void ToolBarManager::saveState() const
|
|
{
|
|
auto ignoreSave = [](QAction* action) {
|
|
// If the toggle action is invisible then it's controlled by the application.
|
|
// In this case the current state is not saved.
|
|
if (!action->isVisible()) {
|
|
return true;
|
|
}
|
|
|
|
QVariant property = action->property("DefaultVisibility");
|
|
if (property.isNull()) {
|
|
return false;
|
|
}
|
|
|
|
// If DefaultVisibility is Unavailable then never save the state because it's
|
|
// always controlled by the client code.
|
|
auto value = static_cast<ToolBarItem::DefaultVisibility>(property.toInt());
|
|
return value == ToolBarItem::DefaultVisibility::Unavailable;
|
|
};
|
|
|
|
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("MainWindow")->GetGroup("Toolbars");
|
|
|
|
QList<QToolBar*> toolbars = toolBars();
|
|
for (QStringList::ConstIterator it = this->toolbarNames.begin(); it != this->toolbarNames.end(); ++it) {
|
|
QToolBar* toolbar = findToolBar(toolbars, *it);
|
|
if (toolbar) {
|
|
if (ignoreSave(toolbar->toggleViewAction())) {
|
|
continue;
|
|
}
|
|
|
|
QByteArray toolbarName = toolbar->objectName().toUtf8();
|
|
hPref->SetBool(toolbarName.constData(), toolbar->isVisible());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBarManager::restoreState() const
|
|
{
|
|
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("MainWindow")->GetGroup("Toolbars");
|
|
|
|
QList<QToolBar*> toolbars = toolBars();
|
|
for (QStringList::ConstIterator it = this->toolbarNames.begin(); it != this->toolbarNames.end(); ++it) {
|
|
QToolBar* toolbar = findToolBar(toolbars, *it);
|
|
if (toolbar) {
|
|
QByteArray toolbarName = toolbar->objectName().toUtf8();
|
|
toolbar->setVisible(hPref->GetBool(toolbarName.constData(), toolbar->isVisible()));
|
|
}
|
|
}
|
|
|
|
|
|
hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
|
->GetGroup("Preferences")->GetGroup("General");
|
|
bool lockToolBars = hPref->GetBool("LockToolBars", false);
|
|
setMovable(!lockToolBars);
|
|
}
|
|
|
|
void ToolBarManager::retranslate() const
|
|
{
|
|
QList<QToolBar*> toolbars = toolBars();
|
|
for (QList<QToolBar*>::Iterator it = toolbars.begin(); it != toolbars.end(); ++it) {
|
|
QByteArray toolbarName = (*it)->objectName().toUtf8();
|
|
(*it)->setWindowTitle(
|
|
QApplication::translate("Workbench",
|
|
(const char*)toolbarName));
|
|
}
|
|
}
|
|
|
|
void Gui::ToolBarManager::setMovable(bool moveable) const
|
|
{
|
|
for (auto& tb : toolBars()) {
|
|
tb->setMovable(moveable);
|
|
}
|
|
}
|
|
|
|
QToolBar* ToolBarManager::findToolBar(const QList<QToolBar*>& toolbars, const QString& item) const
|
|
{
|
|
for (QList<QToolBar*>::ConstIterator it = toolbars.begin(); it != toolbars.end(); ++it) {
|
|
if ((*it)->objectName() == item)
|
|
return *it;
|
|
}
|
|
|
|
return nullptr; // no item with the user data found
|
|
}
|
|
|
|
QAction* ToolBarManager::findAction(const QList<QAction*>& acts, const QString& item) const
|
|
{
|
|
for (QList<QAction*>::ConstIterator it = acts.begin(); it != acts.end(); ++it) {
|
|
if ((*it)->data().toString() == item)
|
|
return *it;
|
|
}
|
|
|
|
return nullptr; // no item with the user data found
|
|
}
|
|
|
|
QList<QToolBar*> ToolBarManager::toolBars() const
|
|
{
|
|
QWidget* mw = getMainWindow();
|
|
QList<QToolBar*> tb;
|
|
QList<QToolBar*> bars = getMainWindow()->findChildren<QToolBar*>();
|
|
for (QList<QToolBar*>::Iterator it = bars.begin(); it != bars.end(); ++it) {
|
|
if ((*it)->parentWidget() == mw)
|
|
tb.push_back(*it);
|
|
}
|
|
|
|
return tb;
|
|
}
|
|
|
|
ToolBarItem::DefaultVisibility ToolBarManager::getToolbarPolicy(const QToolBar* toolbar) const
|
|
{
|
|
auto* action = toolbar->toggleViewAction();
|
|
|
|
QVariant property = action->property("DefaultVisibility");
|
|
if (property.isNull()) {
|
|
return ToolBarItem::DefaultVisibility::Visible;
|
|
}
|
|
|
|
return static_cast<ToolBarItem::DefaultVisibility>(property.toInt());
|
|
}
|
|
|
|
void ToolBarManager::setState(const QList<QString>& names, State state)
|
|
{
|
|
for (auto& name : names) {
|
|
setState(name, state);
|
|
}
|
|
}
|
|
|
|
void ToolBarManager::setState(const QString& name, State state)
|
|
{
|
|
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("MainWindow")->GetGroup("Toolbars");
|
|
|
|
auto visibility = [hPref, name](bool defaultvalue) {
|
|
return hPref->GetBool(name.toStdString().c_str(), defaultvalue);
|
|
};
|
|
|
|
auto saveVisibility = [hPref, name](bool value) {
|
|
hPref->SetBool(name.toStdString().c_str(), value);
|
|
};
|
|
|
|
auto showhide = [visibility](QToolBar* toolbar, ToolBarItem::DefaultVisibility policy) {
|
|
|
|
auto show = visibility( policy == ToolBarItem::DefaultVisibility::Visible );
|
|
|
|
if(show) {
|
|
toolbar->show();
|
|
}
|
|
else {
|
|
toolbar->hide();
|
|
}
|
|
};
|
|
|
|
QToolBar* tb = findToolBar(toolBars(), name);
|
|
if (tb) {
|
|
|
|
if (state == State::RestoreDefault) {
|
|
|
|
auto policy = getToolbarPolicy(tb);
|
|
|
|
if(policy == ToolBarItem::DefaultVisibility::Unavailable) {
|
|
tb->hide();
|
|
tb->toggleViewAction()->setVisible(false);
|
|
}
|
|
else {
|
|
tb->toggleViewAction()->setVisible(true);
|
|
|
|
showhide(tb, policy);
|
|
}
|
|
}
|
|
else if (state == State::ForceAvailable) {
|
|
|
|
auto policy = getToolbarPolicy(tb);
|
|
|
|
tb->toggleViewAction()->setVisible(true);
|
|
|
|
// Unavailable policy defaults to a Visible toolbars when made available
|
|
auto show = visibility( policy == ToolBarItem::DefaultVisibility::Visible ||
|
|
policy == ToolBarItem::DefaultVisibility::Unavailable);
|
|
|
|
if(show) {
|
|
tb->show();
|
|
}
|
|
else {
|
|
tb->hide();
|
|
}
|
|
}
|
|
else if (state == State::ForceHidden) {
|
|
tb->toggleViewAction()->setVisible(false); // not visible in context menus
|
|
tb->hide(); // toolbar not visible
|
|
|
|
}
|
|
else if (state == State::SaveState) {
|
|
auto show = tb->isVisible();
|
|
saveVisibility(show);
|
|
}
|
|
}
|
|
}
|