Added export dialog

This commit is contained in:
Sidharth Anand 2021-02-26 18:30:07 +05:30 committed by Casper Jeukendrup
parent 983c9a56e8
commit 9a22d98b47
31 changed files with 1640 additions and 1 deletions

View file

@ -32,6 +32,10 @@ FocusScope {
property alias text: label.text
property alias font: label.font
property alias wrapMode: label.wrapMode
property alias font: label.font
property alias checkBoxWidth: box.width
property alias checkBoxHeight: box.height
property alias navigation: navCtrl

View file

@ -36,13 +36,16 @@ public:
// Pdf
virtual int exportPdfDpiResolution() const = 0;
virtual void setExportPdfDpiResolution(int dpi) = 0;
// Png
virtual float exportPngDpiResolution() const = 0;
virtual bool exportPngWithTransparentBackground() const = 0;
//! NOTE Maybe set from command line
virtual void setExportPngDpiResolution(std::optional<float> dpi) = 0;
virtual bool exportPngWithTransparentBackground() const = 0;
virtual void setExportPngWithTransparentBackground(bool transparent) = 0;
};
}

View file

@ -44,6 +44,11 @@ int ImagesExportConfiguration::exportPdfDpiResolution() const
return settings()->value(EXPORT_PDF_DPI_RESOLUTION_KEY).toInt();
}
void ImagesExportConfiguration::setExportPdfDpiResolution(int dpi)
{
settings()->setValue(EXPORT_PDF_DPI_RESOLUTION_KEY, Val(dpi));
}
void ImagesExportConfiguration::setExportPngDpiResolution(std::optional<float> dpi)
{
m_customExportPngDpi = dpi;
@ -62,3 +67,8 @@ bool ImagesExportConfiguration::exportPngWithTransparentBackground() const
{
return settings()->value(EXPORT_PNG_USE_TRASNPARENCY_KEY).toBool();
}
void ImagesExportConfiguration::setExportPngWithTransparentBackground(bool transparent)
{
settings()->setValue(EXPORT_PNG_USE_TRASNPARENCY_KEY, Val(transparent));
}

View file

@ -31,11 +31,13 @@ public:
void init();
int exportPdfDpiResolution() const override;
void setExportPdfDpiResolution(int dpi) override;
void setExportPngDpiResolution(std::optional<float> dpi) override;
float exportPngDpiResolution() const override;
bool exportPngWithTransparentBackground() const override;
void setExportPngWithTransparentBackground(bool transparent) override;
private:

View file

@ -40,6 +40,7 @@ class QRect;
namespace mu::notation {
class INotation;
using INotationPtr = std::shared_ptr<INotation>;
using INotationPtrList = std::vector<INotationPtr>;
class INotation
{

View file

@ -36,6 +36,8 @@ public:
virtual void reg(const std::vector<std::string>& suffixes, INotationWriterPtr writer) = 0;
virtual INotationWriterPtr writer(const std::string& suffix) const = 0;
virtual std::vector<std::string> registeredSuffixes() const = 0;
};
}

View file

@ -40,3 +40,14 @@ INotationWriterPtr NotationWritersRegister::writer(const std::string& suffix) co
return nullptr;
}
std::vector<std::string> NotationWritersRegister::registeredSuffixes() const
{
std::vector<std::string> suffixes;
for (auto& it : m_writers) {
suffixes.push_back(it.first);
}
return suffixes;
}

View file

@ -32,6 +32,8 @@ public:
void reg(const std::vector<std::string>& suffixes, INotationWriterPtr writer) override;
INotationWriterPtr writer(const std::string& suffix) const override;
std::vector<std::string> registeredSuffixes() const override;
private:
std::map<std::string, INotationWriterPtr> m_writers;
};

View file

@ -30,6 +30,7 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/iuserscoresconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/iuserscoresservice.h
${CMAKE_CURRENT_LIST_DIR}/ifilescorecontroller.h
${CMAKE_CURRENT_LIST_DIR}/iexportscoreservice.h
${CMAKE_CURRENT_LIST_DIR}/userscorestypes.h
${CMAKE_CURRENT_LIST_DIR}/view/recentscoresmodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/recentscoresmodel.h
@ -43,6 +44,12 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/view/templatesmodel.h
${CMAKE_CURRENT_LIST_DIR}/view/templatepaintview.cpp
${CMAKE_CURRENT_LIST_DIR}/view/templatepaintview.h
${CMAKE_CURRENT_LIST_DIR}/view/exportscoremodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/exportscoremodel.h
${CMAKE_CURRENT_LIST_DIR}/view/exportscoresuffixmodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/exportscoresuffixmodel.h
${CMAKE_CURRENT_LIST_DIR}/view/exportscoresettingsmodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/exportscoresettingsmodel.h
${CMAKE_CURRENT_LIST_DIR}/internal/filescorecontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/filescorecontroller.h
${CMAKE_CURRENT_LIST_DIR}/internal/userscoresconfiguration.cpp
@ -54,6 +61,8 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/internal/templatesrepository.h
${CMAKE_CURRENT_LIST_DIR}/internal/userscoresuiactions.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/userscoresuiactions.h
${CMAKE_CURRENT_LIST_DIR}/internal/exportscoreservice.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/exportscoreservice.h
)
set(MODULE_LINK notation)

View file

@ -0,0 +1,36 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_IEXPORTSCORESERVICE_H
#define MU_USERSCORES_IEXPORTSCORESERVICE_H
#include "modularity/imoduleexport.h"
#include "notation/inotation.h"
namespace mu::userscores {
class IExportScoreService : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(IExportScoreService)
public:
virtual void exportScores(notation::INotationPtrList& notations, io::path& basePath) = 0;
};
}
#endif // MU_USERSCORES_IEXPORTSCORESERVICE_H

