LCOV - code coverage report
Current view: top level - src/palette/widgets - timesignaturepropertiesdialog.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 123 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "timesignaturepropertiesdialog.h"
      24              : 
      25              : #include <QRegularExpression>
      26              : 
      27              : #include "engraving/iengravingfont.h"
      28              : #include "engraving/dom/masterscore.h"
      29              : #include "engraving/dom/measure.h"
      30              : #include "engraving/dom/score.h"
      31              : #include "engraving/dom/timesig.h"
      32              : 
      33              : #include "notation/inotation.h"
      34              : #include "notation/inotationinteraction.h"
      35              : #include "notation/inotationundostack.h" // IWYU pragma: keep
      36              : 
      37              : #include "ui/view/musicalsymbolcodes.h"
      38              : #include "ui/view/widgetstatestore.h"
      39              : 
      40              : static const QString TIME_SIGNATURE_PROPERTIES_DIALOG_NAME("TimeSignaturePropertiesDialog");
      41              : 
      42              : using namespace mu::palette;
      43              : using namespace muse::ui;
      44              : using namespace mu::notation;
      45              : using namespace mu::engraving;
      46              : 
      47              : //---------------------------------------------------------
      48              : //    TimeSigProperties
      49              : //---------------------------------------------------------
      50              : 
      51            0 : TimeSignaturePropertiesDialog::TimeSignaturePropertiesDialog(QWidget* parent)
      52            0 :     : muse::ui::WidgetDialog(parent)
      53              : {
      54            0 :     setObjectName(TIME_SIGNATURE_PROPERTIES_DIALOG_NAME);
      55            0 :     setupUi(this);
      56            0 : }
      57              : 
      58            0 : TimeSignaturePropertiesDialog::~TimeSignaturePropertiesDialog()
      59              : {
      60            0 :     delete m_editedTimeSig;
      61            0 : }
      62              : 
      63            0 : void TimeSignaturePropertiesDialog::classBegin()
      64              : {
      65            0 :     groups->classBegin();
      66              : 
      67            0 :     QString musicalFontFamily = QString::fromStdString(uiConfiguration()->musicalFontFamily());
      68            0 :     int musicalFontSize = uiConfiguration()->musicalFontSize();
      69              : 
      70            0 :     QString radioButtonStyle = QString("QRadioButton { font-family: %1; font-size: %2px }")
      71            0 :                                .arg(musicalFontFamily)
      72            0 :                                .arg(musicalFontSize);
      73              : 
      74            0 :     fourfourButton->setStyleSheet(radioButtonStyle);
      75            0 :     fourfourButton->setText(musicalSymbolToString(MusicalSymbolCodes::Code::TIMESIG_COMMON));
      76            0 :     fourfourButton->setMaximumHeight(30);
      77            0 :     allaBreveButton->setStyleSheet(radioButtonStyle);
      78            0 :     allaBreveButton->setText(musicalSymbolToString(MusicalSymbolCodes::Code::TIMESIG_CUT));
      79            0 :     allaBreveButton->setMaximumHeight(30);
      80              : 
      81            0 :     setWindowFlag(Qt::WindowContextHelpButtonHint, false);
      82              : 
      83            0 :     const INotationInteractionPtr interaction = notation() ? notation()->interaction() : nullptr;
      84            0 :     EngravingItem* element = interaction ? interaction->hitElementContext().element : nullptr;
      85            0 :     IF_ASSERT_FAILED(element && element->isTimeSig()) {
      86            0 :         return;
      87              :     }
      88              : 
      89            0 :     m_originTimeSig = toTimeSig(element);
      90            0 :     m_editedTimeSig = m_originTimeSig->clone();
      91              : 
      92            0 :     zText->setText(m_editedTimeSig->numeratorString());
      93            0 :     nText->setText(m_editedTimeSig->denominatorString());
      94              :     // set validators for numerator and denominator strings
      95              :     // which only accept '+', '*' (or 'x'), '(', ')', '[', ']', digits and some time symb conventional representations
      96            0 :     QRegularExpression regex("[0-9+COXx()\\[\\]\\*\\x00A2\\x00D7\\x00D8\\x00BD\\x00BC]*");
      97            0 :     QValidator* validator = new QRegularExpressionValidator(regex, this);
      98            0 :     zText->setValidator(validator);
      99            0 :     nText->setValidator(validator);
     100              : 
     101            0 :     Fraction nominal = m_editedTimeSig->sig() / m_editedTimeSig->stretch();
     102            0 :     nominal.reduce();
     103              : 
     104              :     // TODO: fix https://musescore.org/en/node/42341
     105              :     // for now, editing of actual (local) time sig is disabled in dialog
     106              :     // but more importantly, the dialog should make it clear that this is "local" change only
     107              :     // and not normally the right way to add 7/4 to a score
     108            0 :     switch (m_editedTimeSig->timeSigType()) {
     109            0 :     case TimeSigType::NORMAL:
     110            0 :         textButton->setChecked(true);
     111            0 :         break;
     112            0 :     case TimeSigType::FOUR_FOUR:
     113            0 :         fourfourButton->setChecked(true);
     114            0 :         break;
     115            0 :     case TimeSigType::ALLA_BREVE:
     116            0 :         allaBreveButton->setChecked(true);
     117            0 :         break;
     118            0 :     case TimeSigType::CUT_BACH:
     119              : //                  cutBachButton->setChecked(true);
     120              : //                  break;
     121              :     case TimeSigType::CUT_TRIPLE:
     122              : //                  cutTripleButton->setChecked(true);
     123            0 :         break;
     124              :     }
     125              : 
     126              :     static const SymIdList prolatioList = {
     127              :         SymId::mensuralProlation1,  // tempus perfectum, prol. perfecta
     128              :         SymId::mensuralProlation2,  // tempus perfectum, prol. imperfecta
     129              :         SymId::mensuralProlation3,  // tempus perfectum, prol. imperfecta, dimin.
     130              :         SymId::mensuralProlation4,  // tempus perfectum, prol. perfecta, dimin.
     131              :         SymId::mensuralProlation5,  // tempus imperf., prol. perfecta
     132              :         SymId::mensuralProlation6,  // tempus imperf., prol. imperfecta
     133              :         SymId::mensuralProlation7,  // tempus imperf., prol. imperfecta, dimin. 1
     134              :         SymId::mensuralProlation8,  // tempus imperf., prol. imperfecta, dimin. 2
     135              :         SymId::mensuralProlation9,  // tempus imperf., prol. imperfecta, dimin. 3
     136              :         SymId::mensuralProlation10, // tempus imperf., prol. imperfecta, dimin. 4
     137              :         SymId::mensuralProlation11, // tempus imperf., prol. imperfecta, dimin. 5
     138            0 :     };
     139              : 
     140            0 :     IEngravingFontPtr symbolFont = paletteScoreProvider()->paletteScore()->engravingFont();
     141              : 
     142            0 :     otherCombo->clear();
     143            0 :     otherCombo->setStyleSheet(QString("QComboBox { font-family: \"%1 Text\"; font-size: %2px; max-height: 30px } ")
     144            0 :                               .arg(QString::fromStdString(symbolFont->family())).arg(musicalFontSize));
     145              : 
     146            0 :     int idx = 0;
     147            0 :     for (SymId prolatio : prolatioList) {
     148            0 :         const QString& str = symbolFont->toString(prolatio);
     149            0 :         if (str.size() > 0) {
     150            0 :             otherCombo->addItem(str, int(prolatio));
     151              : 
     152              :             // if time sig matches this symbol string, set as selected
     153            0 :             if (m_editedTimeSig->timeSigType() == TimeSigType::NORMAL && m_editedTimeSig->denominatorString().isEmpty()
     154            0 :                 && m_editedTimeSig->numeratorString() == str) {
     155            0 :                 textButton->setChecked(false);
     156            0 :                 otherButton->setChecked(true);
     157            0 :                 otherCombo->setCurrentIndex(idx);
     158              : 
     159              :                 // set the custom text fields to empty
     160            0 :                 zText->setText(QString());
     161            0 :                 nText->setText(QString());
     162              :             }
     163              :         }
     164            0 :         idx++;
     165            0 :     }
     166              : 
     167            0 :     Groups g = m_editedTimeSig->groups();
     168            0 :     if (g.empty()) {
     169            0 :         g = Groups::endings(m_editedTimeSig->sig()); // initialize with default
     170              :     }
     171            0 :     groups->setSig(m_editedTimeSig->sig(), g, m_editedTimeSig->numeratorString(), m_editedTimeSig->denominatorString());
     172            0 : }
     173              : 
     174            0 : void TimeSignaturePropertiesDialog::showEvent(QShowEvent* event)
     175              : {
     176            0 :     WidgetStateStore::restoreGeometry(this);
     177            0 :     QDialog::showEvent(event);
     178            0 : }
     179              : 
     180            0 : void TimeSignaturePropertiesDialog::hideEvent(QHideEvent* event)
     181              : {
     182            0 :     WidgetStateStore::saveGeometry(this);
     183            0 :     QDialog::hideEvent(event);
     184            0 : }
     185              : 
     186              : //---------------------------------------------------------
     187              : //   accept
     188              : //---------------------------------------------------------
     189              : 
     190            0 : void TimeSignaturePropertiesDialog::accept()
     191              : {
     192            0 :     auto notation = this->notation();
     193            0 :     if (!notation || !m_editedTimeSig || !m_originTimeSig) {
     194            0 :         return;
     195              :     }
     196              : 
     197            0 :     TimeSigType ts = TimeSigType::NORMAL;
     198            0 :     if (textButton->isChecked() || otherButton->isChecked()) {
     199            0 :         ts = TimeSigType::NORMAL;
     200            0 :     } else if (fourfourButton->isChecked()) {
     201            0 :         ts = TimeSigType::FOUR_FOUR;
     202            0 :     } else if (allaBreveButton->isChecked()) {
     203            0 :         ts = TimeSigType::ALLA_BREVE;
     204              :     }
     205              : 
     206            0 :     m_editedTimeSig->setProperty(mu::engraving::Pid::TIMESIG_TYPE, static_cast<int>(ts));
     207              : 
     208            0 :     if (zText->text() != m_editedTimeSig->numeratorString()) {
     209            0 :         m_editedTimeSig->setNumeratorString(zText->text());
     210              :     }
     211            0 :     if (nText->text() != m_editedTimeSig->denominatorString()) {
     212            0 :         m_editedTimeSig->setDenominatorString(nText->text());
     213              :     }
     214              : 
     215            0 :     if (otherButton->isChecked()) {
     216            0 :         IEngravingFontPtr symbolFont = m_editedTimeSig->score()->engravingFont();
     217            0 :         SymId symId = (SymId)(otherCombo->itemData(otherCombo->currentIndex()).toInt());
     218              :         // ...and set numerator to font string for symbol and denominator to empty string
     219            0 :         m_editedTimeSig->setNumeratorString(symbolFont->toString(symId));
     220            0 :         m_editedTimeSig->setDenominatorString(QString());
     221            0 :     }
     222              : 
     223            0 :     Groups g = groups->groups();
     224            0 :     m_editedTimeSig->setGroups(g);
     225              : 
     226            0 :     notation->undoStack()->prepareChanges(TranslatableString("undoableAction", "Edit time signature properties"));
     227              : 
     228              :     // Change linked mmr timesigs too
     229            0 :     for (EngravingObject* obj : m_originTimeSig->linkList()) {
     230            0 :         TimeSig* timeSig = toTimeSig(obj);
     231            0 :         if (timeSig == m_originTimeSig || (timeSig->track() == m_originTimeSig->track() && timeSig->score() == m_originTimeSig->score())) {
     232            0 :             timeSig->undoChangeProperty(Pid::TIMESIG_TYPE, int(m_editedTimeSig->timeSigType()));
     233            0 :             timeSig->undoChangeProperty(Pid::SHOW_COURTESY, m_editedTimeSig->showCourtesySig());
     234            0 :             timeSig->undoChangeProperty(Pid::NUMERATOR_STRING, m_editedTimeSig->numeratorString());
     235            0 :             timeSig->undoChangeProperty(Pid::DENOMINATOR_STRING, m_editedTimeSig->denominatorString());
     236            0 :             timeSig->undoChangeProperty(Pid::GROUP_NODES, g.nodes());
     237              :         }
     238            0 :     }
     239              : 
     240            0 :     if (m_editedTimeSig->sig() != m_originTimeSig->sig()) {
     241            0 :         notation->interaction()->addTimeSignature(m_originTimeSig->measure(), m_originTimeSig->staffIdx(), m_editedTimeSig);
     242              :     }
     243              : 
     244            0 :     m_originTimeSig->triggerLayoutAll();
     245            0 :     notation->undoStack()->commitChanges();
     246            0 :     notation->notationChanged().send(muse::RectF());
     247              : 
     248            0 :     QDialog::accept();
     249            0 : }
     250              : 
     251            0 : INotationPtr TimeSignaturePropertiesDialog::notation() const
     252              : {
     253            0 :     return projectSession()->currentNotation();
     254              : }
        

Generated by: LCOV version 2.0-1