diff --git a/src/appshell/qml/MuseScore/AppShell/AboutDialog.qml b/src/appshell/qml/MuseScore/AppShell/AboutDialog.qml index 6d8efdf363f5..49e55519b9fe 100644 --- a/src/appshell/qml/MuseScore/AppShell/AboutDialog.qml +++ b/src/appshell/qml/MuseScore/AppShell/AboutDialog.qml @@ -33,134 +33,120 @@ StyledDialogView { title: qsTrc("appshell/about", "About MuseScore Studio") - contentHeight: 424 - contentWidth: 480 + contentHeight: content.implicitHeight + contentWidth: 400 + + margins: 40 AboutModel { id: aboutModel } ColumnLayout { - anchors.fill: parent - spacing: 30 - - ColumnLayout { - id: content - - Layout.fillWidth: true - Layout.fillHeight: true - Layout.topMargin: 36 - Layout.leftMargin: 40 - Layout.rightMargin: 40 + id: content - spacing: 32 + anchors.fill: parent + spacing: 32 - Image { - id: logo - Layout.alignment: Qt.AlignHCenter + Image { + id: logo + Layout.alignment: Qt.AlignHCenter - source: "resources/mu_logo.svg" - sourceSize: Qt.size(100, 100) + source: "resources/mu_logo.svg" + sourceSize: Qt.size(100, 100) - MouseArea { - anchors.fill: parent + MouseArea { + anchors.fill: parent - property int clickCount: 0 + property int clickCount: 0 - onClicked: { - clickCount++ + onClicked: { + clickCount++ - if (clickCount % 3 == 0) { - aboutModel.toggleDevMode() - } + if (clickCount % 3 == 0) { + aboutModel.toggleDevMode() } } } + } - Column { - spacing: 8 - Layout.fillWidth: true + Column { + spacing: 8 + Layout.fillWidth: true - StyledTextLabel { - anchors.horizontalCenter: parent.horizontalCenter - text: qsTrc("appshell/about", "Version:") + " " + aboutModel.museScoreVersion() - font: ui.theme.bodyBoldFont - } + StyledTextLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTrc("appshell/about", "Version: %1").arg(aboutModel.museScoreVersion()) + font: ui.theme.bodyBoldFont + } - Row { - spacing: 4 - anchors.horizontalCenter: parent.horizontalCenter + Row { + spacing: 4 + anchors.horizontalCenter: parent.horizontalCenter - StyledTextLabel { - anchors.verticalCenter: parent.verticalCenter - text: qsTrc("appshell/about", "Revision:") + " " + aboutModel.museScoreRevision() - } + StyledTextLabel { + anchors.verticalCenter: parent.verticalCenter + text: qsTrc("appshell/about", "Revision: %1").arg(aboutModel.museScoreRevisionLink()) + } - FlatButton { - anchors.verticalCenter: parent.verticalCenter - icon: IconCode.COPY + FlatButton { + anchors.verticalCenter: parent.verticalCenter + icon: IconCode.COPY - onClicked: { - aboutModel.copyRevisionToClipboard() - } + onClicked: { + aboutModel.copyRevisionToClipboard() } } } + } - StyledTextLabel { - Layout.fillWidth: true - text: { - let museScoreUrl = aboutModel.museScoreUrl() - - //: %1 will be a link to the MuseScore website - let line1 = qsTrc("appshell/about", "Visit %1 for new versions and more information.") - .arg(`${museScoreUrl.displayName}`) + StyledTextLabel { + Layout.fillWidth: true - let line2 = qsTrc("appshell/about", "Get help with the program or contribute to its development.") - .arg(aboutModel.museScoreForumUrl().url) - .arg(aboutModel.museScoreContributionUrl().url) + text: qsTrc("appshell/about", "Build date: %1") + .arg(aboutModel.museScoreBuildDateTime()) - return line1 + "
" + line2 - } - wrapMode: Text.WordWrap - maximumLineCount: 3 - } - - StyledTextLabel { - Layout.fillWidth: true + wrapMode: Text.WordWrap + maximumLineCount: 2 + } - text: qsTrc("appshell/about", "For privacy information, see our privacy policy.") - .arg(aboutModel.museScorePrivacyPolicyUrl().url) + StyledTextLabel { + Layout.fillWidth: true + text: { + let museScoreUrl = aboutModel.museScoreUrl() - wrapMode: Text.WordWrap - maximumLineCount: 3 - } + //: %1 will be a link to the MuseScore website + let line1 = qsTrc("appshell/about", "Visit %1 for new versions and more information.") + .arg(`${museScoreUrl.displayName}`) - StyledTextLabel { - Layout.fillWidth: true - text: qsTrc("appshell/about", "Copyright © 1999-2026 MuseScore Limited and others.\nPublished under the GNU General Public License version 3.") - .arg("https://www.gnu.org/licenses/gpl-3.0.html") - .replace("\n", "
") + let line2 = qsTrc("appshell/about", "Get help with the program or contribute to its development.") + .arg(aboutModel.museScoreForumUrl().url) + .arg(aboutModel.museScoreContributionUrl().url) - wrapMode: Text.WordWrap - maximumLineCount: 3 + return line1 + "
" + line2 } + wrapMode: Text.WordWrap + maximumLineCount: 3 } - RowLayout { - Layout.alignment: Qt.AlignRight - Layout.rightMargin: 16 - Layout.bottomMargin: 16 + StyledTextLabel { + Layout.fillWidth: true - spacing: 12 + text: qsTrc("appshell/about", "For privacy information, see our privacy policy.") + .arg(aboutModel.museScorePrivacyPolicyUrl().url) - FlatButton { - text: qsTrc("global", "OK") + wrapMode: Text.WordWrap + maximumLineCount: 3 + } - onClicked: { - root.hide() - } - } + StyledTextLabel { + Layout.fillWidth: true + text: qsTrc("appshell/about", "Copyright © 1999-2026 MuseScore Limited and others.\nPublished under the GNU General Public License version 3.") + .arg("https://www.gnu.org/licenses/gpl-3.0.html") + .replace("\n", "
") + + wrapMode: Text.WordWrap + maximumLineCount: 3 } } } diff --git a/src/appshell/qml/MuseScore/AppShell/aboutmodel.cpp b/src/appshell/qml/MuseScore/AppShell/aboutmodel.cpp index f9c57e57a894..cda4783b3073 100644 --- a/src/appshell/qml/MuseScore/AppShell/aboutmodel.cpp +++ b/src/appshell/qml/MuseScore/AppShell/aboutmodel.cpp @@ -30,6 +30,9 @@ using namespace mu::appshell; +//! NOTE: Should match value in projectpropertiesmodel.cpp +static const QString GITHUB_REVISION_URL("https://github.com/musescore/MuseScore/commit/"); + AboutModel::AboutModel(QObject* parent) : QObject(parent), muse::Contextable(muse::iocCtxForQmlObject(this)) { @@ -43,9 +46,33 @@ QString AboutModel::museScoreVersion() const : version; } -QString AboutModel::museScoreRevision() const +QString AboutModel::museScoreRevisionLink() const +{ + const QString rev = QString::fromStdString(configuration()->museScoreRevision()); + if (rev.isEmpty()) { + return muse::qtrc("global", "Unknown"); + } + const QString revUrl = GITHUB_REVISION_URL + rev; + return QString("%2").arg(revUrl, rev); +} + +QString AboutModel::museScoreBuildDateTime() const { - return QString::fromStdString(configuration()->museScoreRevision()); + auto buildDateISO = []() -> QString { + // Attempt to convert __DATE__ into ISO 8601 format (YYYY-MM-DD) + QDate date = QDate::fromString(__DATE__, "MMM dd yyyy"); + if (!date.isValid()) { + date = QDate::fromString(__DATE__, "MMM d yyyy"); + } + return date.isValid() ? date.toString(Qt::ISODate) : __DATE__; + }; + + QString dateTime; + dateTime += buildDateISO(); + dateTime += " "; + dateTime += __TIME__; + + return dateTime; } QVariantMap AboutModel::museScoreUrl() const @@ -86,16 +113,23 @@ QVariantMap AboutModel::musicXMLLicenseDeedUrl() const void AboutModel::copyRevisionToClipboard() const { - QGuiApplication::clipboard()->setText( - QString("OS: %1, Arch.: %2, MuseScore Studio version (%3-bit): %4-%5, revision: github-musescore-musescore-%6") - .arg(QSysInfo::prettyProductName() - + ((QSysInfo::productType() == "windows" && (QSysInfo::productVersion() == "10" || QSysInfo::productVersion() == "11")) - ? " or later" : "")) - .arg(QSysInfo::currentCpuArchitecture()) - .arg(QSysInfo::WordSize) - .arg(application()->version().toString()) - .arg(application()->build()) - .arg(application()->revision())); + QString osName = QSysInfo::prettyProductName(); + if (QSysInfo::productType() == "windows" && (QSysInfo::productVersion() == "10" || QSysInfo::productVersion() == "11")) { + osName += " or later"; + } + + QString forClipboard = QString("OS: %1, Arch.: %2, MuseScore Studio version (%3-bit): %4-%5") + .arg(osName, QSysInfo::currentCpuArchitecture()) + .arg(QSysInfo::WordSize) + .arg(application()->version().toString(), application()->build()); + + const QString rev = QString::fromStdString(configuration()->museScoreRevision()); + if (!rev.isEmpty()) { + const QString revUrl = GITHUB_REVISION_URL + rev; + forClipboard += QString(", revision: [%1](%2)").arg(rev, revUrl); + } + + QGuiApplication::clipboard()->setText(forClipboard); } void AboutModel::toggleDevMode() diff --git a/src/appshell/qml/MuseScore/AppShell/aboutmodel.h b/src/appshell/qml/MuseScore/AppShell/aboutmodel.h index 8b0652decc8a..dfe186f59e6a 100644 --- a/src/appshell/qml/MuseScore/AppShell/aboutmodel.h +++ b/src/appshell/qml/MuseScore/AppShell/aboutmodel.h @@ -49,7 +49,8 @@ class AboutModel : public QObject, public muse::Contextable explicit AboutModel(QObject* parent = nullptr); Q_INVOKABLE QString museScoreVersion() const; - Q_INVOKABLE QString museScoreRevision() const; + Q_INVOKABLE QString museScoreRevisionLink() const; + Q_INVOKABLE QString museScoreBuildDateTime() const; Q_INVOKABLE QVariantMap museScoreUrl() const; Q_INVOKABLE QVariantMap museScoreForumUrl() const; Q_INVOKABLE QVariantMap museScoreContributionUrl() const; diff --git a/src/engraving/rendering/score/headerfooterlayout.cpp b/src/engraving/rendering/score/headerfooterlayout.cpp index beb823c63bd0..68229050401e 100644 --- a/src/engraving/rendering/score/headerfooterlayout.cpp +++ b/src/engraving/rendering/score/headerfooterlayout.cpp @@ -357,14 +357,15 @@ TextBlock HeaderFooterLayout::replaceTextMacros(LayoutContext& ctx, const Page* break; case 'r': if (page->score()->dirty()) { - static String revision; // FIXME - newFragments.back().text += revision; + newFragments.back().text += application()->revision(); } else { int rev = page->score()->mscoreRevision(); - if (rev > 99999) { // MuseScore 1.3 is decimal 5702, 2.0 and later uses a 7-digit hex SHA - newFragments.back().text += String::number(rev, 16); - } else { + if (rev > 0 && rev <= 5709) { // MuseScore 1.x and earlier used decimal numbers, referring to a commit on SourceForge.net, the largest being 5709 newFragments.back().text += String::number(rev, 10); + } else if (rev > 0xffffff) { // MuseScore 2.0 and later use a >= 7-digit hex SHA, referring to a commit on GitHub.com + newFragments.back().text += String::number(rev, 16); + } else { // unknown, like in a self-built development version, or in a very old version of MuseScore before the revision number was tracked + newFragments.back().text += String::number(rev, 10); // or "" or "Unknown"? } } break; diff --git a/src/project/qml/MuseScore/Project/internal/Properties/projectpropertiesmodel.cpp b/src/project/qml/MuseScore/Project/internal/Properties/projectpropertiesmodel.cpp index 7adf125ed6af..28041627c076 100644 --- a/src/project/qml/MuseScore/Project/internal/Properties/projectpropertiesmodel.cpp +++ b/src/project/qml/MuseScore/Project/internal/Properties/projectpropertiesmodel.cpp @@ -32,6 +32,10 @@ using namespace muse::modularity; using namespace mu::notation; using namespace mu::project; +//! NOTE: Should match value in aboutmodel.cpp +static const QString GITHUB_REVISION_URL("https://github.com/musescore/MuseScore/commit/"); +static const QString SOURCEFORGE_REVISION_URL("https://sourceforge.net/p/mscore/code/"); + ProjectPropertiesModel::ProjectPropertiesModel(QObject* parent) : QAbstractListModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this)) { @@ -159,15 +163,23 @@ QString ProjectPropertiesModel::version() const QString ProjectPropertiesModel::revision() const { - int rev = m_projectMetaInfo.musescoreRevision; - QString revision; - if (rev > 99999) { // MuseScore 1.3 is decimal 5702, 2.0 and later uses a 7-digit hex SHA - revision = QString::number(rev, 16); - } else { - revision = QString::number(rev, 10); + const int rev = m_projectMetaInfo.musescoreRevision; + if (rev > 0 && rev <= 5709) { + // MuseScore 1.x and earlier used decimal numbers, referring to a commit on SourceForge.net, the largest being 5709 + const QString revText = QString::number(rev, 10); + const QString revUrl = SOURCEFORGE_REVISION_URL + revText; + return QString("%2").arg(revUrl, revText); + } + + if (rev > 0xffffff) { + // MuseScore 2.0 and later use a >= 7-digit hex SHA, referring to a commit on GitHub.com + const QString revText = QString::number(rev, 16); + const QString revUrl = GITHUB_REVISION_URL + revText; + return QString("%2").arg(revUrl, revText); } - return revision; + // unknown, like in a self-built development version, or in a very old version of MuseScore before the revision number was tracked + return muse::qtrc("global", "Unknown"); } QString ProjectPropertiesModel::apiLevel() const