View file

@ -0,0 +1,152 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "exportscoreservice.h"
#include "translation.h"
using namespace mu::userscores;
using namespace mu::notation;
using namespace mu::framework;
void ExportScoreService::exportScores(INotationPtrList& notations, io::path& exportPath)
{
std::string suffix = io::syffix(exportPath);
auto writer = writers()->writer(suffix);
if (!writer) {
return;
}
for (INotationPtr currentNotation : notations) {
if (shouldExportIndividualPage(suffix)) {
for (int page = 0; page < currentNotation->elements()->msScore()->pages().size(); page++) {
exportSingleScore(writer, exportPath, currentNotation, page);
}
} else {
exportSingleScore(writer, exportPath, currentNotation);
}
}
}
ExportScoreService::FileConflictPolicy ExportScoreService::getConflictPolicy(std::string filename)
{
switch (m_currentConflictPolicy) {
case Replace:
case Skip:
askConflictPolicy(filename);
}
return getEffectivePolicy(m_currentConflictPolicy);
}
bool ExportScoreService::askForRetry(std::string filename) const
{
int btn = interactive()->question("Error", "An error occured while exporting the file " + filename, {
interactive()->buttonData(IInteractive::Button::Retry),
interactive()->buttonData(IInteractive::Button::Abort)
});
return btn == static_cast<int>(IInteractive::Button::Retry);
}
ExportScoreService::FileConflictPolicy ExportScoreService::getEffectivePolicy(ExportScoreService::FileConflictPolicy policy) const
{
switch (policy) {
case ReplaceAll:
return FileConflictPolicy::Replace;
case SkipAll:
return FileConflictPolicy::Skip;
default:
return policy;
}
}
void ExportScoreService::askConflictPolicy(std::string filename)
{
int replace = static_cast<int>(IInteractive::Button::CustomButton) + 1;
int replaceAll = static_cast<int>(IInteractive::Button::CustomButton) + 2;
int skip = static_cast<int>(IInteractive::Button::CustomButton) + 3;
int skipAll = static_cast<int>(IInteractive::Button::CustomButton) + 4;
int btn = interactive()->question("File already exists", "A file already exists with the filename " + filename, {
IInteractive::ButtonData(replace, "Replace"),
IInteractive::ButtonData(replaceAll, "Replace All"),
IInteractive::ButtonData(skip, "Skip"),
IInteractive::ButtonData(skipAll, "Skip All")
});
m_currentConflictPolicy = static_cast<FileConflictPolicy>(btn - replace);
}
bool ExportScoreService::exportSingleScore(INotationWriterPtr writer, io::path exportPath, INotationPtr notation, int page)
{
io::path outPath
= configuration()->completeExportPath(exportPath, notation, isMainNotation(notation), shouldExportIndividualPage(io::syffix(
exportPath)),
page);
QString outPathString = outPath.toQString();
std::string completeFileName = io::filename(outPath).toStdString();
if (fileSystem()->exists(outPathString)) {
if (getConflictPolicy(completeFileName)) {
return false;
}
}
INotationWriter::Options options({
{ INotationWriter::OptionKey::PAGE_NUMBER, Val(page) },
{ INotationWriter::OptionKey::TRANSPARENT_BACKGROUND, Val(imagesExportConfiguration()->exportPngWithTransparentBackground()) }
});
while (true) {
QFile outFile(outPath.toQString());
if (!outFile.open(QFile::WriteOnly)) {
if (askForRetry(completeFileName)) {
continue;
} else {
return false;
}
}
if (!writer->write(notation, outFile, options)) {
outFile.close();
if (askForRetry(completeFileName)) {
continue;
} else {
return false;
}
}
outFile.close();
break;
}
return true;
}
bool ExportScoreService::shouldExportIndividualPage(io::path suffix) const
{
return suffix == "png" || suffix == "svg";
}
bool ExportScoreService::isMainNotation(INotationPtr notation) const
{
return context()->currentMasterNotation()->notation() == notation;
}

View file

@ -0,0 +1,70 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_EXPORTSCORESERVICE_H
#define MU_USERSCORES_EXPORTSCORESERVICE_H
#include "modularity/ioc.h"
#include "iexportscoreservice.h"
#include "iuserscoresconfiguration.h"
#include "iinteractive.h"
#include "notation/inotationwritersregister.h"
#include "importexport/imagesexport/iimagesexportconfiguration.h"
#include "context/iglobalcontext.h"
#include "notation/inotation.h"
#include "system/ifilesystem.h"
namespace mu::userscores {
class ExportScoreService : public IExportScoreService
{
INJECT(userscores, IUserScoresConfiguration, configuration);
INJECT(userscores, framework::IInteractive, interactive);
INJECT(userscores, notation::INotationWritersRegister, writers)
INJECT(userscores, iex::imagesexport::IImagesExportConfiguration, imagesExportConfiguration)
INJECT(userscores, context::IGlobalContext, context)
INJECT(userscores, system::IFileSystem, fileSystem)
public:
void exportScores(notation::INotationPtrList& notations, io::path& exportPath) override;
private:
enum FileConflictPolicy {
Replace,
ReplaceAll,
Skip,
SkipAll
};
bool isMainNotation(notation::INotationPtr notation) const;
FileConflictPolicy getConflictPolicy(std::string filename);
FileConflictPolicy getEffectivePolicy(FileConflictPolicy policy) const;
void askConflictPolicy(std::string filename);
bool askForRetry(std::string filename) const;
bool exportSingleScore(notation::INotationWriterPtr writer, io::path exportPath, notation::INotationPtr score, int page = 0);
bool shouldExportIndividualPage(io::path suffix) const;
FileConflictPolicy m_currentConflictPolicy;
};
}
#endif // MU_USERSCORES_EXPORTSCORESERVICE_H

