Newer
Older
/***************************************************************************
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* 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, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "tnootkaqml.h"
#include "instruments/tbandoneonbg.h"
#include "instruments/tguitarbg.h"
#include "instruments/tpianobg.h"
#include "instruments/tsaxbg.h"
#include "music/timportscore.h"
#include "music/tkeysignature.h"
#include "music/tnamestylefilter.h"
#include "music/ttuneobject.h"
#include "nootkaconfig.h"
#include "qtr.h"
#include "score/taddnoteitem.h"
#include "score/tdummychord.h"
#include "score/tmelodypreview.h"
#include "score/tnoteitem.h"
#include "score/tnotepair.h"
#include "score/tscoreobject.h"
#include "score/tstaffitem.h"
#include "score/tstafflines.h"
SeeLook
committed
#include "taction.h"
#include "tcolor.h"
#include "tglobals.h"
#include "tmtr.h"
#include "tpath.h"
#if defined(Q_OS_ANDROID)
#include <Android/tandroid.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qrandom.h>
#include <QtCore/qtimer.h>
#include <QtGui/qdesktopservices.h>
#include <QtGui/qpalette.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtWidgets/qapplication.h>
#include "Android/tfiledialog.h"
#include <QtCore/qdebug.h>
TnootkaQML *TnootkaQML::m_instance = nullptr;
TnootkaQML::TnootkaQML(QObject *parent)
: QObject(parent)
{
if (m_instance) {
qDebug() << "TnootkaQML instance already exists!";
return;
}
m_instance = this;
qRegisterMetaType<Trhythm>();
qRegisterMetaType<Tmeter>();
qRegisterMetaType<Tclef>();
qRegisterMetaType<Ttune>();
qmlRegisterType<TscoreObject>("Score", 1, 0, "TscoreObject");
qmlRegisterType<TstaffItem>("Score", 1, 0, "TstaffItem");
qmlRegisterType<TnoteItem>("Score", 1, 0, "TnoteItem");
qmlRegisterType<TstaffLines>("Score", 1, 0, "TstaffLines");
qmlRegisterType<TaddNoteItem>("Score", 1, 0, "TaddNoteItem");
qmlRegisterType<TmelodyPreview>("Score", 1, 0, "TmelodyPreview");
qmlRegisterUncreatableType<TmelodyPart>("Score", 1, 0, "TmelodyPart", QStringLiteral("You cannot create an instance of the TcommonInstrument."));
qmlRegisterType<TdummyChord>("Score", 1, 0, "TdummyChord");
qmlRegisterUncreatableType<TcommonInstrument>("Nootka",
1,
0,
"TcommonInstrument",
QStringLiteral("You cannot create an instance of the TcommonInstrument."));
qmlRegisterType<TguitarBg>("Nootka", 1, 0, "TguitarBg");
qmlRegisterType<TpianoBg>("Nootka", 1, 0, "TpianoBg");
qmlRegisterType<TbandoneonBg>("Nootka", 1, 0, "TbandoneonBg");
qmlRegisterType<TsaxBg>("Nootka", 1, 0, "TsaxBg");
qmlRegisterType<Taction>("Nootka", 1, 0, "Taction");
qmlRegisterUncreatableType<TnootkaQML>("Nootka", 1, 0, "Nootka", QStringLiteral("You cannot create an instance of the TnootkaQML."));
qmlRegisterType<TtuneObject>("Nootka", 1, 0, "TtuneObject");
TnootkaQML::~TnootkaQML()
{
m_instance = nullptr;
// #################################################################################################
// ################### INVOKABLE METHODS ############################################
// #################################################################################################
QString TnootkaQML::version()
{
if (qApp->arguments().last().contains(QLatin1String("--no-version")))
return QString();
else
return NOOTKA_VERSION;
Tclef TnootkaQML::clef(int type)
{
return Tclef(static_cast<Tclef::EclefType>(type));
Tmeter TnootkaQML::meter(int m)
{
return Tmeter(static_cast<Tmeter::Emeter>(m));
Tnote TnootkaQML::note(int pitch, int octave, int alter, int rhythm, bool rest, bool dot)
{
return Tnote(static_cast<char>(pitch),
static_cast<char>(octave),
static_cast<char>(alter),
Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
}
Tnote TnootkaQML::note(const Tnote &n, int rhythm, bool rest, bool dot)
{
return Tnote(n, Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
Tnote TnootkaQML::note(const Tnote &n, const Trhythm &r)
{
return Tnote(n, r);
}
Tnote TnootkaQML::note(int chroma, bool sharp)
{
Tnote n(static_cast<short>(chroma));
if (!sharp && (n.alter() != 0 || (n.alter() == 0 && n.note() != 3 && n.note() != 7))) // but skip e and b - otherwise they become fb or cb
n = n.showWithFlat();
return n;
}
Tnote TnootkaQML::setUpperStaff(Tnote n, bool onUpper)
{
n.setOnUpperStaff(onUpper);
return n;
Tnote TnootkaQML::emptyNote()
{
return Tnote();
}
Tnote TnootkaQML::transpose(Tnote n, int semitones)
{
n.transpose(semitones);
return n;
Trhythm TnootkaQML::rhythm(int rtm, bool rest, bool dot, bool triplet)
{
return Trhythm(static_cast<Trhythm::Erhythm>(rtm), rest, dot, triplet);
}
/**
* It returns glyphs of 'Nootka' font in contrary to TnoteObject::getHeadText()
*/
QString TnootkaQML::rhythmText(const Trhythm &r)
{
if (r.rhythm() == Trhythm::NoRhythm)
return QStringLiteral("z"); // just black note-head
QString out;
if (r.isRest())
out = QString(QChar(0xe106 + static_cast<int>(r.rhythm())));
else
out = QString(QChar(66 + static_cast<int>(r.rhythm())));
if (r.hasDot())
out += QStringLiteral(".");
return out;
}
QString TnootkaQML::noteName(const Tnote &n, int style, bool showOctave)
{
// Tnote::toText() method returns only names in user preferred style according to settings
// To cheat it and force note name in any given style we are resetting pointer of is7th_B,
// then Tnote skips filtering of a style during name generation.
auto tmpPtr = TnameStyleFilter::is7th_B();
TnameStyleFilter::setStyleFilter(nullptr, TnameStyleFilter::solfegeStyle());
auto name = n.toText(static_cast<Tnote::EnameStyle>(style), showOctave);
TnameStyleFilter::setStyleFilter(tmpPtr, TnameStyleFilter::solfegeStyle()); // restore is7th_B settings
return name;
}
* So far this method doesn't cheat @p TnameStyleFilter, so improper style for 7th note will be fixed
QString TnootkaQML::styledName(const Tnote &n, int style, bool showOctave)
{
auto tmpStyle = Tnote::defaultStyle;
Tnote::defaultStyle = static_cast<Tnote::EnameStyle>(style);
auto name = n.styledName(showOctave);
Tnote::defaultStyle = tmpStyle;
return name;
QString TnootkaQML::majorKeyName(int key)
{
return TkeySignature(static_cast<char>(key)).getMajorName();
}
QString TnootkaQML::minorKeyName(int key)
{
return TkeySignature(static_cast<char>(key)).getMinorName();
QString TnootkaQML::majAndMinKeyName(int key)
{
return majorKeyName(key) + QLatin1String("<br>") + minorKeyName(key);
}
QStringList TnootkaQML::keyComboModel()
{
QStringList model;
for (int i = -7; i < 8; i++) {
TkeySignature k(i);
model << QLatin1String("(") + k.accidNumber() + QLatin1String(") ") + k.getMajorName() + QLatin1String(" / ") + k.getMinorName();
}
return model;
/**
* Returns difference in semitones between keys @p key1 and @p key2
* expressed in values [-7 to 7] [7b to 7#]
*/
int TnootkaQML::keysDiff(int key1, int key2)
{
TkeySignature k1(static_cast<char>(key1));
return k1.difference(TkeySignature(static_cast<char>(key2)));
bool TnootkaQML::isAndroid()
{
#if defined(Q_OS_ANDROID)
return true;
return false;
#endif
}
bool TnootkaQML::isWindows()
{
#if defined(Q_OS_WIN)
return true;
#else
return false;
#endif
}
bool TnootkaQML::isMac()
{
#if defined(Q_OS_MACOS)
return true;
#else
return false;
#endif
}
QStringList TnootkaQML::guitarTunings()
{
QStringList tunList;
int start = static_cast<int>(Ttune::Standard_EADGBE);
for (int t = start; t < start + 5; ++t)
tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
tunList << QApplication::translate("InstrumentPage", "Custom tuning");
return tunList;
}
QStringList TnootkaQML::bassTunings()
{
QStringList tunList;
int start = static_cast<int>(Ttune::Bass4_EADG);
for (int t = start; t < start + 4; ++t)
tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
tunList << QApplication::translate("InstrumentPage", "Custom tuning");
return tunList;
}
QStringList TnootkaQML::ukuleleTunings()
{
QStringList tunList;
tunList << Ttune::definedName(Ttune::Ukulele_GCEA) << Ttune::definedName(Ttune::Ukulele_Raised);
tunList << qTR("InstrumentPage", "Custom tuning");
return tunList;
}
/**
* Tuning names text model - for instruments with tunings
*/
QStringList TnootkaQML::tuningModel(int instr)
{
switch (static_cast<Tinstrument::Etype>(instr)) {
case Tinstrument::ClassicalGuitar:
case Tinstrument::ElectricGuitar:
return guitarTunings();
case Tinstrument::BassGuitar:
return bassTunings();
case Tinstrument::Ukulele:
return ukuleleTunings();
default:
return QStringList();
}
Ttune TnootkaQML::tuning(int tuningType)
{
if (tuningType > -1) {
if (tuningType == 0)
return Ttune::stdTune;
if (tuningType < 5)
return Ttune::tunes[tuningType - 1];
if (tuningType > 99 && tuningType < 104)
return Ttune::bassTunes[tuningType - 100];
if (tuningType == 110)
return Ttune::ukuleleGCEA;
if (tuningType == 111)
return Ttune::ukuleleRaised;
}
return Ttune();
}
/**
* When third note @s3 is valid the tuning represents real guitar tuning
* otherwise it is an instrument scale
*/
Ttune TnootkaQML::tuning(const Tnote &s1, const Tnote &s2, const Tnote &s3, const Tnote &s4, const Tnote &s5, const Tnote &s6)
{
return Ttune(QApplication::translate("InstrumentPage", "Custom tuning"), s1, s2, s3, s4, s5, s6, s3.isValid() ? Ttune::Custom : Ttune::Scale);
Ttune TnootkaQML::defaultScale(int instr)
{
switch (static_cast<Tinstrument::Etype>(instr)) {
case Tinstrument::ClassicalGuitar:
case Tinstrument::ElectricGuitar:
return Ttune::stdTune;
case Tinstrument::BassGuitar:
return Ttune::bassTunes[0];
case Tinstrument::Piano:
return tuning(Tnote(-11), Tnote(49), Tnote(), Tnote(), Tnote(), Tnote());
case Tinstrument::Bandoneon:
return tuning(Tnote(-11), Tnote(48), Tnote(), Tnote(), Tnote(), Tnote());
case Tinstrument::AltSax:
case Tinstrument::TenorSax:
return tuning(Tnote(11), Tnote(49), Tnote(), Tnote(), Tnote(), Tnote());
return Ttune::ukuleleGCEA;
default: // NoInstrument and any unexpected case
return tuning(Tnote(10), Tnote(54), Tnote(), Tnote(), Tnote(), Tnote());
}
Tinstrument TnootkaQML::instr(int type)
{
return Tinstrument(static_cast<Tinstrument::Etype>(type < 0 || type > INSTR_COUNT - 1 ? 0 : type));
QString TnootkaQML::getXmlToOpen()
{
QString openFile;
#if defined(Q_OS_ANDROID)
if (GLOB->lastXmlDir().isEmpty())
GLOB->setLastXmlDir(Tandroid::getExternalPath());
openFile = TfileDialog::getOpenFileName(GLOB->lastXmlDir(), QStringLiteral("xml|musicxml|mxl"));
SeeLook
committed
#else
openFile = TfileDialog::getOpenFileName(qApp->translate("TmainScoreObject", "Open melody file"),
GLOB->lastXmlDir(),
qApp->translate("TmainScoreObject", "MusicXML file")
+ QLatin1String(": *.xml, *.musicxml, *.mxl (*.xml *.musicxml *.mxl);;") + QLatin1String(" *.xml (*.xml);;")
+ QLatin1String(" *.musicxml (*.musicxml);;") + qApp->translate("TmainScoreObject", "Compressed MusicXML file")
+ QLatin1String(" *.mxl (*.mxl);;"));
SeeLook
committed
#endif
if (!openFile.isEmpty())
GLOB->setLastXmlDir(QFileInfo(openFile).absoluteDir().path());
return openFile;
}
QString TnootkaQML::getXmlToSave(const QString &fileName)
{
QString saveFile;
QString filter;
#if defined(Q_OS_ANDROID)
if (GLOB->lastXmlDir().isEmpty())
GLOB->setLastXmlDir(Tandroid::getExternalPath());
saveFile = TfileDialog::getSaveFileName(GLOB->lastXmlDir() + QLatin1String("/") + fileName, QStringLiteral("musicxml|mxl"));
SeeLook
committed
#else
saveFile = TfileDialog::getSaveFileName(qApp->translate("TmainScoreObject", "Save melody as:"),
GLOB->lastXmlDir() + QDir::separator() + fileName,
qApp->translate("TmainScoreObject", "Compressed MusicXML file") + QLatin1String(" *.mxl (*.mxl);;")
+ qTR("TmainScoreObject", "MusicXML file") + QLatin1String(" (*.musicxml *.xml)"),
SeeLook
committed
#endif
if (!saveFile.isEmpty()) {
GLOB->setLastXmlDir(QFileInfo(saveFile).absoluteDir().path());
// if the dialog does not retrieve an extension for the file we deduct it from the filter
if (QFileInfo(saveFile).suffix().isEmpty()) {
if (filter.endsWith(QLatin1String("(*.mxl)")))
saveFile += QLatin1String(".mxl");
else
saveFile += QLatin1String(".musicxml");
}
return saveFile;
}
QString TnootkaQML::pix(const QString &imageFileName)
{
return Tpath::pix(imageFileName);
SeeLook
committed
QString TnootkaQML::pix(const char *imageName, int height)
{
return pixToHtml(QString(imageName), height);
QString TnootkaQML::TR(const QString &context, const QString &text, const QString &disambiguation, int n)
{
return qTR(qPrintable(context), qPrintable(text), qPrintable(disambiguation), n);
}
QString TnootkaQML::getOnlineDoc(const QString &post)
{
return QString("<p align=\"right\"><a href=\"https://nootka.sourceforge.io/index.php/%1/\">").arg(post)
+ QGuiApplication::translate("ThelpDialogBase", "Open online documentation") + QLatin1String("</a> </p>");
QColor TnootkaQML::alpha(const QColor &c, int a)
{
return Tcolor::alpha(c, a);
SeeLook
committed
}
QColor TnootkaQML::randomColor(int alpha, int level)
{
return QColor(QRandomGenerator::global()->bounded(level), QRandomGenerator::global()->bounded(level), QRandomGenerator::global()->bounded(level), alpha);
SeeLook
committed
}
QColor TnootkaQML::invert(const QColor &c)
{
return Tcolor::invert(c);
}
qreal TnootkaQML::hue(const QColor &c) const
{
return c.hueF();
}
qreal TnootkaQML::saturation(const QColor &c) const
{
return c.saturationF();
}
qreal TnootkaQML::lightness(const QColor &c) const
{
return c.saturationF();
}
int TnootkaQML::factor()
{
#if defined(Q_OS_ANDROID)
// Set Android font according to screen size/density
return qRound(Tmtr::fingerPixels() * 0.35 * GLOB->guiScale());
#else
// but use system font size on desktops
return qRound(Tmtr::systemFont.pointSize() / 72.0 * qApp->primaryScreen()->logicalDotsPerInch() * GLOB->guiScale());
#endif
}
QString TnootkaQML::fontFamily()
{
return Tmtr::systemFont.family();
int TnootkaQML::fingerPixels()
{
return Tmtr::fingerPixels();
}
int TnootkaQML::shortScreenSide()
{
return Tmtr::shortScreenSide();
}
int TnootkaQML::longScreenSide()
{
return Tmtr::longScreenSide();
}
QString TnootkaQML::pixToHtml(const QString &pixName, int height)
{
if (height == 0)
return QString("<img src=\"%1\">").arg(pixName);
QPixmap pix;
if (!pix.load(Tpath::img(qPrintable(pixName))))
return QString();
QByteArray byteArray;
QBuffer buffer(&byteArray);
pix.scaled(qRound(height * (static_cast<qreal>(pix.width()) / static_cast<qreal>(pix.height()))), height, Qt::KeepAspectRatio, Qt::SmoothTransformation)
.save(&buffer, "PNG");
return QString("<img src=\"data:image/png;base64,") + byteArray.toBase64() + "\"/>";
}
void TnootkaQML::openDocLink(const QString &lnk)
{
QDesktopServices::openUrl(QUrl(QLatin1String("https://nootka.sourceforge.io/index.php/") + lnk));
qreal TnootkaQML::bound(qreal min, qreal val, qreal max)
{
return qBound(min, val, max);
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
void TnootkaQML::setQmlEngine(QQmlEngine *e)
{
m_qmlEngine = e;
if (GLOB->isFirstRun) // Wizard - actions are not needed yet
return;
connect(GLOB, &Tglobals::newerVersion, this, &TnootkaQML::warnNewerVersionSlot);
if (m_scoreAct) {
delete m_scoreAct;
delete m_settingsAct;
delete m_levelAct;
delete m_chartsAct;
delete m_melodyAct;
delete m_examAct;
delete m_aboutAct;
}
m_settingsAct = new Taction(QApplication::translate("TtoolBar", "Settings"), QStringLiteral("systemsettings"), this);
connect(m_settingsAct, &Taction::triggered, this, &TnootkaQML::settingsActTriggered);
m_settingsAct->setTip(QApplication::translate("TtoolBar", "Application preferences"), QQuickItem::TopRight);
m_levelAct = new Taction(QApplication::translate("TtoolBar", "Level"), QStringLiteral("levelCreator"), this);
connect(m_levelAct, &Taction::triggered, this, &TnootkaQML::levelActTriggered);
m_levelAct->setTip(QApplication::translate("TtoolBar", "Level creator"), QQuickItem::TopRight);
m_chartsAct = new Taction(QApplication::translate("TtoolBar", "Analyze"), QStringLiteral("charts"), this);
connect(m_chartsAct, &Taction::triggered, this, &TnootkaQML::chartsActTriggered);
m_chartsAct->setTip(tr("Analysis of exam results"), QQuickItem::TopRight);
m_scoreAct = new Taction(QApplication::translate("TmainScoreObject",
"Score",
"it could be 'notation', 'staff' or whatever is associated with that 'place to display musical notes' and "
"this the name is quite short and looks well."),
QStringLiteral("score"),
this);
m_scoreAct->setBgColor(qApp->palette().highlight().color());
connect(m_scoreAct, &Taction::triggered, this, &TnootkaQML::scoreActTriggered);
m_scoreAct->setTip(QApplication::translate("TmainScoreObject", "Manage and navigate the score."), QQuickItem::TopRight);
m_melodyAct = new Taction(QApplication::translate("TmainScoreObject", "Melody"), QStringLiteral("melody"), this);
connect(m_melodyAct, &Taction::triggered, this, &TnootkaQML::melodyActTriggered);
m_melodyAct->setTip(QApplication::translate("TmainScoreObject", "Open, save, generate and play a melody."), QQuickItem::TopRight);
m_examAct = new Taction(QApplication::translate("TtoolBar", "Lessons"), QStringLiteral("startExam"), this);
connect(m_examAct, &Taction::triggered, this, &TnootkaQML::examActTriggered);
m_examAct->setTip(QApplication::translate("TtoolBar", "Start exercises or an exam"), QQuickItem::TopRight);
m_aboutAct = new Taction(this);
connect(m_aboutAct, &Taction::triggered, this, &TnootkaQML::aboutActTriggered);
m_messageColor = qApp->palette().highlight().color();
SeeLook
committed
/**
* Opening files from command line argument starts here, but it is a bit clumsy:
* - for Music XML we are sending @p wantOpenFile() and main score will handle that,
SeeLook
committed
* - but for exam and level files only @p wantOpenFile() signal is emitted
* with 700ms delay to give main window time for initialize
SeeLook
committed
* - then @p MainWindow.qml handles it and creates @p DialogLoader.qml
* - 'dialog loader' invokes @p TdialogLoaderObject::openFile() of its object
* - and there the file is distinguished (exam or level) and appropriate signal is emitted
* - again, 'dialog loader' handles those signals and creates 'exam executor' or 'level creator' apparently
*/
void TnootkaQML::openFile(const QString &runArg)
{
if (GLOB->isExam()) {
qDebug() << "--- Exam or exercise is running. File cannot be opened! ---";
return;
}
if (QFile::exists(runArg)) {
QFile file(runArg);
auto ext = QFileInfo(file).suffix();
if (ext == QLatin1String("xml") || ext == QLatin1String("musicxml") || ext == QLatin1String("mxl")) {
auto fullPath = QDir(file.fileName()).absolutePath();
emit wantOpenXml(fullPath);
} else {
QTimer::singleShot(700, this, [=] {
emit GLOB->wantOpenFile(runArg);
});
}
SeeLook
committed
}
}
void TnootkaQML::setMessageColor(const QColor &mc)
{
if (m_messageColor != mc) {
m_messageColor = mc;
emit messageColorChanged();
}
void TnootkaQML::setStatusTip(const QString &statusText, int tipPos, bool richText)
{
if ((GLOB->showHints() && (!m_messageTimer || (m_messageTimer && !m_messageTimer->isActive()))))
emit statusTip(statusText, tipPos, richText);
}
void TnootkaQML::showTimeMessage(const QString &message, int time, int pos, bool richText)
{
if (!m_messageTimer) {
m_messageTimer = new QTimer(this);
m_messageTimer->setSingleShot(true);
connect(m_messageTimer, &QTimer::timeout, this, [=] {
emit statusTip(QString(), m_messagePos, false);
QTimer::singleShot(300, this, [=] {
setMessageColor(qApp->palette().highlight().color());
}); // restore default status background color
});
}
m_messagePos = pos;
if (m_messageTimer->isActive())
m_messageTimer->stop();
emit statusTip(message, pos, richText);
m_messageTimer->start(time);
bool TnootkaQML::messageTimerActive() const
{
return m_messageTimer ? m_messageTimer->isActive() : false;
QString TnootkaQML::qaTypeText(int qaType)
{
switch (qaType) {
case 0:
return QApplication::translate("Texam", "as note on the staff");
case 1:
return QApplication::translate("Texam", "as note name");
case 2:
return QApplication::translate("Texam", "on instrument");
case 3:
return QApplication::translate("Texam", "as played sound");
default:
return QString();
}
}
QString TnootkaQML::note7translated() const
{
return QApplication::translate("Notation",
"b",
"Give here a name of 7-th note preferred in your country. But only 'b' or 'h' not 'si' or something worst...");
}
QString TnootkaQML::keyNameTranslated() const
{
return QApplication::translate(
"Notation",
"letters",
"DO NOT TRANSLATE IT DIRECTLY. Put here 'letters' or 'solfege' This is country preferred style of naming key signatures. 'letters' means "
"C-major/a-minor names ('major' & 'minor' also are translated by you), 'solfege' means Do-major/La-minor names");
}
/**
* Call method of @p MainWindow.qml to obtain creted popup reference
* then set name of this unsupported file
*/
void TnootkaQML::warnNewerVersionSlot(const QString &fileName)
{
QTimer::singleShot(400, this, [=] {
if (m_warnNewerPopup) { // when popup already exists just append file name to previous name(s)
m_warnNewerPopup->setProperty("fName", m_warnNewerPopup->property("fName").toString() + QLatin1String("<br>") + fileName);
} else {
auto nootWin = qobject_cast<QQmlApplicationEngine *>(m_qmlEngine)->rootObjects().first();
if (nootWin && QString(nootWin->metaObject()->className()).contains("MainWindow_QMLTYPE")) {
QVariant popVar;
QMetaObject::invokeMethod(nootWin, "newerVerPop", Q_RETURN_ARG(QVariant, popVar));
m_warnNewerPopup = qvariant_cast<QObject *>(popVar);
if (m_warnNewerPopup) {
connect(m_warnNewerPopup, &QQuickItem::destroyed, this, [=] {
m_warnNewerPopup = nullptr;
});
m_warnNewerPopup->setProperty("fName", fileName);
}
}
// #################################################################################################
// ################### CONNECTIONS NODE ############################################
// #################################################################################################
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
void TnootkaQML::noteStarted(const Tnote &n)
{
Tnote note = n;
if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
note = note.showWithFlat();
m_ignoreScore = true;
if (m_scoreObject->singleNote()) {
if (!note.isRest()) {
note.setRhythm(Trhythm::NoRhythm);
m_scoreObject->setNote(0, note);
}
} else {
if (!GLOB->showNotesDiff()) {
if (m_scoreObject->selectedItem() == nullptr) {
m_scoreObject->addNote(note, true);
m_startedNoteId = -1;
} else {
if (!note.isRest()) {
auto r = m_scoreObject->selectedItem()->note()->rtm;
r.setRest(false);
note.setRhythm(r);
m_scoreObject->setNote(m_scoreObject->selectedItem(), note);
}
m_startedNoteId = selectedNoteId();
}
}
}
m_ignoreScore = false; // Reset the switch in case it is not consumed
}
void TnootkaQML::noteFinished(const Tnote &n)
{
Tnote note = n;
if (m_instrument)
m_instrument->setNote(note);
if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
note = note.showWithFlat();
m_ignoreScore = true;
if (m_scoreObject->singleNote()) {
note.setRhythm(Trhythm::NoRhythm);
m_scoreObject->setNote(0, note);
} else {
if (!GLOB->showNotesDiff()) {
if (m_scoreObject->selectedItem() == nullptr || m_startedNoteId == -1) {
m_scoreObject->setNote(m_scoreObject->lastNote(), note);
m_scoreObject->setSelectedItem(nullptr);
} else if (m_scoreObject->selectedItem() && !note.isRest()) {
auto r = m_scoreObject->selectedItem()->note()->rtm;
r.setRest(false);
note.setRhythm(r);
m_scoreObject->setNote(m_scoreObject->selectedItem(), note);
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
}
m_ignoreScore = false; // Reset the switch in case it is not consumed
// TODO Do treat tied notes as a single one?
}
void TnootkaQML::setMainScore(QQuickItem *ms)
{
if (!m_mainScore) {
m_mainScore = ms;
m_scoreObject = qobject_cast<TscoreObject *>(qvariant_cast<QObject *>(m_mainScore->property("scoreObj")));
connect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
connect(GLOB, &Tglobals::isExamChanged, this, &TnootkaQML::examStartStop);
if (m_scoreObject && !m_nodeConnected)
connectInstrument();
}
}
void TnootkaQML::setInstrument(TcommonInstrument *ci)
{
if (m_instrument != ci) {
if (m_instrument != nullptr)
m_nodeConnected = false; // reset connection of instrument signal when instrument type changed
m_instrument = ci;
if (m_scoreObject && !m_nodeConnected && !GLOB->isExam())
connectInstrument();
}
}
void TnootkaQML::connectInstrument()
{
if (m_instrument && !m_nodeConnected) {
connect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
m_nodeConnected = true;
}
}
void TnootkaQML::instrumentChangesNoteSlot()
{
m_ignoreScore = true;
Tnote noteToPlay = m_instrument->note();
noteToPlay.transpose(GLOB->transposition());
emit playNote(noteToPlay);
Tnote instrNote = m_instrument->note();
if (m_scoreObject->keySignature() < 0
|| (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats && !(instrNote.alter() == 0 && (instrNote.note() == 3 || instrNote.note() == 7))))
instrNote = instrNote.showWithFlat();
if (m_scoreObject->singleNote()) {
m_scoreObject->setNote(0, instrNote);
if (GLOB->instrument().bandoneon())
m_scoreObject->setTechnical(0, m_instrument->technical());
} else {
if (m_scoreObject->selectedItem() == nullptr) {
auto r = m_scoreObject->workRhythm();
r.setRest(false);
auto instNoteNr = instrNote.chromatic();
// check is note inside score boundaries
if (instNoteNr < m_scoreObject->lowestNote().chromatic() || instNoteNr > m_scoreObject->highestNote().chromatic()) {
r.setRest(true);
instrNote.setNote(0); // invalidate not - it became rest
}
instrNote.setRhythm(r);
m_scoreObject->addNote(instrNote, true);
} else {
auto r = m_scoreObject->selectedItem()->note()->rtm;
r.setRest(false);
instrNote.setRhythm(r);
m_scoreObject->setNote(m_scoreObject->selectedItem(), instrNote);
if (GLOB->instrument().bandoneon()) {
auto seg = m_scoreObject->selectedItem() ? m_scoreObject->noteSegment(m_scoreObject->selectedItem()->index()) : m_scoreObject->lastSegment();
Ttechnical instrData(m_instrument->technical());
if (seg->index() > 0) {
for (int i = seg->index() - 1; i >= 0; --i) {
auto searchNoteData = m_scoreObject->noteSegment(i)->techicalData();
if (searchNoteData.bowing()) { // Show bowing but only when it changes comparing to the previously set bow direction
if (searchNoteData.bowing() == instrData.bowing()) // if it is the same - just reset bowing on note data from the instrument
instrData.setBowing(Ttechnical::BowUndefined);
break;
}
}
}
seg->setTechnical(instrData.data());
}
}
m_ignoreScore = false; // Reset the switch in case it is not consumed
void TnootkaQML::examStartStop()
{
if (GLOB->isExam()) {
disconnect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
disconnect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
} else {
m_nodeConnected = false;
connectInstrument();
connect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
}
}
void TnootkaQML::scoreChangedNoteSlot()
{
if (m_ignoreScore) {
m_ignoreScore = false;
return;
}
auto scoreNote = m_scoreObject->selectedNote();
if (m_instrument)
m_instrument->setNote(scoreNote, getTechicalFromScore());
if (scoreNote.isValid())
scoreNote.transpose(GLOB->transposition());
emit playNote(scoreNote);
}
int TnootkaQML::selectedNoteId() const
{
return m_scoreObject->selectedItem() ? m_scoreObject->selectedItem()->index() : -1;
}
int TnootkaQML::getTechicalFromScore()
{
quint32 technical = NO_TECHNICALS; // empty by default
if (GLOB->instrument().bandoneon() && m_scoreObject->selectedItem()) {
auto selectedSegment = m_scoreObject->noteSegment(m_scoreObject->selectedItem()->index());
Ttechnical dataToSet = selectedSegment->technical();
if (!dataToSet.bowing()) { // no bowing, so look up for any previous note with bowing mark
for (int i = selectedSegment->index(); i >= 0; --i) {
auto searchNoteData = m_scoreObject->noteSegment(i)->techicalData();
if (searchNoteData.bowing()) {
dataToSet.setBowing(searchNoteData.bowing());
break;
}
}
}
technical = dataToSet.data();
}
return technical;
SeeLook
committed
}
void TnootkaQML::selectPlayingNote(int id)
{
m_ignoreScore = true;
m_scoreObject->setSelectedItem(m_scoreObject->note(id));
if (m_instrument)
m_instrument->setNote(m_scoreObject->selectedNote(), getTechicalFromScore());
m_ignoreScore = false; // Reset the switch in case it is not consumed
SeeLook
committed
}
int TnootkaQML::scoreNotesCount() const
{
return m_scoreObject->notesCount();
SeeLook
committed
}
QList<Tnote> &TnootkaQML::scoreNoteList() const
{
return m_scoreObject->noteList();
SeeLook
committed
}
bool TnootkaQML::eventFilter(QObject *obj, QEvent *event)
{
if (obj == qApp && event->type() == QEvent::ApplicationPaletteChange) {
setMessageColor(qApp->palette().highlight().color());
m_scoreAct->setBgColor(qApp->palette().highlight().color());
}
return QObject::eventFilter(obj, event);
}