diff --git a/mscore/musescore.cpp b/mscore/musescore.cpp index 2a1689d7dd2a6..117ac6505f2ef 100644 --- a/mscore/musescore.cpp +++ b/mscore/musescore.cpp @@ -288,7 +288,10 @@ const std::list MuseScore::_allFileOperationEntries { "file-save-online", "print", "undo", - "redo" + "redo", + "", + "zoom-options", + "view-mode" }; const std::list MuseScore::_allPlaybackControlEntries { @@ -1006,46 +1009,63 @@ bool MuseScore::isInstalledExtension(QString extensionId) void MuseScore::populateFileOperations() { - // Save the current zoom and view-mode combobox states. if any. - const auto magState = mag ? std::make_pair(mag->currentIndex(), mag->currentText()) : std::make_pair(-1, QString()); - const auto viewModeComboIndex = viewModeCombo ? viewModeCombo->currentIndex() : -1; + fileTools->clear(); - fileTools->clear(); + bool leftToRightLayout = qApp->layoutDirection() == Qt::LayoutDirection::LeftToRight; - if (qApp->layoutDirection() == Qt::LayoutDirection::LeftToRight) { - for (auto s : _fileOperationEntries) { - if (!*s) { - fileTools->addSeparator(); - } else { - fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(s))); - } - } - } else { - _fileOperationEntries.reverse(); - for (auto s : _fileOperationEntries) { - if (!*s) { - fileTools->addSeparator(); - } else { - fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(s))); + if (!leftToRightLayout) + _fileOperationEntries.reverse(); + + + for (auto s : _fileOperationEntries) { + + if (!*s) + fileTools->addSeparator(); + else if (!strcmp("view-mode", s)) + addViewModeWidget(); + else if (!strcmp("zoom-options", s)) + addZoomOptionsWidget(); + else + fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(s))); } - } - _fileOperationEntries.reverse(); - } - // Currently not customizable in ToolbarEditor - fileTools->addSeparator(); - mag = new MagBox; + if (!leftToRightLayout) + _fileOperationEntries.reverse(); +} - // Restore the saved zoom combobox index and text, if any. - if (magState.first != -1) { +// zoom-options is treated as a special case as it's not a QToolButton +// but, rather, a MagBox (a sub-class of QComboBox) +void MuseScore::addZoomOptionsWidget() +{ + // Save the current zoom state, if any. + const auto magState = mag ? std::make_pair(mag->currentIndex(), mag->currentText()) : std::make_pair(-1, QString()); + QWidget* spacer1 = new QWidget(); + spacer1->setMinimumWidth(1); + spacer1->setMaximumWidth(1); + fileTools->addWidget(spacer1); + mag = new MagBox; + + // Restore the saved zoom combobox index and text, if any. + if (magState.first != -1) { mag->setCurrentIndex(magState.first); mag->setCurrentText(magState.second); - } - - connect(mag, SIGNAL(magChanged(MagIdx)), SLOT(magChanged(MagIdx))); - fileTools->addWidget(mag); - - viewModeCombo = new QComboBox(this); + } + + connect(mag, SIGNAL(magChanged(MagIdx)), SLOT(magChanged(MagIdx))); + fileTools->addWidget(mag); + QWidget* spacer2 = new QWidget(); + spacer2->setMinimumWidth(1); + spacer2->setMaximumWidth(1); + fileTools->addWidget(spacer2); +} + +// view-mode is treated as a special case as it's not a QToolButton +// but, rather, a QComboBox +void MuseScore::addViewModeWidget() + { + // Save the current view-mode combobox state, if any. + const auto viewModeComboIndex = viewModeCombo ? viewModeCombo->currentIndex() : -1; + viewModeCombo = new QComboBox(this); #if defined(Q_OS_MAC) viewModeCombo->setFocusPolicy(Qt::StrongFocus); #else @@ -4690,6 +4710,12 @@ void MuseScore::changeState(ScoreState val) getAction("split-measure")->setEnabled(cs && cs->masterScore()->excerpts().size() == 0); } + // currentWorkspace can be nullptr in some contexts, so check to avoid crash + Workspace* currentWorkspace = WorkspacesManager::currentWorkspace(); + if (currentWorkspace) { + getAction("edit-toolbars")->setEnabled(currentWorkspace->canCustomizeToolbars()); + } + // disabling top level menu entries does not // work for MAC diff --git a/mscore/musescore.h b/mscore/musescore.h index b217d24075e0f..523055ec5c83d 100644 --- a/mscore/musescore.h +++ b/mscore/musescore.h @@ -835,6 +835,8 @@ public slots: std::list* fileOperationEntries() { return &_fileOperationEntries; } void setFileOperationEntries(std::list l) { _fileOperationEntries = l; } void populateFileOperations(); + void addViewModeWidget(); + void addZoomOptionsWidget(); static const std::list& allPlaybackControlEntries() { return _allPlaybackControlEntries; } std::list* playbackControlEntries() { return &_playbackControlEntries; } diff --git a/mscore/toolbarEditor.cpp b/mscore/toolbarEditor.cpp index 1f2dadeade542..a75d7d02c6361 100644 --- a/mscore/toolbarEditor.cpp +++ b/mscore/toolbarEditor.cpp @@ -13,6 +13,7 @@ #include "toolbarEditor.h" #include "musescore.h" #include "workspace.h" +#include "shortcut.h" #include "icons.h" namespace Ms { @@ -64,8 +65,8 @@ ToolbarEditor::ToolbarEditor(QWidget* parent) up->setIcon(*icons[int(Icons::arrowUp_ICON)]); down->setIcon(*icons[int(Icons::arrowDown_ICON)]); - add->setIcon(*icons[int(Icons::goPrevious_ICON)]); - remove->setIcon(*icons[int(Icons::goNext_ICON)]); + add->setIcon(*icons[int(Icons::goNext_ICON)]); + remove->setIcon(*icons[int(Icons::goPrevious_ICON)]); MuseScore::restoreGeometry(this); } @@ -76,16 +77,18 @@ ToolbarEditor::ToolbarEditor(QWidget* parent) void ToolbarEditor::init() { - QString name = WorkspacesManager::currentWorkspace()->name(); - bool writable = !WorkspacesManager::currentWorkspace()->readOnly(); - if (!writable) { - name += " " + tr("(not changeable)"); - } - add->setEnabled(writable); - remove->setEnabled(writable); - up->setEnabled(writable); - down->setEnabled(writable); - workspaceName->setText(name); + Workspace* currentWorkspace = WorkspacesManager::currentWorkspace(); + QString name = currentWorkspace->name(); + QString mainTitle = tr("Customize Toolbars"); + setWindowTitle(mainTitle + " (" + name + ")"); + + // defensive - don't expect to be here if workspace is not customizable + bool canBeCustomized = currentWorkspace->canCustomizeToolbars(); + + add->setEnabled(canBeCustomized); + remove->setEnabled(canBeCustomized); + up->setEnabled(canBeCustomized); + down->setEnabled(canBeCustomized); // Syncs the editor with the current toolbars new_toolbars->at(0) = mscore->noteInputMenuEntries(); @@ -100,7 +103,7 @@ void ToolbarEditor::init() void ToolbarEditor::accepted() { - if (WorkspacesManager::currentWorkspace()->readOnly()) { + if (!WorkspacesManager::currentWorkspace()->canCustomizeToolbars()) { return; } // Updates the toolbars @@ -113,6 +116,54 @@ void ToolbarEditor::accepted() WorkspacesManager::currentWorkspace()->setDirty(true); } + +//--------------------------------------------------------- +// listItem +//--------------------------------------------------------- +QListWidgetItem* ToolbarEditor::listItem(const char* id) + { + QListWidgetItem* item = _listItem(id); + + if (!item) + return nullptr; + + // add a sensible height for cases where there is no icon + // to avoid row looking squashed + item->setSizeHint(QSize(item->sizeHint().width(), 26)); + + item->setData(Qt::UserRole, QVariant::fromValue((void*)id)); + return item; + } + +QListWidgetItem* ToolbarEditor::_listItem(const char* id) + { + if (QString(id).isEmpty()) + return new QListWidgetItem(tr("Separator")); + + if (!strcmp("view-mode", id)) + return new QListWidgetItem(tr("View mode")); + + if (!strcmp("zoom-options", id)) + return new QListWidgetItem(tr("Zoom options")); + + QAction* action = getAction(id); + + if (action) { + QString itemName = Shortcut::getShortcut(id)->text(); + + // in this case we can't use the menu name (it's too short + // as it's used for the button label). So use the shortcut + // description instead + if (strncmp(id, "voice-", 6) == 0) + itemName = Shortcut::getShortcut(id)->descr(); + + return new QListWidgetItem(action->icon(), itemName); + } + + qDebug()<<"ToolbarEditor does not recognize id for toolbar item: "<& all, std::list* current) { actionList->clear(); - availableList->clear(); - for (auto i : *current) { - QAction* a = getAction(i); - QListWidgetItem* item; - QString actionName = QString(i); - if (a) { - item = new QListWidgetItem(a->icon(), actionName); - } else if (actionName.isEmpty()) { - item = new QListWidgetItem(tr("Separator")); - } else { - item = new QListWidgetItem(actionName); - } - item->setData(Qt::UserRole, QVariant::fromValue((void*)i)); + for (auto currentId : *current) { + QListWidgetItem* item = listItem(currentId); + if (!item) + continue; + actionList->addItem(item); } - for (auto i : all) { + + refreshAvailableList(all, current); +} + + +void ToolbarEditor::refreshAvailableList(const std::list& all, std::list* current) +{ + availableList->clear(); + + for (auto id : all) { bool found = false; - for (auto k : *current) { - if (strcmp(k, i) == 0) { + + for (auto allId : *current) { + if (strcmp(allId, id) == 0) { found = true; break; } - } - if (!found) { - QAction* a = getAction(i); - QListWidgetItem* item = 0; - QString actionName = QString(i); - if (a) { - item = new QListWidgetItem(a->icon(), actionName); - } else if (!actionName.isEmpty()) { - item = new QListWidgetItem(QString(i)); - } - if (item) { - item->setData(Qt::UserRole, QVariant::fromValue((void*)i)); - availableList->addItem(item); - } - } } - QListWidgetItem* item = new QListWidgetItem(tr("Separator")); - item->setData(Qt::UserRole, QVariant::fromValue((void*)"")); - availableList->addItem(item); + + if (found) + continue; + + if (QString(id).isEmpty()) + continue; + + QListWidgetItem* item = listItem(id); + if (!item) + continue; + + availableList->addItem(item); + } + + // add the Separator item + availableList->addItem(listItem("")); } //--------------------------------------------------------- @@ -178,26 +229,26 @@ bool ToolbarEditor::isSpacer(QListWidgetItem* item) const void ToolbarEditor::addAction() { - int cr = availableList->currentRow(); - if (cr == -1) { + int currentRow = availableList->currentRow(); + + if (currentRow == -1) return; - } - QListWidgetItem* item = availableList->item(cr); - - if (isSpacer(item)) { - QListWidgetItem* nitem = new QListWidgetItem(item->text()); - nitem->setData(Qt::UserRole, QVariant::fromValue((void*)"")); - item = nitem; - } else { - item = availableList->takeItem(cr); - } - cr = actionList->currentRow(); - if (cr == -1) { + + QListWidgetItem* item = availableList->item(currentRow); + + if (isSpacer(item)) + item = listItem(""); + else + item = availableList->takeItem(currentRow); + + currentRow = actionList->currentRow(); + + if (currentRow == -1) actionList->addItem(item); - } else { - actionList->insertItem(cr, item); - } - updateNewToolbar(toolbarList->currentRow()); + else + actionList->insertItem(currentRow, item); + + updateNewToolbar(toolbarList->currentRow()); } //--------------------------------------------------------- @@ -206,15 +257,28 @@ void ToolbarEditor::addAction() void ToolbarEditor::removeAction() { - int cr = actionList->currentRow(); - if (cr == -1) { + int actionListRow = actionList->currentRow(); + + if (actionListRow == -1) return; - } - QListWidgetItem* item = actionList->takeItem(cr); - if (!isSpacer(item)) { - availableList->addItem(item); - } + + QListWidgetItem* item = actionList->takeItem(actionListRow); updateNewToolbar(toolbarList->currentRow()); + + if (isSpacer(item)) + return; + + int toolbarListRow = toolbarList->currentRow(); + switch (toolbarListRow) { + case 0: + refreshAvailableList(MuseScore::allNoteInputMenuEntries(), new_toolbars->at(toolbarListRow)); + break; + case 1: + refreshAvailableList(MuseScore::allFileOperationEntries(), new_toolbars->at(toolbarListRow)); + break; + case 2: + refreshAvailableList(MuseScore::allPlaybackControlEntries(), new_toolbars->at(toolbarListRow)); + } } //--------------------------------------------------------- diff --git a/mscore/toolbarEditor.h b/mscore/toolbarEditor.h index e84eecf11cfc7..a0baa36c8b32a 100644 --- a/mscore/toolbarEditor.h +++ b/mscore/toolbarEditor.h @@ -28,6 +28,9 @@ class ToolbarEditor : public QDialog, public Ui::ToolbarEditor void updateNewToolbar(int toolbar_to_update); void populateLists(const std::list&, std::list*); + void refreshAvailableList(const std::list&, std::list*); + QListWidgetItem* listItem(const char* id); + QListWidgetItem* _listItem(const char* id); bool isSpacer(QListWidgetItem*) const; virtual void hideEvent(QHideEvent*); diff --git a/mscore/toolbarEditor.ui b/mscore/toolbarEditor.ui index 367bd3991ebd4..524940e644541 100644 --- a/mscore/toolbarEditor.ui +++ b/mscore/toolbarEditor.ui @@ -11,11 +11,11 @@ - Edit Toolbar + Customize Toolbars - - + + 0 @@ -23,25 +23,22 @@ - Toolbar + Available actions - - - - - 0 - 0 - + + + + Qt::Horizontal - - Actions + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + 0 @@ -49,73 +46,52 @@ - Available Actions + Actions - - - - -1 + + + + Actions available to show on toolbar - - - - - - - - - - - - - 0 - 0 - - - - Workspace: - - - - - - - true - - - - - - + + + Add action to toolbar + - :/data/icons/go-previous.svg:/data/icons/go-previous.svg + :/data/icons/go-next.svg:/data/icons/go-next.svg + + Remove action from toolbar + - :/data/icons/go-next.svg:/data/icons/go-next.svg + :/data/icons/go-previous.svg:/data/icons/go-previous.svg + + Move action up + @@ -127,6 +103,9 @@ + + Move action down + @@ -138,20 +117,39 @@ - - - - Qt::Horizontal + + + + Toolbar to customize - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + -1 + + + + + + + + 0 + 0 + + + + Toolbar + + + + + + + Actions shown on toolbar - workspaceName toolbarList actionList add diff --git a/mscore/workspace.cpp b/mscore/workspace.cpp index c9fa176095063..ea98208a2cde6 100644 --- a/mscore/workspace.cpp +++ b/mscore/workspace.cpp @@ -240,6 +240,8 @@ void MuseScore::changeWorkspace(Workspace* p, bool first) preferencesChanged(true); } + getAction("edit-toolbars")->setEnabled(WorkspacesManager::currentWorkspace()->canCustomizeToolbars()); + connect(getPaletteWorkspace(), &PaletteWorkspace::userPaletteChanged, WorkspacesManager::currentWorkspace(), QOverload<>::of(&Workspace::setDirty), Qt::UniqueConnection); @@ -247,6 +249,7 @@ void MuseScore::changeWorkspace(Workspace* p, bool first) emit mscore->workspacesChanged(); } + //--------------------------------------------------------- // updateIcons //--------------------------------------------------------- @@ -1139,6 +1142,12 @@ void Workspace::setDirty(bool val) _saveTimer.start(); } + +bool Workspace::canCustomizeToolbars() +{ + return saveToolbars && !_readOnly; +} + //--------------------------------------------------------- // save //--------------------------------------------------------- diff --git a/mscore/workspace.h b/mscore/workspace.h index 1b14b43c67689..150ae564b0950 100644 --- a/mscore/workspace.h +++ b/mscore/workspace.h @@ -90,6 +90,7 @@ public slots: void read(XmlReader&); void read(); bool readOnly() const { return _readOnly; } + bool canCustomizeToolbars(); void setReadOnly(bool val) { _readOnly = val; } void reset(); diff --git a/mscore/workspacedialog.cpp b/mscore/workspacedialog.cpp index 649e42843c046..a5e39ad5451d8 100644 --- a/mscore/workspacedialog.cpp +++ b/mscore/workspacedialog.cpp @@ -149,6 +149,9 @@ void WorkspaceDialog::accepted() newWorkspace->setSaveMenuBar(menubarCheck->isChecked()); preferences.setUseLocalPreferences(prefsCheck->isChecked()); + //enable or disable customize toolbars menu to reflect current status + getAction("edit-toolbars")->setEnabled(newWorkspace->canCustomizeToolbars()); + //save newly created/edited workspace newWorkspace->save(); diff --git a/share/workspaces/Advanced.xml b/share/workspaces/Advanced.xml index 0d379f67a54a1..fb8d4362be363 100644 --- a/share/workspaces/Advanced.xml +++ b/share/workspaces/Advanced.xml @@ -3334,6 +3334,9 @@ print undo redo + + zoom-options + view-mode midi-on diff --git a/share/workspaces/Basic.xml b/share/workspaces/Basic.xml index d5e5f182d0da3..4e83ca146a9fb 100644 --- a/share/workspaces/Basic.xml +++ b/share/workspaces/Basic.xml @@ -926,7 +926,10 @@ print undo redo - + + zoom-options + view-mode + midi-on