View file

@ -45,6 +45,8 @@ void FileScoreController::init()
dispatcher()->reg(this, "file-save-a-copy", this, &FileScoreController::saveScoreCopy);
dispatcher()->reg(this, "file-save-selection", this, &FileScoreController::saveSelection);
dispatcher()->reg(this, "file-export", this, &FileScoreController::exportScore);
dispatcher()->reg(this, "file-import-pdf", this, &FileScoreController::importPdf);
dispatcher()->reg(this, "clear-recent", this, &FileScoreController::clearRecentScores);
@ -213,6 +215,11 @@ void FileScoreController::continueLastSession()
openScore(lastScorePath);
}
void FileScoreController::exportScore()
{
interactive()->open("musescore://userscores/export");
}
io::path FileScoreController::selectScoreOpenningFile(const QStringList& filter)
{
QString filterStr = filter.join(";;");

View file

@ -74,6 +74,8 @@ private:
Ret doOpenScore(const io::path& filePath);
void doSaveScore(const io::path& filePath = io::path(), notation::SaveMode saveMode = notation::SaveMode::Save);
void exportScore();
io::path defaultSavingFilePath() const;
void prependToRecentScoreList(const io::path& filePath);

View file

@ -37,6 +37,7 @@ static const Settings::Key USER_SCORES_PATH(module_name, "application/paths/mySc
static const Settings::Key PREFERRED_SCORE_CREATION_MODE_KEY(module_name, "userscores/preferedScoreCreationMode");
const QString UserScoresConfiguration::DEFAULT_FILE_SUFFIX(".mscz");
const QString UserScoresConfiguration::DEFAULT_EXPORT_SUFFIX(".pdf");
static const std::string TEMPLATES_PATH("/templates");
@ -193,6 +194,26 @@ io::path UserScoresConfiguration::defaultSavingFilePath(const io::path& fileName
return scoresPath().val + "/" + fileName + DEFAULT_FILE_SUFFIX;
}
io::path UserScoresConfiguration::defaultExportPath(const std::string& fileName) const
{
return scoresPath().val + "/" + fileName + DEFAULT_EXPORT_SUFFIX;
}
io::path UserScoresConfiguration::completeExportPath(io::path basePath, INotationPtr notation, bool isMain, bool singlePage,
int pageNumber) const
{
io::path suffix = io::syffix(basePath);
io::path exportDirectory = io::dirpath(basePath);
io::path baseFilename = io::basename(basePath);
std::string notationNameExtension
= (isMain ? "" : "-" + io::escapeFileName(notation->metaInfo().title).toStdString());
std::string pageNameExtension = (singlePage ? "-" + std::to_string(pageNumber) : "");
std::string completeFileName = baseFilename.toStdString() + notationNameExtension + pageNameExtension + "." + suffix.toStdString();
return exportDirectory + "/" + completeFileName;
}
QColor UserScoresConfiguration::templatePreviewBackgroundColor() const
{
return notationConfiguration()->backgroundColor();

View file

@ -40,6 +40,7 @@ class UserScoresConfiguration : public IUserScoresConfiguration
public:
static const QString DEFAULT_FILE_SUFFIX;
static const QString DEFAULT_EXPORT_SUFFIX;
void init();
@ -57,6 +58,9 @@ public:
void setScoresPath(const io::path& path) override;
io::path defaultSavingFilePath(const io::path& fileName) const override;
io::path defaultExportPath(const std::string& fileName) const override;
io::path completeExportPath(io::path basePath, notation::INotationPtr notation, bool isMain, bool singlePage,
int pageNumber) const override;
QColor templatePreviewBackgroundColor() const override;
async::Notification templatePreviewBackgroundChanged() const override;

View file

@ -29,6 +29,7 @@
#include "retval.h"
#include "io/path.h"
#include "userscorestypes.h"
#include "notation/inotation.h"
namespace mu::userscores {
class IUserScoresConfiguration : MODULE_EXPORT_INTERFACE
@ -52,6 +53,9 @@ public:
virtual void setScoresPath(const io::path& path) = 0;
virtual io::path defaultSavingFilePath(const io::path& fileName) const = 0;
virtual io::path defaultExportPath(const std::string& fileName) const = 0;
virtual io::path completeExportPath(io::path basePath, notation::INotationPtr notation, bool isMain, bool singlePage,
int pageNumber) const = 0;
virtual QColor templatePreviewBackgroundColor() const = 0;
virtual async::Notification templatePreviewBackgroundChanged() const = 0;

View file

@ -0,0 +1,235 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import MuseScore.Ui 1.0
import MuseScore.UiComponents 1.0
import MuseScore.UserScores 1.0
import "internal"
QmlDialog {
id: root
width: 640
height: 480
modal: true
title: qsTrc("userscores", "Export Score")
Rectangle {
id: content
anchors.fill: parent
color: ui.theme.popupBackgroundColor
ExportScoreModel {
id: scoresModel
}
ExportScoreSuffixModel {
id: suffixModel
}
ExportScoreSettingsModel {
id: settingsModel
}
QtObject {
id: privateProperties
readonly property int sideMargin: 24
readonly property int buttonsMargin: 24
readonly property int rectangleRadius: 4
}
Component.onCompleted: {
scoresModel.load();
suffixModel.load();
settingsModel.load();
settingsModel.changeType(fileTypeSelect.value)
}
ColumnLayout {
anchors.fill: parent
spacing: 0
StyledTextLabel {
anchors.left: parent.left
anchors.leftMargin: privateProperties.sideMargin
Layout.topMargin: privateProperties.sideMargin
text: qsTrc("global", "Export")
font: ui.theme.headerBoldFont
}
RowLayout {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.preferredHeight: childrenRect.height
spacing: 12
width: parent.width - spacing
Layout.bottomMargin: privateProperties.sideMargin
Layout.topMargin: privateProperties.sideMargin
ExportScoresListView {
id: scoresList
model: scoresModel
Layout.preferredWidth: parent.width / 2 - parent.spacing / 2 - Layout.leftMargin
Layout.fillHeight: true
}
Rectangle {
id: rightRectangle
Layout.fillWidth: true
Layout.fillHeight: true
color: ui.theme.popupBackgroundColor
Column {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 12
Column {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
spacing: 6
StyledTextLabel {
Layout.fillWidth: true
text: qsTrc("userscores", "Export To:")
font.capitalization: Font.AllUppercase
}
Row {
id: exportToPath
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
spacing: 6
TextInputField {
id: fileExportPathInput
height: 30
width: rightRectangle.width * 0.8
currentText: scoresModel.exportPath()
onCurrentTextEdited: {
scoresModel.setExportPath(newTextValue);
}
}
FlatButton {
id: browsePathButton
icon: IconCode.NEW_FILE
height:30
width: rightRectangle.width * 0.1
onClicked: {
fileExportPathInput.currentText = scoresModel.chooseExportPath();
}
}
}
}
Column {
id: exportSuffixSelection
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
spacing: 6
StyledTextLabel {
Layout.fillWidth: true
Layout.topMargin: 24
text: qsTrc("userscores", "Export As: ")
font.capitalization: Font.AllUppercase
}
StyledComboBox {
id: fileTypeSelect
property var currValue: suffixModel.getDefaultRow()
width: rightRectangle.width * 0.8
textRoleName: "suffix"
valueRoleName: "value"
model: suffixModel
currentIndex: indexOfValue(currValue)
onValueChanged: {
currValue = value;
scoresModel.setExportSuffix(valueFromModel(indexOfValue(value), "suffix"));
fileExportPathInput.currentText = scoresModel.exportPath();
settingsModel.changeType(valueFromModel(indexOfValue(value), "suffix"));
}
}
}
Column {
id: exportSettingsContainer
anchors.top: exportSuffixSelection.bottom
ExportFilePages {
model: settingsModel
}
}
}
}
}
Row {
Layout.preferredHeight: childrenRect.height
Layout.bottomMargin: privateProperties.buttonsMargin
Layout.rightMargin: privateProperties.buttonsMargin
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
spacing: 12
FlatButton {
text: qsTrc("global", "Close")
onClicked: {
root.hide()
}
}
FlatButton {
id: exportButton
text: qsTrc("global", "Export")
accentButton: true
onClicked: {
scoresModel.exportScores();
root.hide();
}
}
}
}
}
}

View file

@ -0,0 +1,187 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs 1.2
import MuseScore.Ui 1.0
import MuseScore.UiComponents 1.0
import MuseScore.UserScores 1.0
Item {
id: exportPageStack
property var model
height: 450
Column {
id: pdfPage
width: parent.width
height: parent.height
spacing: 12
ListView {
width: parent.width
height: parent.height
orientation: Qt.Vertical
model: exportPageStack.model
delegate: Column {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
spacing: 6
StyledTextLabel {
Layout.fillWidth: true
height: 30
text: friendlyNameRole
font.capitalization: Font.AllUppercase
}
Item {
id: control
height: 30
width: 150
Loader {
id: loader
property var val: valRole
anchors.fill: parent
sourceComponent: exportPageStack.componentByType(typeRole)
onLoaded: loader.item.val = loader.val
onValChanged: {
if (loader.item) {
loader.item.val = loader.val
}
}
}
Connections {
target: loader.item
function onChanged(newVal) {
exportPageStack.model.changeVal(model.index, newVal)
}
}
}
}
}
}
function componentByType(type) {
switch (type) {
case "Undefined": return textComp;
case "Bool": return boolComp;
case "Int": return intComp;
case "Double": return doubleComp;
case "String": return textComp;
case "Color": return colorComp;
}
return textComp;
}
Component {
id: textComp
TextInputField {
id: textControl
anchors.fill: parent
property var val
currentText: val
signal changed(var newVal)
onCurrentTextEdited: {
changed(newVal)
}
}
}
Component {
id: colorComp
Rectangle {
id: colorControl
property var val
signal changed(var newVal)
anchors.fill: parent
color: val
ColorDialog {
id: colorDialog
title: "Please choose a color"
onAccepted: colorControl.changed(colorDialog.color)
}
MouseArea {
anchors.fill: parent
onClicked: colorDialog.open()
}
}
}
Component {
id: intComp
IncrementalPropertyControl {
iconMode: iconModeEnum.hidden
property var val
currentValue: val.toString()
step: 1
minValue: 72
maxValue: 2400
signal changed(var newVal)
onValueEdited: {
currentValue = newValue
changed(newValue)
}
}
}
Component {
id: doubleComp
IncrementalPropertyControl {
iconMode: iconModeEnum.hidden
property var val
currentValue: val.toString()
step: 1
minValue: 72
maxValue: 2400
signal changed(var newVal)
onValueEdited: {
currentValue = newValue
changed(newValue)
}
}
}
Component {
id: boolComp
CheckBox {
id: checkbox
property var val
signal changed(var newVal)
anchors.fill: parent
checked: val ? true : false
onClicked: checkbox.changed(!checkbox.checked)
}
}
}

View file

@ -0,0 +1,48 @@
import QtQuick 2.15
import MuseScore.Ui 1.0
import MuseScore.UiComponents 1.0
ListItemBlank {
id: root
property string title: ""
property bool isMain: false
signal scoreClicked()
height: 30
onClicked: {
root.scoreClicked();
}
CheckBox {
id: scoreCheckBox
checked: isSelected
width: parent.width * 1 / 6
height: checkBoxHeight
onClicked: {
root.scoreClicked();
}
anchors.left: parent.left
anchors.leftMargin: (width - checkBoxWidth) / 2
anchors.verticalCenter: parent.verticalCenter
}
StyledTextLabel {
text: root.title
horizontalAlignment: Qt.AlignLeft
font: ui.theme.bodyFont
width: parent.width * 5 / 6
height: parent.height
anchors.left: scoreCheckBox.right
}
}

View file

@ -0,0 +1,121 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import MuseScore.Ui 1.0
import MuseScore.UiComponents 1.0
import MuseScore.UserScores 1.0
Item {
id:root
property var model
QtObject {
id: privateProperties
property var leftMargin: 12
}
Column {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: privateProperties.leftMargin
spacing: 16
Row {
width: parent.width
height: childrenRect.height
CheckBox {
id: selectAllScores
onClicked: {
checked = !checked
isIndeterminate = false
root.model.toggleAllSelections(checked);
}
width: parent.width * 1 / 6
height: checkBoxHeight
anchors.left: parent.left
anchors.leftMargin: (width - checkBoxWidth) / 2
anchors.verticalCenter: parent.verticalCenter
}
StyledTextLabel {
text: "Scores to export"
width: parent.width * 5 / 6
font.family: ui.theme.bodyBoldFont
horizontalAlignment: Qt.AlignLeft
font.capitalization: Font.AllUppercase
anchors.left: selectAllScores.right
}
}
}
RoundedRectangle {
anchors.top: header.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: privateProperties.leftMargin
anchors.rightMargin: 8
anchors.topMargin: 6
color: ui.theme.backgroundPrimaryColor
ListView {
id: scoresButtonGroup
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.width
spacing: 0
model: root.model
boundsBehavior: Flickable.StopAtBounds
interactive: height < contentHeight
clip: true
ScrollBar.vertical: StyledScrollBar {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 8
}
Connections {
target: root.model
function onSelectionChanged() {
var selections = root.model.selectionLength();
selectAllScores.checked = selections === root.model.rowCount();
selectAllScores.isIndeterminate = selections > 0 && !selectAllScores.checked;
}
}
delegate: ExportScoreListDelegate {
title: model.title
isSelected: model.isSelected
isMain: model.isMain
onScoreClicked: {
root.model.toggleSelection(model.index);
}
}
}
}
}

View file

@ -1,3 +1,4 @@
module MuseScore.UserScores
ScoresPage 1.0 ScoresPage.qml
NewScoreDialog 1.0 NewScoreDialog.qml
ExportDialog 1.0 ExportDialog.qml

View file

@ -44,6 +44,8 @@ public:
MOCK_METHOD(void, setScoresPath, (const io::path&), (override));
MOCK_METHOD(io::path, defaultSavingFilePath, (const io::path&), (const, override));
MOCK_METHOD(io::path, defaultExportPath, (const std::string&), (const, override));
MOCK_METHOD(io::path, completeExportPath, (io::path, notation::INotationPtr, bool, bool, int), (const, override));
MOCK_METHOD(QColor, templatePreviewBackgroundColor, (), (const, override));
MOCK_METHOD(async::Notification, templatePreviewBackgroundChanged, (), (const, override));

View file

@ -3,6 +3,7 @@
<file>qml/MuseScore/UserScores/qmldir</file>
<file>qml/MuseScore/UserScores/NewScoreDialog.qml</file>
<file>qml/MuseScore/UserScores/ScoresPage.qml</file>
<file>qml/MuseScore/UserScores/ExportDialog.qml</file>
<file>qml/MuseScore/UserScores/internal/AdditionalInfoView.qml</file>
<file>qml/MuseScore/UserScores/internal/GeneralInfoItem.qml</file>
<file>qml/MuseScore/UserScores/internal/KeySignatureListView.qml</file>
@ -21,5 +22,8 @@
<file>qml/MuseScore/UserScores/internal/ChooseInstrumentsAndTemplatesPage.qml</file>
<file>qml/MuseScore/UserScores/internal/ChooseTemplatePage.qml</file>
<file>qml/MuseScore/UserScores/internal/ScoreInfoPage.qml</file>
<file>qml/MuseScore/UserScores/internal/ExportFilePages.qml</file>
<file>qml/MuseScore/UserScores/internal/ExportScoresListView.qml</file>
<file>qml/MuseScore/UserScores/internal/ExportScoreListDelegate.qml</file>
</qresource>
</RCC>

View file

@ -32,9 +32,13 @@
#include "view/scorethumbnail.h"
#include "view/templatesmodel.h"
#include "view/templatepaintview.h"
#include "view/exportscoremodel.h"
#include "view/exportscoresuffixmodel.h"
#include "view/exportscoresettingsmodel.h"
#include "internal/filescorecontroller.h"
#include "internal/userscoresconfiguration.h"
#include "internal/userscoresservice.h"
#include "internal/exportscoreservice.h"
#include "internal/templatesrepository.h"
#include "internal/userscoresuiactions.h"
@ -48,6 +52,7 @@ using namespace mu::ui;
static std::shared_ptr<FileScoreController> s_fileController = std::make_shared<FileScoreController>();
static std::shared_ptr<UserScoresConfiguration> s_userScoresConfiguration = std::make_shared<UserScoresConfiguration>();
static std::shared_ptr<UserScoresService> s_userScoresService = std::make_shared<UserScoresService>();
static std::shared_ptr<ExportScoreService> s_exportScoreService = std::make_shared<ExportScoreService>();
static void userscores_init_qrc()
{
@ -65,6 +70,7 @@ void UserScoresModule::registerExports()
ioc()->registerExport<IUserScoresService>(moduleName(), s_userScoresService);
ioc()->registerExport<ITemplatesRepository>(moduleName(), new TemplatesRepository());
ioc()->registerExport<IFileScoreController>(moduleName(), s_fileController);
ioc()->registerExport<IExportScoreService>(moduleName(), s_exportScoreService);
}
void UserScoresModule::resolveImports()
@ -78,6 +84,9 @@ void UserScoresModule::resolveImports()
if (ir) {
ir->registerUri(Uri("musescore://userscores/newscore"),
ContainerMeta(ContainerType::QmlDialog, "MuseScore/UserScores/NewScoreDialog.qml"));
ir->registerUri(Uri("musescore://userscores/export"),
ContainerMeta(ContainerType::QmlDialog, "MuseScore/UserScores/ExportDialog.qml"));
}
}
@ -91,6 +100,9 @@ void UserScoresModule::registerUiTypes()
qmlRegisterType<RecentScoresModel>("MuseScore.UserScores", 1, 0, "RecentScoresModel");
qmlRegisterType<NewScoreModel>("MuseScore.UserScores", 1, 0, "NewScoreModel");
qmlRegisterType<AdditionalInfoModel>("MuseScore.UserScores", 1, 0, "AdditionalInfoModel");
qmlRegisterType<ExportScoreModel>("MuseScore.UserScores", 1, 0, "ExportScoreModel");
qmlRegisterType<ExportScoreSuffixModel>("MuseScore.UserScores", 1, 0, "ExportScoreSuffixModel");
qmlRegisterType<ExportScoreSettingsModel>("MuseScore.UserScores", 1, 0, "ExportScoreSettingsModel");
qmlRegisterType<ScoreThumbnail>("MuseScore.UserScores", 1, 0, "ScoreThumbnail");
qmlRegisterType<TemplatesModel>("MuseScore.UserScores", 1, 0, "TemplatesModel");

View file

@ -0,0 +1,234 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "exportscoremodel.h"
#include "translation.h"
#include "uicomponents/view/itemmultiselectionmodel.h"
using namespace mu::userscores;
using namespace mu::notation;
using namespace mu::uicomponents;
ExportScoreModel::ExportScoreModel(QObject* parent)
: QAbstractListModel(parent), m_selectionModel(new ItemMultiSelectionModel(this))
, m_exportPath(configuration()->defaultExportPath(masterNotation()->metaInfo().title.toStdString()))
{
connect(m_selectionModel, &ItemMultiSelectionModel::selectionChanged, this, &ExportScoreModel::selectionChanged);
}
void ExportScoreModel::load()
{
beginResetModel();
IMasterNotationPtr masterNotation = this->masterNotation();
if (!masterNotation) {
endResetModel();
return;
}
m_notations << masterNotation->notation();
for (IExcerptNotationPtr excerpt : masterNotation->excerpts().val) {
m_notations << excerpt->notation();
}
endResetModel();
}
QVariant ExportScoreModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
INotationPtr notation = m_notations[index.row()];
switch (role) {
case RoleTitle:
return notation->metaInfo().title;
case RoleIsSelected:
return m_selectionModel->isSelected(index);
case RoleIsMain:
return isMainNotation(notation);
}
return QVariant();
}
int ExportScoreModel::rowCount(const QModelIndex&) const
{
return m_notations.size();
}
QHash<int, QByteArray> ExportScoreModel::roleNames() const
{
static const QHash<int, QByteArray> roles {
{ RoleTitle, "title" },
{ RoleIsSelected, "isSelected" },
{ RoleIsMain, "isMain" }
};
return roles;
}
void ExportScoreModel::toggleSelection(int scoreIndex)
{
if (!isNotationIndexValid(scoreIndex)) {
return;
}
QModelIndex modelIndex = index(scoreIndex);
m_selectionModel->QItemSelectionModel::select(modelIndex, QItemSelectionModel::Toggle);
emit dataChanged(modelIndex, modelIndex);
}
void ExportScoreModel::toggleAllSelections(bool select)
{
QModelIndexList previousSelectedIndexes = m_selectionModel->selectedIndexes();
m_selectionModel->QItemSelectionModel::select(QItemSelection(index(0), index(m_notations.size() - 1)),
select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
QModelIndexList newSelectedIndexes = m_selectionModel->selectedIndexes();
QSet<QModelIndex> indexesToUpdate(previousSelectedIndexes.begin(), previousSelectedIndexes.end());
indexesToUpdate = indexesToUpdate.unite(QSet<QModelIndex>(newSelectedIndexes.begin(), newSelectedIndexes.end()));
for (const QModelIndex& indexToUpdate : indexesToUpdate) {
emit dataChanged(indexToUpdate, indexToUpdate);
}
}
IMasterNotationPtr ExportScoreModel::masterNotation() const
{
return context()->currentMasterNotation();
}
bool ExportScoreModel::isMainNotation(INotationPtr notation) const
{
return notation == masterNotation()->notation();
}
bool ExportScoreModel::isNotationIndexValid(int index) const
{
return index >= 0 && index < m_notations.size();
}
QList<int> ExportScoreModel::selectedRows() const
{
QList<int> result;
for (const QModelIndex& index: m_selectionModel->selectedIndexes()) {
result << index.row();
}
return result;
}
int ExportScoreModel::selectionLength() const
{
return selectedRows().size();
}
QString ExportScoreModel::exportPath() const
{
return m_exportPath.toQString();
}
void ExportScoreModel::setExportPath(QString path)
{
m_exportPath = io::path(path);
}
QString ExportScoreModel::chooseExportPath()
{
io::path requestedPath = interactive()->selectSavingFile(qtrc("userscores", "Export Score"), m_exportPath, exportFliter());
if (!requestedPath.empty()) {
m_exportPath = requestedPath;
}
return m_exportPath.toQString();
}
void ExportScoreModel::setExportSuffix(QString suffix)
{
m_exportPath = dirpath(m_exportPath) + "/" + basename(m_exportPath) + "." + suffix;
}
void ExportScoreModel::exportScores()
{
INotationPtrList notations;
for (int i : selectedRows()) {
notations.push_back(m_notations[i]);
}
exportScoreService()->exportScores(notations, m_exportPath);
}
int ExportScoreModel::pdfResolution() const
{
return imageExportConfiguration()->exportPdfDpiResolution();
}
void ExportScoreModel::setPdfResolution(const int& resolution)
{
imageExportConfiguration()->setExportPdfDpiResolution(resolution);
}
int ExportScoreModel::pngResolution() const
{
return imageExportConfiguration()->exportPngDpiResolution();
}
void ExportScoreModel::setPngResolution(const int& resolution)
{
imageExportConfiguration()->setExportPngDpiResolution(resolution);
}
bool ExportScoreModel::pngTransparentBackground() const
{
return imageExportConfiguration()->exportPngWithTransparentBackground();
}
void ExportScoreModel::setPngTransparentBackground(const bool& transparent)
{
imageExportConfiguration()->setExportPngWithTransparentBackground(transparent);
}
QString ExportScoreModel::exportFliter() const
{
io::path suffix = io::syffix(m_exportPath);
QString filter;
if (suffix == "pdf") {
filter = QObject::tr("PDF files") + " (*.pdf)";
} else if (suffix == "png") {
filter = QObject::tr("PNG images") + " (*.png)";
} else if (suffix == "svg") {
filter = QObject::tr("SVG images") + " (*.svg)";
} else if (suffix == "mp3") {
filter = QObject::tr("mp3 audio") + " (*.mp3)";
} else if (suffix == "midi") {
filter = QObject::tr("midi file") + " (*.mid, *.midi)";
}
return filter;
}

View file

@ -0,0 +1,108 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_EXPORTSCOREMODEL_H
#define MU_USERSCORES_EXPORTSCOREMODEL_H
#include "modularity/ioc.h"
#include "iinteractive.h"
#include "context/iglobalcontext.h"
#include "iuserscoresconfiguration.h"
#include "notation/inotationwritersregister.h"
#include "importexport/imagesexport/iimagesexportconfiguration.h"
#include "iexportscoreservice.h"
namespace mu::uicomponents {
class ItemMultiSelectionModel;
}
namespace mu::userscores {
class ExportScoreModel : public QAbstractListModel
{
Q_OBJECT
INJECT(userscores, framework::IInteractive, interactive)
INJECT(userscores, context::IGlobalContext, context)
INJECT(userscores, IUserScoresConfiguration, configuration)
INJECT(userscores, notation::INotationWritersRegister, writers)
INJECT(userscores, iex::imagesexport::IImagesExportConfiguration, imageExportConfiguration)
INJECT(userscores, IExportScoreService, exportScoreService)
Q_PROPERTY(int pdfResolution READ pdfResolution WRITE setPdfResolution)
Q_PROPERTY(int pngResolution READ pngResolution WRITE setPngResolution)
Q_PROPERTY(bool pngTransparentBackground READ pngTransparentBackground WRITE setPngTransparentBackground)
public:
explicit ExportScoreModel(QObject* parent = nullptr);
QVariant data(const QModelIndex& index, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void load();
Q_INVOKABLE void toggleSelection(int scoreIndex);
Q_INVOKABLE void toggleAllSelections(bool select);
Q_INVOKABLE int selectionLength() const;
Q_INVOKABLE QString exportPath() const;
Q_INVOKABLE void setExportPath(QString path);
Q_INVOKABLE QString chooseExportPath();
Q_INVOKABLE void setExportSuffix(QString suffix);
Q_INVOKABLE void exportScores();
int pdfResolution() const;
void setPdfResolution(const int& resolution);
int pngResolution() const;
void setPngResolution(const int& resolution);
bool pngTransparentBackground() const;
void setPngTransparentBackground(const bool& transparent);
signals:
void selectionChanged();
private:
enum Roles {
RoleTitle = Qt::UserRole + 1,
RoleIsSelected,
RoleIsMain
};
bool isMainNotation(notation::INotationPtr notation) const;
bool isNotationIndexValid(int index) const;
notation::IMasterNotationPtr masterNotation() const;
QList<int> selectedRows() const;
uicomponents::ItemMultiSelectionModel* m_selectionModel = nullptr;
QList<notation::INotationPtr> m_notations;
QString exportFliter() const;
io::path m_exportPath;
};
}
#endif // MU_USERSCORES_EXPORTSCOREMODEL_H

View file

@ -0,0 +1,138 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "exportscoresettingsmodel.h"
#include <QRegularExpression>
using namespace mu::userscores;
using namespace mu::framework;
ExportScoreSettingsModel::ExportScoreSettingsModel(QObject* parent)
: QAbstractListModel(parent)
{
}
QVariant ExportScoreSettingsModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || index.row() >= rowCount()) {
return QVariant();
}
const Settings::Item& item = m_currentItems.at(index.row());
switch (role) {
case SectionRole:
return QString::fromStdString(item.key.moduleName);
case KeyRole:
return QString::fromStdString(item.key.key);
case FriendlyNameRole:
return friendlyNameFromKey(QString::fromStdString(item.key.key));
case TypeRole:
return typeToString(item.value.type());
case ValRole:
return item.value.toQVariant();
}
return QVariant();
}
int ExportScoreSettingsModel::rowCount(const QModelIndex&) const
{
return m_currentItems.size();
}
QHash<int, QByteArray> ExportScoreSettingsModel::roleNames() const
{
static const QHash<int, QByteArray> roles = {
{ SectionRole, "sectionRole" },
{ KeyRole, "keyRole" },
{ FriendlyNameRole, "friendlyNameRole" },
{ TypeRole, "typeRole" },
{ ValRole, "valRole" }
};
return roles;
}
void ExportScoreSettingsModel::load()
{
beginResetModel();
m_completeItems.clear();
m_currentItems.clear();
Settings::Items items = settings()->items();
for (auto it = items.cbegin(); it != items.cend(); ++it) {
if (it->second.key.key.find("export") != std::string::npos) {
m_completeItems << it->second;
m_currentItems << it->second;
}
}
endResetModel();
}
void ExportScoreSettingsModel::changeVal(int idx, QVariant newVal)
{
Settings::Item& item = m_currentItems[idx];
Val::Type type = item.value.type();
item.value = Val::fromQVariant(newVal);
item.value.setType(type);
settings()->setValue(item.key, item.value);
emit dataChanged(index(idx), index(idx));
}
void ExportScoreSettingsModel::changeType(QString type)
{
m_currentItems.clear();
beginResetModel();
for (auto item : m_completeItems) {
if (item.key.key.find(type.toStdString()) != std::string::npos) {
m_currentItems << item;
}
}
endResetModel();
}
QString ExportScoreSettingsModel::typeToString(Val::Type t) const
{
switch (t) {
case Val::Type::Undefined: return "Undefined";
case Val::Type::Bool: return "Bool";
case Val::Type::Int: return "Int";
case Val::Type::Double: return "Double";
case Val::Type::String: return "String";
case Val::Type::Color: return "Color";
case Val::Type::Variant: return "Variant";
}
return "Undefined";
}
QString ExportScoreSettingsModel::friendlyNameFromKey(QString key) const
{
QString name = key.split("/", Qt::SkipEmptyParts).last();
name.replace(QRegularExpression("([A-Z](?=[a-z]+)|[A-Z]+(?![a-z]))"), " \\1");
return name;
}

View file

@ -0,0 +1,64 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_EXPORTSCORESETTINGSMODEL_H
#define MU_USERSCORES_EXPORTSCORESETTINGSMODEL_H
#include "modularity/ioc.h"
#include "settings.h"
#include <QAbstractListModel>
namespace mu::userscores {
class ExportScoreSettingsModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ExportScoreSettingsModel(QObject* parent = nullptr);
QVariant data(const QModelIndex& index, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void load();
Q_INVOKABLE void changeVal(int index, QVariant newVal);
Q_INVOKABLE void changeType(QString type);
private:
enum Roles {
SectionRole = Qt::UserRole + 1,
KeyRole,
FriendlyNameRole,
TypeRole,
ValRole
};
QString typeToString(Val::Type t) const;
QString friendlyNameFromKey(QString key) const;
QList<framework::Settings::Item> m_completeItems;
QList<framework::Settings::Item> m_currentItems;
};
}
#endif // MU_USERSCORES_EXPORTSCORESETTINGSMODEL_H

View file

@ -0,0 +1,85 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "exportscoresuffixmodel.h"
#include "log.h"
#include <algorithm>
using namespace mu::userscores;
const std::string ExportScoreSuffixModel::DEFAULT_EXPORT_SUFFIX("pdf");
ExportScoreSuffixModel::ExportScoreSuffixModel(QObject* parent)
: QAbstractListModel(parent)
, m_defaultSuffix(0)
{
}
void ExportScoreSuffixModel::load()
{
beginResetModel();
auto suffixes = writers()->registeredSuffixes();
m_defaultSuffix = static_cast<int>(std::find(suffixes.begin(), suffixes.end(), DEFAULT_EXPORT_SUFFIX) - suffixes.begin());
for (std::string suffix : suffixes) {
m_suffixes << QString::fromStdString(suffix);
}
endResetModel();
}
QVariant ExportScoreSuffixModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
switch (role) {
case RoleSuffix:
return m_suffixes[index.row()];
case RoleValue:
return index.row();
}
return QVariant();
}
int ExportScoreSuffixModel::rowCount(const QModelIndex&) const
{
return m_suffixes.size();
}
QHash<int, QByteArray> ExportScoreSuffixModel::roleNames() const
{
static const QHash<int, QByteArray> roles {
{ RoleSuffix, "suffix" },
{ RoleValue, "value" }
};
return roles;
}
int ExportScoreSuffixModel::getDefaultRow() const
{
return m_defaultSuffix;
}

View file

@ -0,0 +1,60 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_EXPORTSCORESUFFIXMODEL_H
#define MU_USERSCORES_EXPORTSCORESUFFIXMODEL_H
#include "modularity/ioc.h"
#include "notation/inotationwritersregister.h"
#include <QAbstractListModel>
namespace mu::userscores {
class ExportScoreSuffixModel : public QAbstractListModel
{
Q_OBJECT
INJECT(scores, notation::INotationWritersRegister, writers)
public:
static const std::string DEFAULT_EXPORT_SUFFIX;
explicit ExportScoreSuffixModel(QObject* parent = nullptr);
QVariant data(const QModelIndex& index, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void load();
Q_INVOKABLE int getDefaultRow() const;
private:
enum Roles {
RoleSuffix = Qt::UserRole + 1,
RoleValue
};
QList<QString> m_suffixes;
int m_defaultSuffix;
};
}
#endif // MU_USERSCORES_EXPORTSCORESUFFIXMODEL_H