diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..672b5ad6000879f12176e137275cb167584d5bf1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,91 @@ +--- +# SPDX-FileCopyrightText: 2019 Christoph Cullmann <cullmann@kde.org> +# SPDX-FileCopyrightText: 2019 Gernot Gebhard <gebhard@absint.com> +# +# SPDX-License-Identifier: MIT + +# This file got automatically created by ECM, do not edit +# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the config options +# and https://community.kde.org/Policies/Frameworks_Coding_Style#Clang-format_automatic_code_formatting +# for clang-format tips & tricks +--- +Language: JavaScript +DisableFormat: true +--- + +# Style for C++ +Language: Cpp + +# base is WebKit coding style: https://webkit.org/code-style-guidelines/ +# below are only things set that diverge from this style! +BasedOnStyle: WebKit + +# enforce C++11 (e.g. for std::vector<std::vector<lala>> +Standard: Cpp11 + +# 4 spaces indent +TabWidth: 4 + +# 2 * 80 wide lines +ColumnLimit: 160 + +# sort includes inside line separated groups +SortIncludes: true + +# break before braces on function, namespace and class definitions. +BreakBeforeBraces: Linux + +# CrlInstruction *a; +PointerAlignment: Right + +# horizontally aligns arguments after an open bracket. +AlignAfterOpenBracket: Align + +# don't move all parameters to new line +AllowAllParametersOfDeclarationOnNextLine: false + +# no single line functions +AllowShortFunctionsOnASingleLine: InlineOnly + +# no single line enums +AllowShortEnumsOnASingleLine: true + +# always break before you encounter multi line strings +AlwaysBreakBeforeMultilineStrings: true + +# don't move arguments to own lines if they are not all on the same +BinPackArguments: false + +# don't move parameters to own lines if they are not all on the same +BinPackParameters: false + +# In case we have an if statement with multiple lines the operator should be at the beginning of the line +# but we do not want to break assignments +BreakBeforeBinaryOperators: NonAssignment + +# format C++11 braced lists like function calls +Cpp11BracedListStyle: true + +# do not put a space before C++11 braced lists +SpaceBeforeCpp11BracedList: false + +# remove empty lines +KeepEmptyLinesAtTheStartOfBlocks: false + +# no namespace indentation to keep indent level low +NamespaceIndentation: None + +# we use template< without space. +SpaceAfterTemplateKeyword: false + +# Always break after template declaration +AlwaysBreakTemplateDeclarations: true + +# macros for which the opening brace stays attached. +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE , wl_resource_for_each, wl_resource_for_each_safe ] + +# keep lambda formatting multi-line if not empty +AllowShortLambdasOnASingleLine: Empty + +# We do not want clang-format to put all arguments on a new line +AllowAllArgumentsOnNextLine: false diff --git a/src/charts/tchartitem.cpp b/src/charts/tchartitem.cpp index 2463776b23a4b7755ccceaa00836efd6d1bb43e8..978d4b484d8ee4427560d3a88c89baa61f59ec7e 100644 --- a/src/charts/tchartitem.cpp +++ b/src/charts/tchartitem.cpp @@ -359,7 +359,7 @@ void TchartItem::setAverLineGroup(int averGr) { //################################################################################################# void TchartItem::getExamFileSlot() { - QString fileName = TfileDialog::getOpenFileName(TexTrans::loadExamFileTxt(), GLOB->E->examsDir, TexTrans::examFilterTxt()); + QString fileName = TfileDialog::getOpenFileName(TexTrans::loadExamFileTxt(), GLOB->examParams->examsDir, TexTrans::examFilterTxt()); if (!fileName.isEmpty()) loadExam(fileName); } diff --git a/src/dialogs/tlevelcreatoritem.cpp b/src/dialogs/tlevelcreatoritem.cpp index f96ec21f9d7986b18a3d62bb766cf68bcaf03637..82cce4d5ac25c27c799a88c30201976be99b7a95 100644 --- a/src/dialogs/tlevelcreatoritem.cpp +++ b/src/dialogs/tlevelcreatoritem.cpp @@ -104,7 +104,7 @@ void TlevelCreatorItem::continueLevelSave(const QString& name, const QString& de // Saving to file QLatin1String dotNel(".nel"); bool toTr = name.startsWith(QLatin1String("tr(")); - QString fName = QDir::toNativeSeparators(GLOB->E->levelsDir + QLatin1String("/") + m_level->name.mid(toTr ? 3 : 0)); + QString fName = QDir::toNativeSeparators(GLOB->examParams->levelsDir + QLatin1String("/") + m_level->name.mid(toTr ? 3 : 0)); fName = fName.replace(QLatin1String("."), QString()); //HACK: file dialogues don't like dots in the names if (QFileInfo::exists(fName + dotNel)) fName += QLatin1String("-") + QDateTime::currentDateTime().toString(QLatin1String("(dd-MMM-hhmmss)")); @@ -122,7 +122,7 @@ void TlevelCreatorItem::continueLevelSave(const QString& name, const QString& de if (fileName.right(4) != dotNel) fileName += dotNel; - GLOB->E->levelsDir = QFileInfo(fileName).absoluteDir().absolutePath(); + GLOB->examParams->levelsDir = QFileInfo(fileName).absoluteDir().absolutePath(); if (!Tlevel::saveToFile(*m_level, fileName)) { wantNotSavedMessage(QString(), tr("Cannot open file for writing") + QLatin1String("\n") + fileName); if (m_resumeAfterLevelChange) diff --git a/src/dialogs/tlevelselector.cpp b/src/dialogs/tlevelselector.cpp index 4f8bdb8df6f79c8f89a0a5ad69d9a48477e191d8..74e2316a1f083ba31d747e79a5b0936d7729cab3 100644 --- a/src/dialogs/tlevelselector.cpp +++ b/src/dialogs/tlevelselector.cpp @@ -191,12 +191,12 @@ void TlevelSelector::loadFromFile(QString levelFile) { GLOB->E->levelsDir = Tandroid::getExternalPath(); levelFile = TfileDialog::getOpenFileName(GLOB->E->levelsDir, QStringLiteral("nel")); #else - levelFile = TfileDialog::getOpenFileName(tr("Load exam level"), GLOB->E->levelsDir, levelFilterTxt() + QLatin1String(" (*.nel)")); + levelFile = TfileDialog::getOpenFileName(tr("Load exam level"), GLOB->examParams->levelsDir, levelFilterTxt() + QLatin1String(" (*.nel)")); #endif QFile file(levelFile); Tlevel level = getLevelFromFile(file); if (!level.name.isEmpty()) { - GLOB->E->levelsDir = QFileInfo(levelFile).absoluteDir().absolutePath(); + GLOB->examParams->levelsDir = QFileInfo(levelFile).absoluteDir().absolutePath(); addLevel(level, levelFile, true); checkLast(); updateRecentLevels(); diff --git a/src/dialogs/ttunerdialogitem.cpp b/src/dialogs/ttunerdialogitem.cpp index c255e165f0e5e2e765c78b5f6707ed16a317c20c..3c82ea683352a9414ea57ff1bc5ea3417cceecd9 100644 --- a/src/dialogs/ttunerdialogitem.cpp +++ b/src/dialogs/ttunerdialogitem.cpp @@ -71,16 +71,16 @@ QString TtunerDialogItem::noteName() const { QStringList TtunerDialogItem::tuningModel() { QStringList model; auto t = GLOB->Gtune(); - auto pitch440Offset = GLOB->A->a440diff; + auto pitch440Offset = GLOB->audioParams->a440diff; if (GLOB->instrument().isGuitar() && t->stringNr() > 2) { // guitar for (int i = 1; i <= t->stringNr(); i++) { - float offPitch = TnoteStruct::pitchToFreq(t->str(i).toMidi() + pitch440Offset + static_cast<qreal>(GLOB->A->transposition)); + float offPitch = TnoteStruct::pitchToFreq(t->str(i).toMidi() + pitch440Offset + static_cast<qreal>(GLOB->audioParams->transposition)); model << QString("%1;%2 Hz").arg((t->str(i)).styledName()).arg(offPitch, 0, 'f', 1); } } else { // no guitar - C-major scale frequencies for (int i = 1; i < 8; i++) { Tnote n(i, 1, 0); - float offPitch = TnoteStruct::pitchToFreq(n.toMidi() + pitch440Offset + static_cast<qreal>(GLOB->A->transposition)); + float offPitch = TnoteStruct::pitchToFreq(n.toMidi() + pitch440Offset + static_cast<qreal>(GLOB->audioParams->transposition)); model << QString("%1;%2 Hz").arg(n.styledName()).arg(offPitch, 0, 'f', 1); } } @@ -97,7 +97,7 @@ QStringList TtunerDialogItem::tuningModel() { */ void TtunerDialogItem::setWorkFreq(int wFreq) { if (wFreq != m_workFreq) { - GLOB->A->a440diff = Tglobals::pitchOfFreq(static_cast<qreal>(wFreq)) - Tglobals::pitchOfFreq(440.0); + GLOB->audioParams->a440diff = Tglobals::pitchOfFreq(static_cast<qreal>(wFreq)) - Tglobals::pitchOfFreq(440.0); m_workFreq = wFreq; emit workFreqChanged(); emit tuningModelChanged(); diff --git a/src/libs/core/Android/tandroid.cpp b/src/libs/core/Android/tandroid.cpp index 05503d5fa1b8e68c59cda76520145a4db25e3436..3c369f84f2a9d1ee23257e646c496be3713307a4 100644 --- a/src/libs/core/Android/tandroid.cpp +++ b/src/libs/core/Android/tandroid.cpp @@ -16,170 +16,173 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tandroid.h" #include <QtAndroidExtras/qandroidfunctions.h> #include <QtAndroidExtras/qandroidjnienvironment.h> #include <QtCore/qdatetime.h> #include <QtCore/qfileinfo.h> -#include <QtWidgets/qmessagebox.h> -#include <QtWidgets/qapplication.h> #include <QtCore/qstandardpaths.h> +#include <QtWidgets/qapplication.h> +#include <QtWidgets/qmessagebox.h> #include <QtCore/qdebug.h> - -void Tandroid::keepScreenOn(bool on) { - QtAndroid::runOnAndroidThread([on]{ - QAndroidJniObject activity = QtAndroid::androidActivity(); - if (activity.isValid()) { - QAndroidJniObject window = - activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - - if (window.isValid()) { - const int FLAG_KEEP_SCREEN_ON = 128; - if (on) - window.callMethod<void>("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); - else - window.callMethod<void>("clearFlags", "(I)V", FLAG_KEEP_SCREEN_ON); - } - } - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - env->ExceptionClear(); - }); +void Tandroid::keepScreenOn(bool on) +{ + QtAndroid::runOnAndroidThread([on] { + QAndroidJniObject activity = QtAndroid::androidActivity(); + if (activity.isValid()) { + QAndroidJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); + + if (window.isValid()) { + const int FLAG_KEEP_SCREEN_ON = 128; + if (on) + window.callMethod<void>("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); + else + window.callMethod<void>("clearFlags", "(I)V", FLAG_KEEP_SCREEN_ON); + } + } + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + env->ExceptionClear(); + }); } - -void Tandroid::disableRotation(bool disRot) { - int orientation = disRot ? 0 : 10; // SCREEN_ORIENTATION_LANDSCAPE or SCREEN_ORIENTATION_FULL_SENSOR - QtAndroid::runOnAndroidThread([orientation]{ - QAndroidJniObject activity = QtAndroid::androidActivity(); - if (activity.isValid()) - activity.callMethod<void>("setRequestedOrientation" , "(I)V", orientation); - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - env->ExceptionClear(); - }); +void Tandroid::disableRotation(bool disRot) +{ + int orientation = disRot ? 0 : 10; // SCREEN_ORIENTATION_LANDSCAPE or SCREEN_ORIENTATION_FULL_SENSOR + QtAndroid::runOnAndroidThread([orientation] { + QAndroidJniObject activity = QtAndroid::androidActivity(); + if (activity.isValid()) + activity.callMethod<void>("setRequestedOrientation", "(I)V", orientation); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + env->ExceptionClear(); + }); } - -int Tandroid::getAPIlevelNr() { - return QtAndroid::androidSdkVersion(); +int Tandroid::getAPIlevelNr() +{ + return QtAndroid::androidSdkVersion(); } - -bool Tandroid::hasWriteAccess() { - if (QtAndroid::androidSdkVersion() < 23) - return true; - else - return QtAndroid::checkPermission(QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE")) == QtAndroid::PermissionResult::Granted; +bool Tandroid::hasWriteAccess() +{ + if (QtAndroid::androidSdkVersion() < 23) + return true; + else + return QtAndroid::checkPermission(QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE")) == QtAndroid::PermissionResult::Granted; } - -void Tandroid::askForWriteAcces() { - if (QtAndroid::androidSdkVersion() >= 23) { - const QString writeID("android.permission.WRITE_EXTERNAL_STORAGE"); - if (QtAndroid::checkPermission(writeID) != QtAndroid::PermissionResult::Granted) { - auto perms = QtAndroid::requestPermissionsSync(QStringList() << writeID); - qDebug() << writeID << (perms[writeID] == QtAndroid::PermissionResult::Granted); +void Tandroid::askForWriteAcces() +{ + if (QtAndroid::androidSdkVersion() >= 23) { + const QString writeID("android.permission.WRITE_EXTERNAL_STORAGE"); + if (QtAndroid::checkPermission(writeID) != QtAndroid::PermissionResult::Granted) { + auto perms = QtAndroid::requestPermissionsSync(QStringList() << writeID); + qDebug() << writeID << (perms[writeID] == QtAndroid::PermissionResult::Granted); + } } - } } -QString Tandroid::getExternalPath() { - QString extPath; - if (getAPIlevelNr() < 19) { // look for SD card only before Kitkat, otherwise it is inaccessible - extPath = qgetenv("SECONDARY_STORAGE"); - if (!extPath.isEmpty()) { - if (!QFileInfo(extPath).isWritable()) { - qDebug() << "[Tandroid] No write access to secondary storage!"; - extPath.clear(); - } +QString Tandroid::getExternalPath() +{ + QString extPath; + if (getAPIlevelNr() < 19) { // look for SD card only before Kitkat, otherwise it is inaccessible + extPath = qgetenv("SECONDARY_STORAGE"); + if (!extPath.isEmpty()) { + if (!QFileInfo(extPath).isWritable()) { + qDebug() << "[Tandroid] No write access to secondary storage!"; + extPath.clear(); + } + } } - } - askForWriteAcces(); + askForWriteAcces(); - if (extPath.isEmpty()) - extPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); -// extPath = qgetenv("EXTERNAL_STORAGE"); // return primary storage path (device internal) - if (!QFileInfo(extPath).isWritable()) { - qDebug() << "[Tandroid] No write access to primary storage!"; - extPath.clear(); - } - return extPath; + if (extPath.isEmpty()) + extPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + // extPath = qgetenv("EXTERNAL_STORAGE"); // return primary storage path (device internal) + if (!QFileInfo(extPath).isWritable()) { + qDebug() << "[Tandroid] No write access to primary storage!"; + extPath.clear(); + } + return extPath; } - -QString Tandroid::getRunArgument() { - QString argument; - QAndroidJniObject activity = QtAndroid::androidActivity(); - if (activity.isValid()) { - QAndroidJniObject intent = activity.callObjectMethod("getIntent", "()Landroid/content/Intent;"); - if (intent.isValid()) { - QAndroidJniObject data = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); - if (data.isValid()) { - QAndroidJniObject arg = data.callObjectMethod("getPath", "()Ljava/lang/String;"); - if (arg.isValid()) - argument = arg.toString(); - } +QString Tandroid::getRunArgument() +{ + QString argument; + QAndroidJniObject activity = QtAndroid::androidActivity(); + if (activity.isValid()) { + QAndroidJniObject intent = activity.callObjectMethod("getIntent", "()Landroid/content/Intent;"); + if (intent.isValid()) { + QAndroidJniObject data = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); + if (data.isValid()) { + QAndroidJniObject arg = data.callObjectMethod("getPath", "()Ljava/lang/String;"); + if (arg.isValid()) + argument = arg.toString(); + } + } } - } - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - env->ExceptionClear(); - return argument; + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + env->ExceptionClear(); + return argument; } - -void Tandroid::restartNootka() { - auto activity = QtAndroid::androidActivity(); - auto packageManager = activity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); - auto activityIntent = packageManager.callObjectMethod("getLaunchIntentForPackage", - "(Ljava/lang/String;)Landroid/content/Intent;", - activity.callObjectMethod("getPackageName", - "()Ljava/lang/String;").object()); - auto pendingIntent = QAndroidJniObject::callStaticObjectMethod("android/app/PendingIntent", "getActivity", - "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;", - activity.object(), jint(0), activityIntent.object(), - QAndroidJniObject::getStaticField<jint>("android/content/Intent", - "FLAG_ACTIVITY_CLEAR_TOP")); - auto alarmManager = activity.callObjectMethod("getSystemService", - "(Ljava/lang/String;)Ljava/lang/Object;", - QAndroidJniObject::getStaticObjectField("android/content/Context", - "ALARM_SERVICE", - "Ljava/lang/String;").object()); - alarmManager.callMethod<void>("set", - "(IJLandroid/app/PendingIntent;)V", - QAndroidJniObject::getStaticField<jint>("android/app/AlarmManager", "RTC"), - jlong(QDateTime::currentMSecsSinceEpoch() + 750), pendingIntent.object()); - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - env->ExceptionClear(); +void Tandroid::restartNootka() +{ + auto activity = QtAndroid::androidActivity(); + auto packageManager = activity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); + auto activityIntent = packageManager.callObjectMethod("getLaunchIntentForPackage", + "(Ljava/lang/String;)Landroid/content/Intent;", + activity.callObjectMethod("getPackageName", "()Ljava/lang/String;").object()); + auto pendingIntent = + QAndroidJniObject::callStaticObjectMethod("android/app/PendingIntent", + "getActivity", + "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;", + activity.object(), + jint(0), + activityIntent.object(), + QAndroidJniObject::getStaticField<jint>("android/content/Intent", "FLAG_ACTIVITY_CLEAR_TOP")); + auto alarmManager = + activity.callObjectMethod("getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;", + QAndroidJniObject::getStaticObjectField("android/content/Context", "ALARM_SERVICE", "Ljava/lang/String;").object()); + alarmManager.callMethod<void>("set", + "(IJLandroid/app/PendingIntent;)V", + QAndroidJniObject::getStaticField<jint>("android/app/AlarmManager", "RTC"), + jlong(QDateTime::currentMSecsSinceEpoch() + 750), + pendingIntent.object()); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + env->ExceptionClear(); } - -void Tandroid::sendExam(const QString& title, const QString &message, const QString& filePath) { - QAndroidJniObject jTitle = QAndroidJniObject::fromString(title); - QAndroidJniObject jMessage = QAndroidJniObject::fromString(message); - QAndroidJniObject jFile = QAndroidJniObject::fromString(filePath); - QAndroidJniObject::callStaticMethod<void>( - "net/sf/nootka/TshareExam", "send", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - jTitle.object<jstring>(), jMessage.object<jstring>(), jFile.object<jstring>()); - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - env->ExceptionClear(); +void Tandroid::sendExam(const QString &title, const QString &message, const QString &filePath) +{ + QAndroidJniObject jTitle = QAndroidJniObject::fromString(title); + QAndroidJniObject jMessage = QAndroidJniObject::fromString(message); + QAndroidJniObject jFile = QAndroidJniObject::fromString(filePath); + QAndroidJniObject::callStaticMethod<void>("net/sf/nootka/TshareExam", + "send", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + jTitle.object<jstring>(), + jMessage.object<jstring>(), + jFile.object<jstring>()); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + env->ExceptionClear(); } - -QString Tandroid::accountName() { -// QString model = QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "MODEL").toString(); -// QAndroidJniObject juser = QtAndroid::androidActivity().callObjectMethod("getUser", "()Ljava/lang/String;"); -// return model; - return QStringLiteral("Android"); -// return QAndroidJniObject::callStaticObjectMethod<jstring> -// ("net/sf/nootka/account", -// "getName").toString(); +QString Tandroid::accountName() +{ + // QString model = QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "MODEL").toString(); + // QAndroidJniObject juser = QtAndroid::androidActivity().callObjectMethod("getUser", "()Ljava/lang/String;"); + // return model; + return QStringLiteral("Android"); + // return QAndroidJniObject::callStaticObjectMethod<jstring> + // ("net/sf/nootka/account", + // "getName").toString(); } diff --git a/src/libs/core/Android/tandroid.h b/src/libs/core/Android/tandroid.h index 6f66871ff7e6c611c3b24a076724adf70638fda2..9c14ab3fa9e31593757341062dc8b5d3922f6fb4 100644 --- a/src/libs/core/Android/tandroid.h +++ b/src/libs/core/Android/tandroid.h @@ -16,55 +16,52 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TANDROID_H #define TANDROID_H - #include <QtCore/qstring.h> - /** * Android functions requiring invoking native methods through JNI */ -namespace Tandroid { - void keepScreenOn(bool on); +namespace Tandroid +{ +void keepScreenOn(bool on); - void disableRotation(bool disRot); +void disableRotation(bool disRot); - /** - * Returns a number of Android API on a hosting device. - */ - int getAPIlevelNr(); +/** + * Returns a number of Android API on a hosting device. + */ +int getAPIlevelNr(); - /** - * Returns path to external storage (SD card). - */ - QString getExternalPath(); +/** + * Returns path to external storage (SD card). + */ +QString getExternalPath(); - /** - * @p TRUE when write access to device is granted. API 23 and above - */ - bool hasWriteAccess(); +/** + * @p TRUE when write access to device is granted. API 23 and above + */ +bool hasWriteAccess(); - void askForWriteAcces(); +void askForWriteAcces(); - /** - * Returns command line argument (usually exam/level file name. - */ - QString getRunArgument(); +/** + * Returns command line argument (usually exam/level file name. + */ +QString getRunArgument(); - void sendExam(const QString& title, const QString& message, const QString& filePath); +void sendExam(const QString &title, const QString &message, const QString &filePath); - /** - * Restarts the application by calling java code with delay. - * WARING! Application has to be terminated just after this method. - */ - void restartNootka(); +/** + * Restarts the application by calling java code with delay. + * WARING! Application has to be terminated just after this method. + */ +void restartNootka(); - QString accountName(); +QString accountName(); } - #endif // TANDROID_H diff --git a/src/libs/core/Android/tfiledialog.cpp b/src/libs/core/Android/tfiledialog.cpp index 46cb6d9ff6321493632d194567e560f4a1b6cbaa..9475807cd24898de0fd57099268477c7400c5a03 100644 --- a/src/libs/core/Android/tfiledialog.cpp +++ b/src/libs/core/Android/tfiledialog.cpp @@ -19,438 +19,431 @@ #include "tfiledialog.h" #include "tmtr.h" -#if defined (Q_OS_ANDROID) -#include <qtr.h> -#include <tpath.h> +#if defined(Q_OS_ANDROID) #include <Android/tandroid.h> #include <QtWidgets/QtWidgets> - +#include <qtr.h> +#include <tpath.h> class TiconProvider : public QFileIconProvider { - public: + TiconProvider(QFileIconProvider *realProv) + : QFileIconProvider() + , m_realProvider(realProv) + { + } - TiconProvider(QFileIconProvider *realProv) : - QFileIconProvider(), - m_realProvider(realProv) - { - } - - virtual QIcon icon(const QFileInfo& info) const { - if (info.isDir()) - return QIcon(QLatin1String(":/mobile/dir.png")); - else if (info.suffix().contains(QLatin1String("nel"))) - return QIcon(Tpath::img("nootka-level")); - else if (info.suffix().contains(QLatin1String("noo"))) - return QIcon(Tpath::img("nootka-exam")); - else if (info.suffix().contains(QLatin1String("xml"))) // either *.xml or *.musicxml - return QIcon(Tpath::img("melody")); - else if (info.suffix().contains(QLatin1String("mxl"))) - return QIcon(Tpath::img("melody")); - else - return m_realProvider->icon(info); - } - - virtual QIcon icon(IconType type) const { - switch (type) { - case Folder: - return qApp->style()->standardIcon(QStyle::SP_DirIcon); - default: - return qApp->style()->standardIcon(QStyle::SP_FileIcon); + virtual QIcon icon(const QFileInfo &info) const + { + if (info.isDir()) + return QIcon(QLatin1String(":/mobile/dir.png")); + else if (info.suffix().contains(QLatin1String("nel"))) + return QIcon(Tpath::img("nootka-level")); + else if (info.suffix().contains(QLatin1String("noo"))) + return QIcon(Tpath::img("nootka-exam")); + else if (info.suffix().contains(QLatin1String("xml"))) // either *.xml or *.musicxml + return QIcon(Tpath::img("melody")); + else if (info.suffix().contains(QLatin1String("mxl"))) + return QIcon(Tpath::img("melody")); + else + return m_realProvider->icon(info); + } + + virtual QIcon icon(IconType type) const + { + switch (type) { + case Folder: + return qApp->style()->standardIcon(QStyle::SP_DirIcon); + default: + return qApp->style()->standardIcon(QStyle::SP_FileIcon); + } } - } private: - QFileIconProvider *m_realProvider; + QFileIconProvider *m_realProvider; }; -//################################################################################################# -//################### class TnewDirMessage ############################################ -//################################################################################################# +// ################################################################################################# +// ################### class TnewDirMessage ############################################ +// ################################################################################################# /** * Subclass of QDialog to get name of new directory */ class TnewDirMessage : public QDialog { - public: - explicit TnewDirMessage(QWidget* parent = nullptr) : - QDialog(parent) - { - auto label = new QLabel(qTR("QFileDialog", "Create New Folder"), this); - m_edit = new QLineEdit(this); - m_edit->setPlaceholderText(qTR("QFileSystemModel", "Name")); - m_edit->setMinimumWidth(qMin<int>(Tmtr::longScreenSide() / 3, fontMetrics().width(QStringLiteral("w")) * 20)); - - QSize iconS(Tmtr::fingerPixels() * 0.7, Tmtr::fingerPixels() * 0.7); - m_createButt = new QPushButton(QIcon(QLatin1String(":/mobile/newDir.png")), - qTR("QFileDialog", "&New Folder").replace(QLatin1String("&"), QString()), - this); - m_createButt->setIconSize(iconS); - m_createButt->setDisabled(true); - m_createButt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - m_createButt->setDefault(true); - auto cancelButt = new QPushButton(style()->standardIcon(QStyle::SP_DialogCancelButton), - qTR("QPlatformTheme", "Cancel"), - this); - cancelButt->setIconSize(iconS); - cancelButt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - - auto lay = new QVBoxLayout; - lay->addWidget(label); - lay->addWidget(m_edit); - lay->addSpacing(Tmtr::fingerPixels() / 2); - auto buttonLay = new QHBoxLayout; + explicit TnewDirMessage(QWidget *parent = nullptr) + : QDialog(parent) + { + auto label = new QLabel(qTR("QFileDialog", "Create New Folder"), this); + m_edit = new QLineEdit(this); + m_edit->setPlaceholderText(qTR("QFileSystemModel", "Name")); + m_edit->setMinimumWidth(qMin<int>(Tmtr::longScreenSide() / 3, fontMetrics().width(QStringLiteral("w")) * 20)); + + QSize iconS(Tmtr::fingerPixels() * 0.7, Tmtr::fingerPixels() * 0.7); + m_createButt = + new QPushButton(QIcon(QLatin1String(":/mobile/newDir.png")), qTR("QFileDialog", "&New Folder").replace(QLatin1String("&"), QString()), this); + m_createButt->setIconSize(iconS); + m_createButt->setDisabled(true); + m_createButt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + m_createButt->setDefault(true); + auto cancelButt = new QPushButton(style()->standardIcon(QStyle::SP_DialogCancelButton), qTR("QPlatformTheme", "Cancel"), this); + cancelButt->setIconSize(iconS); + cancelButt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + auto lay = new QVBoxLayout; + lay->addWidget(label); + lay->addWidget(m_edit); + lay->addSpacing(Tmtr::fingerPixels() / 2); + auto buttonLay = new QHBoxLayout; buttonLay->addWidget(m_createButt); buttonLay->addWidget(cancelButt); buttonLay->setContentsMargins(0, 0, 0, 0); - lay->addLayout(buttonLay); - lay->setContentsMargins(0, 0, 0, 0); - setLayout(lay); + lay->addLayout(buttonLay); + lay->setContentsMargins(0, 0, 0, 0); + setLayout(lay); - connect(m_createButt, &QPushButton::clicked, this, &QDialog::accept); - connect(cancelButt, &QPushButton::clicked, this, &QDialog::reject); - connect(m_edit, &QLineEdit::textChanged, this, &TnewDirMessage::textChangedSlot); - } + connect(m_createButt, &QPushButton::clicked, this, &QDialog::accept); + connect(cancelButt, &QPushButton::clicked, this, &QDialog::reject); + connect(m_edit, &QLineEdit::textChanged, this, &TnewDirMessage::textChangedSlot); + } - QString getName() { return m_edit->text(); } + QString getName() { return m_edit->text(); } - static QString dirName(QWidget* parent = 0) { - TnewDirMessage newDir(parent); - if (newDir.exec() == Accepted && !newDir.getName().isEmpty()) - return newDir.getName(); - else - return QString(); - } + static QString dirName(QWidget *parent = 0) + { + TnewDirMessage newDir(parent); + if (newDir.exec() == Accepted && !newDir.getName().isEmpty()) + return newDir.getName(); + else + return QString(); + } protected: - void textChangedSlot(const QString& t) { m_createButt->setDisabled(t.isEmpty()); } + void textChangedSlot(const QString &t) { m_createButt->setDisabled(t.isEmpty()); } private: - QLineEdit *m_edit; - QPushButton *m_createButt; + QLineEdit *m_edit; + QPushButton *m_createButt; }; - - /* static */ -QString TfileDialog::getOpenFileName(const QString& directory, const QString& filter) { - TfileDialog openDialog(directory, filter, TfileDialog::e_acceptOpen); - if (openDialog.exec() == QFileDialog::Accepted) - return openDialog.selectedFile(); - else - return QString(); +QString TfileDialog::getOpenFileName(const QString &directory, const QString &filter) +{ + TfileDialog openDialog(directory, filter, TfileDialog::e_acceptOpen); + if (openDialog.exec() == QFileDialog::Accepted) + return openDialog.selectedFile(); + else + return QString(); } - -QString TfileDialog::getSaveFileName(const QString& directory, const QString& filter) { - TfileDialog saveDialog(directory, filter, TfileDialog::e_acceptSave); - if (saveDialog.exec() == QFileDialog::Accepted) - return saveDialog.selectedFile(); - else - return QString(); +QString TfileDialog::getSaveFileName(const QString &directory, const QString &filter) +{ + TfileDialog saveDialog(directory, filter, TfileDialog::e_acceptSave); + if (saveDialog.exec() == QFileDialog::Accepted) + return saveDialog.selectedFile(); + else + return QString(); } - -//################################################################################################# -//################################################################################################# -//################### class TfileDialog ############################################ -//################################################################################################# -//################################################################################################# -TfileDialog::TfileDialog(const QString& directory, const QString& filter, EacceptMode mode) : - QDialog(nullptr), - m_acceptMode(mode), - m_newDirItem(nullptr) +// ################################################################################################# +// ################################################################################################# +// ################### class TfileDialog ############################################ +// ################################################################################################# +// ################################################################################################# +TfileDialog::TfileDialog(const QString &directory, const QString &filter, EacceptMode mode) + : QDialog(nullptr) + , m_acceptMode(mode) + , m_newDirItem(nullptr) { - showMaximized(); - - Tandroid::askForWriteAcces(); -// left side menu - m_menu = new QListWidget(this); - m_menu->setIconSize(QSize(60, 60)); - m_menu->setMaximumWidth(80); - m_menu->setViewMode(QListView::IconMode); - m_menu->setMovement(QListView::Static); - m_menu->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_menu->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - int bSize = qBound<int>(Tmtr::fingerPixels() * 1.1, Tmtr::longScreenSide() / 12, Tmtr::fingerPixels() * 1.6); - m_menu->setIconSize(QSize(bSize, bSize)); - m_menu->setMaximumWidth(bSize + 10); - QFont f = font(); - f.setPixelSize(qMin<int>(bSize / 5, fontMetrics().height())); - m_menu->setFont(f); - m_menu->setObjectName(QLatin1String("fileMenu")); // revert colors - m_menu->setStyleSheet(m_menu->styleSheet() + QLatin1String(" QListWidget#fileMenu { background: palette(base); color: palette(text); }")); - QScroller::grabGesture(m_menu->viewport(), QScroller::LeftMouseButtonGesture); - - QStringList filters = filter.split(QLatin1String("|")); - - QIcon fileIcon; - if (filters.size() == 1) { // if filter has only one entry - check is it *.nel or *.noo and use its icon - if (filters.first().contains(QLatin1String("nel"))) - fileIcon = QIcon(Tpath::img("nootka-level")); - else if (filters.first().contains(QLatin1String("noo"))) - fileIcon = QIcon(Tpath::img("nootka-exam")); - } - if (filters.size() != 1 || fileIcon.isNull()) { // if more or none filters or other file types use standard icons + showMaximized(); + + Tandroid::askForWriteAcces(); + // left side menu + m_menu = new QListWidget(this); + m_menu->setIconSize(QSize(60, 60)); + m_menu->setMaximumWidth(80); + m_menu->setViewMode(QListView::IconMode); + m_menu->setMovement(QListView::Static); + m_menu->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_menu->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + int bSize = qBound<int>(Tmtr::fingerPixels() * 1.1, Tmtr::longScreenSide() / 12, Tmtr::fingerPixels() * 1.6); + m_menu->setIconSize(QSize(bSize, bSize)); + m_menu->setMaximumWidth(bSize + 10); + QFont f = font(); + f.setPixelSize(qMin<int>(bSize / 5, fontMetrics().height())); + m_menu->setFont(f); + m_menu->setObjectName(QLatin1String("fileMenu")); // revert colors + m_menu->setStyleSheet(m_menu->styleSheet() + QLatin1String(" QListWidget#fileMenu { background: palette(base); color: palette(text); }")); + QScroller::grabGesture(m_menu->viewport(), QScroller::LeftMouseButtonGesture); + + QStringList filters = filter.split(QLatin1String("|")); + + QIcon fileIcon; + if (filters.size() == 1) { // if filter has only one entry - check is it *.nel or *.noo and use its icon + if (filters.first().contains(QLatin1String("nel"))) + fileIcon = QIcon(Tpath::img("nootka-level")); + else if (filters.first().contains(QLatin1String("noo"))) + fileIcon = QIcon(Tpath::img("nootka-exam")); + } + if (filters.size() != 1 || fileIcon.isNull()) { // if more or none filters or other file types use standard icons + if (mode == e_acceptSave) + fileIcon = QIcon(Tpath::img("save")); + else + fileIcon = QIcon(Tpath::img("open")); + } + m_acceptItem = addMenuItem(fileIcon, mode == e_acceptSave ? qTR("QShortcut", "Save") : qTR("QShortcut", "Open")); + + QString space = QLatin1String(" "); + QString newLine = QLatin1String("\n"); + m_dirUpItem = addMenuItem(QIcon(QLatin1String(":/mobile/dirUp.png")), qTR("QFileDialog", "Parent Directory").replace(space, newLine)); if (mode == e_acceptSave) - fileIcon = QIcon(Tpath::img("save")); - else - fileIcon = QIcon(Tpath::img("open")); - } - m_acceptItem = addMenuItem(fileIcon, - mode == e_acceptSave ? qTR("QShortcut", "Save") : qTR("QShortcut", "Open")); - - QString space = QLatin1String(" "); - QString newLine = QLatin1String("\n"); - m_dirUpItem = addMenuItem(QIcon(QLatin1String(":/mobile/dirUp.png")), - qTR("QFileDialog", "Parent Directory").replace(space, newLine)); - if (mode == e_acceptSave) - m_newDirItem = addMenuItem(QIcon(QLatin1String(":/mobile/newDir.png")), - qTR("QFileDialog", "&New Folder").replace(space, newLine).replace(QLatin1String("&"), QString())); - if (Tandroid::getAPIlevelNr() < 19) // display SD card shortcut only below Kitkat - addMenuItem(QIcon(QLatin1String(":/mobile/card.png")), tr("Memory card").replace(space, newLine)); - m_cancelItem = addMenuItem(QIcon(Tpath::img("exit")), qTR("QShortcut", "Close")); - -// upper location label, file name edit, extension combo - m_locationLab = new QLabel(this); - m_locationLab->setAlignment(Qt::AlignRight); - m_locationLab->setFixedWidth(qApp->fontMetrics().height() * 7); - - m_editName = new QLineEdit(this); - m_editName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - if (mode == e_acceptOpen) - m_editName->setReadOnly(true); - - m_extensionCombo = new QComboBox(this); - if (!filter.isEmpty()) { - int filterCharsLen = 0, longestId = 0; - for (int f = 0; f < filters.size(); ++f) { - if (filters[f].length() > filterCharsLen) { - filterCharsLen = filters[f].length(); - longestId = f; - } + m_newDirItem = addMenuItem(QIcon(QLatin1String(":/mobile/newDir.png")), + qTR("QFileDialog", "&New Folder").replace(space, newLine).replace(QLatin1String("&"), QString())); + if (Tandroid::getAPIlevelNr() < 19) // display SD card shortcut only below Kitkat + addMenuItem(QIcon(QLatin1String(":/mobile/card.png")), tr("Memory card").replace(space, newLine)); + m_cancelItem = addMenuItem(QIcon(Tpath::img("exit")), qTR("QShortcut", "Close")); + + // upper location label, file name edit, extension combo + m_locationLab = new QLabel(this); + m_locationLab->setAlignment(Qt::AlignRight); + m_locationLab->setFixedWidth(qApp->fontMetrics().height() * 7); + + m_editName = new QLineEdit(this); + m_editName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + if (mode == e_acceptOpen) + m_editName->setReadOnly(true); + + m_extensionCombo = new QComboBox(this); + if (!filter.isEmpty()) { + int filterCharsLen = 0, longestId = 0; + for (int f = 0; f < filters.size(); ++f) { + if (filters[f].length() > filterCharsLen) { + filterCharsLen = filters[f].length(); + longestId = f; + } + } + m_extensionCombo->setFixedWidth(qApp->fontMetrics().boundingRect(filters[longestId]).width() + qApp->fontMetrics().height() * 1.5); } - m_extensionCombo->setFixedWidth(qApp->fontMetrics().boundingRect(filters[longestId]).width() + qApp->fontMetrics().height() * 1.5); - } - -// file list - m_list = new QListView(this); - int is = Tmtr::fingerPixels(); - m_list->setIconSize(QSize(is, is)); - m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - m_list->setSelectionBehavior(QAbstractItemView::SelectItems); - m_list->setSelectionMode(QAbstractItemView::SingleSelection); - m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - QScroller::grabGesture(m_list->viewport(), QScroller::LeftMouseButtonGesture); - -// Layout - m_lay = new QBoxLayout(QBoxLayout::LeftToRight); - m_lay->addWidget(m_menu); + + // file list + m_list = new QListView(this); + int is = Tmtr::fingerPixels(); + m_list->setIconSize(QSize(is, is)); + m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_list->setSelectionBehavior(QAbstractItemView::SelectItems); + m_list->setSelectionMode(QAbstractItemView::SingleSelection); + m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + QScroller::grabGesture(m_list->viewport(), QScroller::LeftMouseButtonGesture); + + // Layout + m_lay = new QBoxLayout(QBoxLayout::LeftToRight); + m_lay->addWidget(m_menu); auto innLay = new QVBoxLayout; auto fileLay = new QHBoxLayout; - fileLay->addWidget(m_locationLab); - fileLay->addWidget(m_editName); - fileLay->addWidget(m_extensionCombo); + fileLay->addWidget(m_locationLab); + fileLay->addWidget(m_editName); + fileLay->addWidget(m_extensionCombo); innLay->addLayout(fileLay); innLay->addWidget(m_list); - m_lay->addLayout(innLay); - setLayout(m_lay); - innLay->setContentsMargins(0, m_lay->contentsMargins().top(), 0, 0); - m_lay->setContentsMargins(0, 0, m_lay->contentsMargins().right(), 0); - -// Determine root path from given parameter 'directory' - m_fileModel = new QFileSystemModel(this); - QFileInfo fi(directory); - // Directory rather exists (checked at the very beginning) - if not it is treated as a file - if (fi.isDir()) - m_fileModel->setRootPath(directory); - else { - m_fileModel->setRootPath(fi.absolutePath()); - m_editName->setText(fi.fileName()); - } - updateLocationLabel(); - -// Set filters - if (filters.size()) { - for(int i = 0; i < filters.size(); ++i) { - filters[i].prepend(QLatin1String(".")); - m_extensionCombo->addItem(filters[i]); - filters[i].prepend(QLatin1String("*")); + m_lay->addLayout(innLay); + setLayout(m_lay); + innLay->setContentsMargins(0, m_lay->contentsMargins().top(), 0, 0); + m_lay->setContentsMargins(0, 0, m_lay->contentsMargins().right(), 0); + + // Determine root path from given parameter 'directory' + m_fileModel = new QFileSystemModel(this); + QFileInfo fi(directory); + // Directory rather exists (checked at the very beginning) - if not it is treated as a file + if (fi.isDir()) + m_fileModel->setRootPath(directory); + else { + m_fileModel->setRootPath(fi.absolutePath()); + m_editName->setText(fi.fileName()); } - m_extensionCombo->setCurrentIndex(0); - m_fileModel->setNameFilters(filters); - m_fileModel->setNameFilterDisables(false); - } + updateLocationLabel(); - m_list->setModel(m_fileModel); - m_list->setRootIndex(m_fileModel->index(m_fileModel->rootPath())); + // Set filters + if (filters.size()) { + for (int i = 0; i < filters.size(); ++i) { + filters[i].prepend(QLatin1String(".")); + m_extensionCombo->addItem(filters[i]); + filters[i].prepend(QLatin1String("*")); + } + m_extensionCombo->setCurrentIndex(0); + m_fileModel->setNameFilters(filters); + m_fileModel->setNameFilterDisables(false); + } - m_fileModel->setIconProvider(new TiconProvider(m_fileModel->iconProvider())); + m_list->setModel(m_fileModel); + m_list->setRootIndex(m_fileModel->index(m_fileModel->rootPath())); - connect(m_menu, &QListWidget::itemClicked, this, &TfileDialog::menuClickedSlot); - connect(m_list, &QListView::clicked, this, &TfileDialog::itemClickedSlot); + m_fileModel->setIconProvider(new TiconProvider(m_fileModel->iconProvider())); -// Adjust width of menu list according to widest text - QTimer::singleShot(100, this, [=] { m_menu->setFixedWidth(m_menu->sizeHintForColumn(0) + 2 * m_menu->frameWidth()); }); + connect(m_menu, &QListWidget::itemClicked, this, &TfileDialog::menuClickedSlot); + connect(m_list, &QListView::clicked, this, &TfileDialog::itemClickedSlot); - QString externalStorage = Tandroid::getExternalPath(); - if (mode == e_acceptSave && externalStorage == m_fileModel->rootPath()) { - // Ask to create Nootka folder but only when file dialog is called with external storage path (first launch) - if (!externalStorage.isEmpty() && !QFileInfo::exists(externalStorage + QLatin1String("/Nootka"))) - QTimer::singleShot(200, this, [=] { createNootkaDir(); }); - } -} + // Adjust width of menu list according to widest text + QTimer::singleShot(100, this, [=] { + m_menu->setFixedWidth(m_menu->sizeHintForColumn(0) + 2 * m_menu->frameWidth()); + }); + QString externalStorage = Tandroid::getExternalPath(); + if (mode == e_acceptSave && externalStorage == m_fileModel->rootPath()) { + // Ask to create Nootka folder but only when file dialog is called with external storage path (first launch) + if (!externalStorage.isEmpty() && !QFileInfo::exists(externalStorage + QLatin1String("/Nootka"))) + QTimer::singleShot(200, this, [=] { + createNootkaDir(); + }); + } +} TfileDialog::~TfileDialog() { - delete m_fileModel; + delete m_fileModel; } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -void TfileDialog::itemClickedSlot(const QModelIndex& index) { - if (m_fileModel->isDir(index)) { - m_list->setRootIndex(index); - m_fileModel->setRootPath(m_fileModel->filePath(index)); - updateLocationLabel(); - if (acceptMode() == e_acceptOpen) { - m_editName->setText(QString()); - m_selectedFile.clear(); // reset previously selected file - } - } else { - m_selectedFile = m_fileModel->fileInfo(index).absoluteFilePath(); - m_editName->setText(QFileInfo(m_selectedFile).fileName()); - } +void TfileDialog::itemClickedSlot(const QModelIndex &index) +{ + if (m_fileModel->isDir(index)) { + m_list->setRootIndex(index); + m_fileModel->setRootPath(m_fileModel->filePath(index)); + updateLocationLabel(); + if (acceptMode() == e_acceptOpen) { + m_editName->setText(QString()); + m_selectedFile.clear(); // reset previously selected file + } + } else { + m_selectedFile = m_fileModel->fileInfo(index).absoluteFilePath(); + m_editName->setText(QFileInfo(m_selectedFile).fileName()); + } } - -void TfileDialog::performAction() { - if (acceptMode() == e_acceptSave) { - if (m_editName->text().isEmpty()) { - QMessageBox::warning(this, QString(), qTR("QFileSystemModel", "Invalid filename")); - return; - } else { - m_selectedFile = m_fileModel->rootPath() + QLatin1String("/") + m_editName->text(); - QFileInfo fi(m_selectedFile); - bool addExt = false; // Add current file extension - if (!fi.suffix().isEmpty()) { - if (m_extensionCombo->currentIndex() > -1 && m_extensionCombo->currentText() != m_selectedFile.right(4)) - addExt = true; - } else - addExt = true; - if (addExt) { - m_selectedFile.append(m_extensionCombo->currentText()); - fi.setFile(m_selectedFile); - } - if (fi.exists()) { - if (QMessageBox::question(this, QString(), - qTR("QFileDialog", "%1 already exists.\nDo you want to replace it?").arg(m_selectedFile)) != QMessageBox::Ok) { - m_selectedFile.clear(); - return; +void TfileDialog::performAction() +{ + if (acceptMode() == e_acceptSave) { + if (m_editName->text().isEmpty()) { + QMessageBox::warning(this, QString(), qTR("QFileSystemModel", "Invalid filename")); + return; + } else { + m_selectedFile = m_fileModel->rootPath() + QLatin1String("/") + m_editName->text(); + QFileInfo fi(m_selectedFile); + bool addExt = false; // Add current file extension + if (!fi.suffix().isEmpty()) { + if (m_extensionCombo->currentIndex() > -1 && m_extensionCombo->currentText() != m_selectedFile.right(4)) + addExt = true; + } else + addExt = true; + if (addExt) { + m_selectedFile.append(m_extensionCombo->currentText()); + fi.setFile(m_selectedFile); + } + if (fi.exists()) { + if (QMessageBox::question(this, QString(), qTR("QFileDialog", "%1 already exists.\nDo you want to replace it?").arg(m_selectedFile)) + != QMessageBox::Ok) { + m_selectedFile.clear(); + return; + } + } } - } } - } - QDialog::accept(); + QDialog::accept(); } - -void TfileDialog::menuClickedSlot(QListWidgetItem* item) { - if (item == m_cancelItem) - reject(); - else if (item == m_acceptItem) - performAction(); - else if (item == m_dirUpItem) - dirUpSlot(); - else if (item == m_newDirItem) - newDirSlot(); - else if (m_menu->currentRow() == 3) { - itemClickedSlot(m_fileModel->index(Tandroid::getExternalPath())); - } +void TfileDialog::menuClickedSlot(QListWidgetItem *item) +{ + if (item == m_cancelItem) + reject(); + else if (item == m_acceptItem) + performAction(); + else if (item == m_dirUpItem) + dirUpSlot(); + else if (item == m_newDirItem) + newDirSlot(); + else if (m_menu->currentRow() == 3) { + itemClickedSlot(m_fileModel->index(Tandroid::getExternalPath())); + } } - -void TfileDialog::dirUpSlot() { - QDir dir(m_fileModel->rootDirectory()); - if (dir.cdUp()) { - m_fileModel->setRootPath(dir.absolutePath()); - m_list->setRootIndex(m_fileModel->index(dir.absolutePath())); - updateLocationLabel(); - } +void TfileDialog::dirUpSlot() +{ + QDir dir(m_fileModel->rootDirectory()); + if (dir.cdUp()) { + m_fileModel->setRootPath(dir.absolutePath()); + m_list->setRootIndex(m_fileModel->index(dir.absolutePath())); + updateLocationLabel(); + } } - -void TfileDialog::newDirSlot() { - createNewDir(TnewDirMessage::dirName(this)); +void TfileDialog::newDirSlot() +{ + createNewDir(TnewDirMessage::dirName(this)); } - -void TfileDialog::createNewDir(const QString& newDir) { - if (!newDir.isEmpty()) { - auto dirIndex = m_fileModel->mkdir(m_list->rootIndex(), newDir); - if (dirIndex.isValid()) - itemClickedSlot(dirIndex); - else - QMessageBox::warning(this, - m_newDirItem->text(), qTR("QFtp", "Creating directory failed:\n%1").arg(newDir)); - } +void TfileDialog::createNewDir(const QString &newDir) +{ + if (!newDir.isEmpty()) { + auto dirIndex = m_fileModel->mkdir(m_list->rootIndex(), newDir); + if (dirIndex.isValid()) + itemClickedSlot(dirIndex); + else + QMessageBox::warning(this, m_newDirItem->text(), qTR("QFtp", "Creating directory failed:\n%1").arg(newDir)); + } } - -QListWidgetItem* TfileDialog::addMenuItem(const QIcon& icon, const QString& text) { - auto menuButton = new QListWidgetItem(m_menu); - menuButton->setIcon(icon); - if (!text.isEmpty()) - menuButton->setText(text); - menuButton->setTextAlignment(Qt::AlignHCenter); - menuButton->setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren); - return menuButton; +QListWidgetItem *TfileDialog::addMenuItem(const QIcon &icon, const QString &text) +{ + auto menuButton = new QListWidgetItem(m_menu); + menuButton->setIcon(icon); + if (!text.isEmpty()) + menuButton->setText(text); + menuButton->setTextAlignment(Qt::AlignHCenter); + menuButton->setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren); + return menuButton; } - -void TfileDialog::updateLocationLabel() { - m_locationLab->setText(fontMetrics().elidedText(m_fileModel->rootPath() + QLatin1String("/"), - Qt::ElideMiddle, m_locationLab->width(), Qt::TextShowMnemonic)); +void TfileDialog::updateLocationLabel() +{ + m_locationLab->setText( + fontMetrics().elidedText(m_fileModel->rootPath() + QLatin1String("/"), Qt::ElideMiddle, m_locationLab->width(), Qt::TextShowMnemonic)); } - -void TfileDialog::createNootkaDir() { - if (QMessageBox::question(this, QString(), - tr("Directory named <b>Nootka</b> will be created in<br>%1<br>Application files will be written there.").arg(m_fileModel->rootPath())) - == QMessageBox::Yes) - createNewDir(QLatin1String("Nootka")); +void TfileDialog::createNootkaDir() +{ + if (QMessageBox::question( + this, + QString(), + tr("Directory named <b>Nootka</b> will be created in<br>%1<br>Application files will be written there.").arg(m_fileModel->rootPath())) + == QMessageBox::Yes) + createNewDir(QLatin1String("Nootka")); } #else - -QString TfileDialog::getOpenFileName(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { - QString fN; - auto f = qApp->font(); - qApp->setFont(Tmtr::systemFont); - fN = QFileDialog::getOpenFileName(nullptr, caption, dir, filter, selectedFilter, options); - qApp->setFont(f); - return fN; +QString TfileDialog::getOpenFileName(const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + QString fN; + auto f = qApp->font(); + qApp->setFont(Tmtr::systemFont); + fN = QFileDialog::getOpenFileName(nullptr, caption, dir, filter, selectedFilter, options); + qApp->setFont(f); + return fN; } - -QString TfileDialog::getSaveFileName(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { - QString fN; - auto f = qApp->font(); - qApp->setFont(Tmtr::systemFont); - fN = QFileDialog::getSaveFileName(nullptr, caption, dir, filter, selectedFilter, options); - qApp->setFont(f); - return fN; +QString TfileDialog::getSaveFileName(const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + QString fN; + auto f = qApp->font(); + qApp->setFont(Tmtr::systemFont); + fN = QFileDialog::getSaveFileName(nullptr, caption, dir, filter, selectedFilter, options); + qApp->setFont(f); + return fN; } - - #endif - diff --git a/src/libs/core/Android/tfiledialog.h b/src/libs/core/Android/tfiledialog.h index 6b5e704443089625c50ed92e0b15eee2bd7e5c53..de62144b87b6cdafe47f16c5c704907566701817 100644 --- a/src/libs/core/Android/tfiledialog.h +++ b/src/libs/core/Android/tfiledialog.h @@ -19,16 +19,13 @@ #ifndef TFILEDIALOG_H #define TFILEDIALOG_H - #include <QtCore/qglobal.h> +#if defined(Q_OS_ANDROID) -#if defined (Q_OS_ANDROID) - +#include <QtWidgets/qboxlayout.h> #include <QtWidgets/qdialog.h> #include <QtWidgets/qfilesystemmodel.h> -#include <QtWidgets/qboxlayout.h> - class QListWidget; class QListWidgetItem; @@ -37,7 +34,6 @@ class QLineEdit; class QLabel; class QComboBox; - /** * Implementation of file dialog for mobile devices. * It is far from ideal but much better than native QFileDialog. @@ -51,67 +47,64 @@ class QComboBox; */ class TfileDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - /** - * File dialog type: for opening or saving - */ - enum EacceptMode { e_acceptSave, e_acceptOpen }; - Q_ENUM(EacceptMode) - - explicit TfileDialog(const QString& directory, const QString& filter, EacceptMode mode); - ~TfileDialog() override; + /** + * File dialog type: for opening or saving + */ + enum EacceptMode { e_acceptSave, e_acceptOpen }; + Q_ENUM(EacceptMode) - static QString getSaveFileName(const QString& directory = QString(), const QString& filter = QString()); - static QString getOpenFileName(const QString& directory = QString(), const QString& filter = QString()); + explicit TfileDialog(const QString &directory, const QString &filter, EacceptMode mode); + ~TfileDialog() override; - QString selectedFile() const { return m_selectedFile; } + static QString getSaveFileName(const QString &directory = QString(), const QString &filter = QString()); + static QString getOpenFileName(const QString &directory = QString(), const QString &filter = QString()); - /** - * Left to right by default - */ - void setDirection(QBoxLayout::Direction dir) { m_lay->setDirection(dir); } - EacceptMode acceptMode() { return m_acceptMode; } + QString selectedFile() const { return m_selectedFile; } + /** + * Left to right by default + */ + void setDirection(QBoxLayout::Direction dir) { m_lay->setDirection(dir); } + EacceptMode acceptMode() { return m_acceptMode; } protected: - void itemClickedSlot(const QModelIndex& index); - void menuClickedSlot(QListWidgetItem* item); - void dirUpSlot(); - void newDirSlot(); - void performAction(); /**< Open or save */ - void createNewDir(const QString& newDir); /**< Creates new directory with name @p newDir in current model node */ + void itemClickedSlot(const QModelIndex &index); + void menuClickedSlot(QListWidgetItem *item); + void dirUpSlot(); + void newDirSlot(); + void performAction(); /**< Open or save */ + void createNewDir(const QString &newDir); /**< Creates new directory with name @p newDir in current model node */ /** * Displays message about possibility of creating Nootka folder on external card (if exists - or internal then). * If user agreed - creates the directory. */ - void createNootkaDir(); + void createNootkaDir(); - QListWidgetItem* addMenuItem(const QIcon& icon, const QString& text = QString()); - void updateLocationLabel(); + QListWidgetItem *addMenuItem(const QIcon &icon, const QString &text = QString()); + void updateLocationLabel(); private: - QFileSystemModel *m_fileModel; - QString m_selectedFile; - EacceptMode m_acceptMode; - - QBoxLayout *m_lay; - QListView *m_list; - QListWidget *m_menu; - QListWidgetItem *m_acceptItem, *m_cancelItem, *m_dirUpItem, *m_newDirItem; - QLineEdit *m_editName; - QLabel *m_locationLab; - QComboBox *m_extensionCombo; - + QFileSystemModel *m_fileModel; + QString m_selectedFile; + EacceptMode m_acceptMode; + + QBoxLayout *m_lay; + QListView *m_list; + QListWidget *m_menu; + QListWidgetItem *m_acceptItem, *m_cancelItem, *m_dirUpItem, *m_newDirItem; + QLineEdit *m_editName; + QLabel *m_locationLab; + QComboBox *m_extensionCombo; }; #else -#include <QtWidgets/qfiledialog.h> #include "nootkacoreglobal.h" - +#include <QtWidgets/qfiledialog.h> /** * Provides methods to get open/save file name, @@ -121,12 +114,18 @@ private: */ class NOOTKACORE_EXPORT TfileDialog { - public: - static QString getOpenFileName(const QString &caption = QString(), const QString &dir = QString(), - const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = QFlag(0)); - static QString getSaveFileName(const QString &caption = QString(), const QString &dir = QString(), - const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = QFlag(0)); + static QString getOpenFileName(const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = nullptr, + QFileDialog::Options options = QFlag(0)); + + static QString getSaveFileName(const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = nullptr, + QFileDialog::Options options = QFlag(0)); }; #endif diff --git a/src/libs/core/checktime.h b/src/libs/core/checktime.h index 1292f051dfbaf9b2178f8049c584c8291854a144..a5557cc45db85107cdd8d2fc7361ddc8613b32fc 100644 --- a/src/libs/core/checktime.h +++ b/src/libs/core/checktime.h @@ -10,12 +10,9 @@ * (...) * ) */ -#define CONCAT_(x,y) x##y -#define CONCAT(x,y) CONCAT_(x,y) -#define CHECKTIME(x) \ - QElapsedTimer CONCAT(sb_, __LINE__); \ - CONCAT(sb_, __LINE__).start(); \ - x \ - qDebug() << __FILE__ << __FUNCTION__ << ":" << __LINE__ << " Elapsed time: " << CONCAT(sb_, __LINE__).nsecsElapsed() / 1000000.0 << " ms."; - - +#define CONCAT_(x, y) x##y +#define CONCAT(x, y) CONCAT_(x, y) +#define CHECKTIME(x) \ + QElapsedTimer CONCAT(sb_, __LINE__); \ + CONCAT(sb_, __LINE__).start(); \ + x qDebug() << __FILE__ << __FUNCTION__ << ":" << __LINE__ << " Elapsed time: " << CONCAT(sb_, __LINE__).nsecsElapsed() / 1000000.0 << " ms."; diff --git a/src/libs/core/exam/tattempt.cpp b/src/libs/core/exam/tattempt.cpp index ab3788d354864754e99044d5559c12674b7ed686..2a7c9000f6f106c38268c04998ff500cf1b0b593 100644 --- a/src/libs/core/exam/tattempt.cpp +++ b/src/libs/core/exam/tattempt.cpp @@ -20,96 +20,87 @@ #include "tqaunit.h" #include <QtCore/qvariant.h> - -Tattempt::Tattempt() : - m_playedCounter(0), - m_sum(0), - m_totalTime(0), - m_prepTime(0) +Tattempt::Tattempt() + : m_playedCounter(0) + , m_sum(0) + , m_totalTime(0) + , m_prepTime(0) { } - -void Tattempt::add(quint32 mistake) { - mistakes << mistake; - m_sum |= mistake; +void Tattempt::add(quint32 mistake) +{ + mistakes << mistake; + m_sum |= mistake; } - Tattempt::~Tattempt() -{} - - -void Tattempt::updateEffectiveness() { - if (mistakes.size()) { - qreal effSum = 0.0; - m_sum = 0; - for (int i = 0; i < mistakes.size(); ++i) { - quint32 m = mistakes[i]; - m_sum |= m; - if (m == TQAunit::e_correct) - effSum += CORRECT_EFF; - else if (!(m & TQAunit::e_wrongNote) && !(m & TQAunit::e_wrongPos)) - effSum += NOTBAD_EFF; - } - m_effectiveness = effSum / (qreal)mistakes.size(); - } else - m_effectiveness = 0.0; +{ +} - if (effectiveness() >= 50.0) { - if (m_sum & TQAunit::e_wrongNote) { // subtract e_wrongNote if summary has sufficient effectiveness - m_sum = m_sum - TQAunit::e_wrongNote; // attempt was successful - m_sum |= TQAunit::e_littleNotes; // but has little valid notes +void Tattempt::updateEffectiveness() +{ + if (mistakes.size()) { + qreal effSum = 0.0; + m_sum = 0; + for (int i = 0; i < mistakes.size(); ++i) { + quint32 m = mistakes[i]; + m_sum |= m; + if (m == TQAunit::e_correct) + effSum += CORRECT_EFF; + else if (!(m & TQAunit::e_wrongNote) && !(m & TQAunit::e_wrongPos)) + effSum += NOTBAD_EFF; + } + m_effectiveness = effSum / (qreal)mistakes.size(); + } else + m_effectiveness = 0.0; + + if (effectiveness() >= 50.0) { + if (m_sum & TQAunit::e_wrongNote) { // subtract e_wrongNote if summary has sufficient effectiveness + m_sum = m_sum - TQAunit::e_wrongNote; // attempt was successful + m_sum |= TQAunit::e_littleNotes; // but has little valid notes + } } - } } - -void Tattempt::toXml(QXmlStreamWriter& xml) const { - xml.writeStartElement(QLatin1String("a")); // a like attempt +void Tattempt::toXml(QXmlStreamWriter &xml) const +{ + xml.writeStartElement(QLatin1String("a")); // a like attempt if (mistakes.size()) { - xml.writeStartElement(QLatin1String("mistakes")); - for (int i = 0; i < mistakes.size(); ++i) - xml.writeTextElement(QLatin1String("m"), QVariant(mistakes[i]).toString()); - xml.writeEndElement(); // mistakes + xml.writeStartElement(QLatin1String("mistakes")); + for (int i = 0; i < mistakes.size(); ++i) + xml.writeTextElement(QLatin1String("m"), QVariant(mistakes[i]).toString()); + xml.writeEndElement(); // mistakes } if (m_playedCounter) - xml.writeTextElement(QLatin1String("p"), QVariant(m_playedCounter).toString()); + xml.writeTextElement(QLatin1String("p"), QVariant(m_playedCounter).toString()); if (m_totalTime) - xml.writeTextElement(QLatin1String("tt"), QVariant(m_totalTime).toString()); + xml.writeTextElement(QLatin1String("tt"), QVariant(m_totalTime).toString()); if (m_prepTime) - xml.writeTextElement(QLatin1String("pt"), QVariant(m_prepTime).toString()); - xml.writeEndElement(); // a + xml.writeTextElement(QLatin1String("pt"), QVariant(m_prepTime).toString()); + xml.writeEndElement(); // a } - -void Tattempt::fromXml(QXmlStreamReader& xml) { - m_playedCounter = 0; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("mistakes")) { - mistakes.clear(); - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("m")) - mistakes << xml.readElementText().toInt(); - else +void Tattempt::fromXml(QXmlStreamReader &xml) +{ + m_playedCounter = 0; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("mistakes")) { + mistakes.clear(); + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("m")) + mistakes << xml.readElementText().toInt(); + else + xml.skipCurrentElement(); + } + updateEffectiveness(); + } else if (xml.name() == QLatin1String("p")) + m_playedCounter = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("tt")) + m_totalTime = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("pt")) + m_prepTime = xml.readElementText().toInt(); + else xml.skipCurrentElement(); - } - updateEffectiveness(); - } else if (xml.name() == QLatin1String("p")) - m_playedCounter = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("tt")) - m_totalTime = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("pt")) - m_prepTime = xml.readElementText().toInt(); - else - xml.skipCurrentElement(); - } + } } - - - - - - - - diff --git a/src/libs/core/exam/tattempt.h b/src/libs/core/exam/tattempt.h index 3d943cd04f6edd0170d8229bf2199cbf313e6b92..da8e59c97fe00cd06661869f9f444b951b4717a3 100644 --- a/src/libs/core/exam/tattempt.h +++ b/src/libs/core/exam/tattempt.h @@ -20,9 +20,8 @@ #define TATTEMPT_H #include "nootkacoreglobal.h" -#include <QtCore/qxmlstream.h> #include <QtCore/qlist.h> - +#include <QtCore/qxmlstream.h> /** * This class describes an attempt to 'resolve/guess' an exercise/exam question @@ -31,88 +30,85 @@ */ class NOOTKACORE_EXPORT Tattempt { - public: - Tattempt(); - - virtual ~Tattempt(); - - /** - * Adds time and mistake of a note to the lists. - */ - void add(quint32 mistake); - - /** - * Type of mistake of every note in a melody - */ - QList<quint32> mistakes; - - /** - * How many times user played or listened to a question melody. - */ - quint16 playedCount() const { return m_playedCounter; } - - /** - * Increases playback counter - */ - void melodyWasPlayed() { m_playedCounter++; } - - /** - * @p TRUE when no mistakes and times in the lists and playback counter is 0 - */ - bool isEmpty() const { return mistakes.isEmpty() && m_playedCounter == 0; } - - /** - * Total answer time of this attempt. - */ - quint32 totalTime() const { return m_totalTime; } - void setTotalTime(quint32 tt) { m_totalTime = tt; } - - /** - * Time spent to prepare playing answer. Between question time start and detected first played note. - */ - quint32 prepareTime() const { return m_prepTime; } - void setPrepareTime(quint32 prepTime) { m_prepTime = prepTime; } - - /** - * Logical sum of all mistakes in the attempt - */ - quint32 summary() const { return m_sum; } - - /** - * All notes were correct. - */ - bool isCorrect() const { return m_sum == 0; } - - /** - * Some note(s) are 'not bad' but none is wrong. - */ - bool isNotBad() { return m_sum && !(m_sum & 64 || m_sum & 16); } - - /** - * Some note(s) are wrong. - */ - bool isWrong() { return m_sum & 64; } - - void toXml(QXmlStreamWriter& xml) const; - void fromXml(QXmlStreamReader& xml); - - /** - * Effectiveness of the attempt. - */ - qreal effectiveness() const { return m_effectiveness; } - - /** - * Calculates an effectiveness from mistakes, and logical mistakes sum. - */ - void updateEffectiveness(); + Tattempt(); + + virtual ~Tattempt(); + + /** + * Adds time and mistake of a note to the lists. + */ + void add(quint32 mistake); + + /** + * Type of mistake of every note in a melody + */ + QList<quint32> mistakes; + + /** + * How many times user played or listened to a question melody. + */ + quint16 playedCount() const { return m_playedCounter; } + + /** + * Increases playback counter + */ + void melodyWasPlayed() { m_playedCounter++; } + + /** + * @p TRUE when no mistakes and times in the lists and playback counter is 0 + */ + bool isEmpty() const { return mistakes.isEmpty() && m_playedCounter == 0; } + + /** + * Total answer time of this attempt. + */ + quint32 totalTime() const { return m_totalTime; } + void setTotalTime(quint32 tt) { m_totalTime = tt; } + + /** + * Time spent to prepare playing answer. Between question time start and detected first played note. + */ + quint32 prepareTime() const { return m_prepTime; } + void setPrepareTime(quint32 prepTime) { m_prepTime = prepTime; } + + /** + * Logical sum of all mistakes in the attempt + */ + quint32 summary() const { return m_sum; } + + /** + * All notes were correct. + */ + bool isCorrect() const { return m_sum == 0; } + + /** + * Some note(s) are 'not bad' but none is wrong. + */ + bool isNotBad() { return m_sum && !(m_sum & 64 || m_sum & 16); } + + /** + * Some note(s) are wrong. + */ + bool isWrong() { return m_sum & 64; } + + void toXml(QXmlStreamWriter &xml) const; + void fromXml(QXmlStreamReader &xml); + + /** + * Effectiveness of the attempt. + */ + qreal effectiveness() const { return m_effectiveness; } + + /** + * Calculates an effectiveness from mistakes, and logical mistakes sum. + */ + void updateEffectiveness(); private: - - quint16 m_playedCounter; - qreal m_effectiveness; - quint32 m_sum, m_totalTime, m_prepTime; - + quint16 m_playedCounter; + qreal m_effectiveness; + quint32 m_sum, m_totalTime, m_prepTime; }; #endif // TATTEMPT_H diff --git a/src/libs/core/exam/texam.cpp b/src/libs/core/exam/texam.cpp index 245bdcf5e0a07d092078b1811088bb7e26fc3a87..2ac93d1f56dbb98691dc7d41cc4ac0f7c5a2be95 100644 --- a/src/libs/core/exam/texam.cpp +++ b/src/libs/core/exam/texam.cpp @@ -17,24 +17,22 @@ ***************************************************************************/ #include "texam.h" -#include "tlevel.h" #include "tattempt.h" #include "tglobals.h" -#include <QtCore/qfile.h> +#include "tlevel.h" #include <QtCore/qdatastream.h> #include <QtCore/qdatetime.h> #include <QtCore/qdir.h> -#include <QtCore/qsettings.h> +#include <QtCore/qfile.h> #include <QtCore/qrandom.h> +#include <QtCore/qsettings.h> #include <QtWidgets/qmessagebox.h> -#if defined (Q_OS_ANDROID) - #include <Android/tandroid.h> +#if defined(Q_OS_ANDROID) +#include <Android/tandroid.h> #endif #include <QtCore/qdebug.h> - - /*static*/ /** * Versions history: @@ -64,680 +62,686 @@ const qint32 Texam::currentVersion = 0x9512170C; // version 5 const quint16 Texam::maxAnswerTime = 65500; -int Texam::examVersionNr(qint32 ver) { - if ((ver - examVersion) % 2) - return -1; // invalid when rest of division is 1 - return ((ver - examVersion) / 2) + 1 ; +int Texam::examVersionNr(qint32 ver) +{ + if ((ver - examVersion) % 2) + return -1; // invalid when rest of division is 1 + return ((ver - examVersion) / 2) + 1; } - -bool Texam::couldBeExam(qint32 ver) { - int givenVersion = examVersionNr(ver); - if (givenVersion >= 1 && givenVersion <= 127) - return true; - else - return false; +bool Texam::couldBeExam(qint32 ver) +{ + int givenVersion = examVersionNr(ver); + if (givenVersion >= 1 && givenVersion <= 127) + return true; + else + return false; } - -bool Texam::isExamVersion(qint32 ver) { - if (examVersionNr(ver) <= examVersionNr(currentVersion)) - return true; - else - return false; +bool Texam::isExamVersion(qint32 ver) +{ + if (examVersionNr(ver) <= examVersionNr(currentVersion)) + return true; + else + return false; } /** Obsolete! It has no meaning in XML versions (above 3) */ -qint32 Texam::examVersionToLevel(qint32 examVer) { - if (examVersionNr(examVer) <= 2) - return Tlevel::getVersionId(1); // level version 1 for exam versions 1 and 2 - else - return Tlevel::getVersionId(2); // level version 2 for exam versions 3 and so +qint32 Texam::examVersionToLevel(qint32 examVer) +{ + if (examVersionNr(examVer) <= 2) + return Tlevel::getVersionId(1); // level version 1 for exam versions 1 and 2 + else + return Tlevel::getVersionId(2); // level version 2 for exam versions 3 and so } - -bool Texam::areQuestTheSame(TQAunit* q1, TQAunit* q2) { - if (q1->questionAs == q2->questionAs && // the same questions - q1->answerAs == q2->answerAs && // the same answers - q1->qa.note == q2->qa.note && // the same notes - q1->qa.pos() == q2->qa.pos() // the same frets - ) - return true; - else - return false; +bool Texam::areQuestTheSame(TQAunit *q1, TQAunit *q2) +{ + if (q1->questionAs == q2->questionAs && // the same questions + q1->answerAs == q2->answerAs && // the same answers + q1->qa.note == q2->qa.note && // the same notes + q1->qa.pos() == q2->qa.pos() // the same frets + ) + return true; + else + return false; } - -QString Texam::formatReactTime(quint16 timeX10, bool withUnit) { - QString hh, mm, ss; - int dig = 0; - if (timeX10 / 36000) { - hh = QString("%1").arg(timeX10 / 36000); - dig = 2; - } - int dig2 = 0; - if ((timeX10 % 36000) / 600) { - mm = QString("%1").arg((timeX10 % 36000) / 600, dig, 'i', 0, '0'); - dig2 = 2; - } - ss = QString("%1").arg(((timeX10 % 36000) % 600) / 10, dig2, 'i', 0, '0' ); - QString res; - if (!hh.isEmpty()) - res = hh + QLatin1String(":"); - if (!mm.isEmpty()) - res += mm + QLatin1String(":"); - QString unitS; - if (withUnit && timeX10 < 600) - unitS = QLatin1String(" s"); - return res + ss + QString(".%1").arg(timeX10 % 10) + unitS; +QString Texam::formatReactTime(quint16 timeX10, bool withUnit) +{ + QString hh, mm, ss; + int dig = 0; + if (timeX10 / 36000) { + hh = QString("%1").arg(timeX10 / 36000); + dig = 2; + } + int dig2 = 0; + if ((timeX10 % 36000) / 600) { + mm = QString("%1").arg((timeX10 % 36000) / 600, dig, 'i', 0, '0'); + dig2 = 2; + } + ss = QString("%1").arg(((timeX10 % 36000) % 600) / 10, dig2, 'i', 0, '0'); + QString res; + if (!hh.isEmpty()) + res = hh + QLatin1String(":"); + if (!mm.isEmpty()) + res += mm + QLatin1String(":"); + QString unitS; + if (withUnit && timeX10 < 600) + unitS = QLatin1String(" s"); + return res + ss + QString(".%1").arg(timeX10 % 10) + unitS; } /*end of static*/ -//################################################################################################# -//################### CONSTRUCTOR ############################################ -//################################################################################################# -Texam::Texam(Tlevel* l, const QString& userName): - m_fileName(QString()), - m_userName(userName), - m_totalTime(0), m_attempts(0), - m_mistNr(0), m_tmpMist(0), - m_averReactTime(0), m_workTime(0), - m_halfMistNr(0), m_tmpHalf(0), - m_isFinished(false), m_melody(false), m_isExercise(false), - m_penaltysNr(0), - m_blackCount(0), - m_okTime(0), - m_effectivenes(0.0), - m_skippedUnit(nullptr) +// ################################################################################################# +// ################### CONSTRUCTOR ############################################ +// ################################################################################################# +Texam::Texam(Tlevel *l, const QString &userName) + : m_fileName(QString()) + , m_userName(userName) + , m_totalTime(0) + , m_attempts(0) + , m_mistNr(0) + , m_tmpMist(0) + , m_averReactTime(0) + , m_workTime(0) + , m_halfMistNr(0) + , m_tmpHalf(0) + , m_isFinished(false) + , m_melody(false) + , m_isExercise(false) + , m_penaltysNr(0) + , m_blackCount(0) + , m_okTime(0) + , m_effectivenes(0.0) + , m_skippedUnit(nullptr) { - setLevel(l); + setLevel(l); } - Texam::~Texam() { - clearAnswList(); - m_blackList.clear(); - m_blackNumbers.clear(); - if (m_skippedUnit) - delete m_skippedUnit; + clearAnswList(); + m_blackList.clear(); + m_blackNumbers.clear(); + if (m_skippedUnit) + delete m_skippedUnit; } -//################################################################################################# -//################### PUBLIC ############################################ -//################################################################################################# -void Texam::setExercise() { - if (count()) { - qDebug() << "[Texam] Exam has got questions already. Can't set it as an exercise!"; - return; - } - setFileName(QDir::toNativeSeparators(QFileInfo(GLOB->config->fileName()).absolutePath() + QLatin1String("/exercise2.noo"))); - m_isExercise = true; +// ################################################################################################# +// ################### PUBLIC ############################################ +// ################################################################################################# +void Texam::setExercise() +{ + if (count()) { + qDebug() << "[Texam] Exam has got questions already. Can't set it as an exercise!"; + return; + } + setFileName(QDir::toNativeSeparators(QFileInfo(GLOB->config->fileName()).absolutePath() + QLatin1String("/exercise2.noo"))); + m_isExercise = true; } - -void Texam::setLevel(Tlevel* l) { - m_level = l; - m_melody = l->canBeMelody(); +void Texam::setLevel(Tlevel *l) +{ + m_level = l; + m_melody = l->canBeMelody(); } - -void Texam::setFileName(const QString& fileName) { - if (isExercise()) { - qDebug() << "[Texam] Can not set a file name for exercise"; - return; - } - m_fileName = fileName; +void Texam::setFileName(const QString &fileName) +{ + if (isExercise()) { + qDebug() << "[Texam] Can not set a file name for exercise"; + return; + } + m_fileName = fileName; } - -void Texam::skipLast(bool skip) { - if (skip != (bool)m_skippedUnit) { - if (skip) { - if (m_skippedUnit) - qDebug() << "[Texam] Previously skipped question unit will be overridden by newly skipped."; - delete m_skippedUnit; - m_skippedUnit = m_answList.takeLast(); - } else { - if (!m_skippedUnit) - qDebug() << "[Texam] There is no skipped unit to revert it back!"; - else { - m_answList << m_skippedUnit; - m_skippedUnit = 0; +void Texam::skipLast(bool skip) +{ + if (skip != (bool)m_skippedUnit) { + if (skip) { + if (m_skippedUnit) + qDebug() << "[Texam] Previously skipped question unit will be overridden by newly skipped."; + delete m_skippedUnit; + m_skippedUnit = m_answList.takeLast(); + } else { + if (!m_skippedUnit) + qDebug() << "[Texam] There is no skipped unit to revert it back!"; + else { + m_answList << m_skippedUnit; + m_skippedUnit = 0; + } } } - } } - -Texam::EerrorType Texam::loadFromFile(const QString& fileName) { -#if defined (Q_OS_ANDROID) - Tandroid::askForWriteAcces(); +Texam::EerrorType Texam::loadFromFile(const QString &fileName) +{ +#if defined(Q_OS_ANDROID) + Tandroid::askForWriteAcces(); #endif - m_okTime = 0; - m_tmpMist = 0; - m_tmpHalf = 0; - m_fileName = fileName; - QFile file(fileName); - m_workTime = 0; - m_mistNr = 0; - m_blackCount = 0; - m_attempts = 0; - m_isExercise = false; - m_blackList.clear(); - clearAnswList(); - EerrorType result = e_file_OK; - quint32 ev; //exam template version - if (file.open(QIODevice::ReadOnly)) { - QDataStream in(&file); - in >> ev; - if (couldBeExam(ev)) { - if (!isExamVersion(ev)) { - qDebug() << "[Texam] Exam file" << fileName << "created with newer Nootka version"; - GLOB->warnAboutNewerVersion(fileName); - return e_newerVersion; - } - } else - return e_file_not_valid; - - bool isExamFileOk = true; - if (examVersionNr(ev) > 3) { - if (examVersionNr(ev) > 5) - in.setVersion(QDataStream::Qt_5_9); - else - in.setVersion(QDataStream::Qt_5_2); - QByteArray arrayXML = file.readAll(); - arrayXML.remove(0, 4); - QByteArray unZipXml = qUncompress(arrayXML); - if (!unZipXml.isEmpty()) { - QXmlStreamReader xml(unZipXml); - isExamFileOk = loadFromXml(xml); - } else { - qDebug() << "[Texam] Problems with decompressing exam file"; - return e_file_not_valid; - } - } else { - in.setVersion(QDataStream::Qt_4_7); - isExamFileOk = loadFromBin(in, ev); - } - - m_melody = m_level->canBeMelody(); - updateEffectiveness(); - updateAverageReactTime(true); - - if (!isExamFileOk) - result = e_file_corrupted; - file.close(); - } else { - Tlevel::fileIOerrorMsg(file); - result = e_cant_open; - } - updateBlackCount(); - - if (m_level->clef.type() == Tclef::Bass_F_8down) { - qDebug() << "[Texam] OBSOLETE bass dropped clef detected. Converting exam to ordinary bass clef."; - transposeAfterBassDropped(); - } - - return result; -} + m_okTime = 0; + m_tmpMist = 0; + m_tmpHalf = 0; + m_fileName = fileName; + QFile file(fileName); + m_workTime = 0; + m_mistNr = 0; + m_blackCount = 0; + m_attempts = 0; + m_isExercise = false; + m_blackList.clear(); + clearAnswList(); + EerrorType result = e_file_OK; + quint32 ev; // exam template version + if (file.open(QIODevice::ReadOnly)) { + QDataStream in(&file); + in >> ev; + if (couldBeExam(ev)) { + if (!isExamVersion(ev)) { + qDebug() << "[Texam] Exam file" << fileName << "created with newer Nootka version"; + GLOB->warnAboutNewerVersion(fileName); + return e_newerVersion; + } + } else + return e_file_not_valid; + + bool isExamFileOk = true; + if (examVersionNr(ev) > 3) { + if (examVersionNr(ev) > 5) + in.setVersion(QDataStream::Qt_5_9); + else + in.setVersion(QDataStream::Qt_5_2); + QByteArray arrayXML = file.readAll(); + arrayXML.remove(0, 4); + QByteArray unZipXml = qUncompress(arrayXML); + if (!unZipXml.isEmpty()) { + QXmlStreamReader xml(unZipXml); + isExamFileOk = loadFromXml(xml); + } else { + qDebug() << "[Texam] Problems with decompressing exam file"; + return e_file_not_valid; + } + } else { + in.setVersion(QDataStream::Qt_4_7); + isExamFileOk = loadFromBin(in, ev); + } + m_melody = m_level->canBeMelody(); + updateEffectiveness(); + updateAverageReactTime(true); -bool Texam::loadFromBin(QDataStream& in, quint32 ev) { - quint16 questNr; - bool isExamFileOk = true; - in >> m_userName; - getLevelFromStream(in, *(m_level), examVersionToLevel(ev)); - in >> m_tune; - in >> m_totalTime; - in >> questNr >> m_averReactTime >> m_mistNr; - if (examVersionNr(ev) >= 2) { - in >> m_halfMistNr >> m_penaltysNr >> m_isFinished; - } else { // exam version 1 - m_halfMistNr = 0; - m_penaltysNr = 0; - m_isFinished = false; - } - while (!in.atEnd()) { - TQAunit qaUnit; - if (!getTQAunitFromStream(in, qaUnit)) - isExamFileOk = false; - if ((qaUnit.questionAs == TQAtype::e_asName || qaUnit.answerAs == TQAtype::e_asName) - && qaUnit.styleOfQuestion() < 0) { - qaUnit.setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), qaUnit.styleOfAnswer()); - } /** In old versions, style was set to 0 so now it gives styleOfQuestion = -1 - * Also in transition Nootka versions it was left unchanged. - * Unfixed it invokes stupid names in charts. - * We are fixing it by insert user preferred style of naming */ - if (qaUnit.time <= maxAnswerTime || ev == examVersion) { // add to m_answList - m_answList << new TQAunit(qaUnit); - grabFromLastUnit(); - } else { // add to m_blackList - m_blackList << qaUnit; + if (!isExamFileOk) + result = e_file_corrupted; + file.close(); + } else { + Tlevel::fileIOerrorMsg(file); + result = e_cant_open; } - } - if (!checkQuestionNumber(questNr)) { - isExamFileOk = false; - } - if (examVersionNr(ev) >= 2 && (m_tmpMist != m_mistNr || m_tmpHalf != m_halfMistNr)) { - m_mistNr = m_tmpMist; // we try to fix exam file to give proper number of mistakes - m_halfMistNr = m_tmpHalf; - isExamFileOk = false; - } else { - m_mistNr = m_tmpMist; // transition to exam version 2 - } - if (ev == examVersion) { - convertToVersion2(); - m_halfMistNr = m_tmpHalf; - } - return isExamFileOk; -} + updateBlackCount(); + if (m_level->clef.type() == Tclef::Bass_F_8down) { + qDebug() << "[Texam] OBSOLETE bass dropped clef detected. Converting exam to ordinary bass clef."; + transposeAfterBassDropped(); + } + return result; +} -bool Texam::loadFromXml(QXmlStreamReader& xml) { - bool ok = true; - int questNr = 0; - if (xml.readNextStartElement()) { - if (xml.name() != QLatin1String("exam")) { - qDebug() << "[Texam] There is no 'exam' key in that XML"; - return false; +bool Texam::loadFromBin(QDataStream &in, quint32 ev) +{ + quint16 questNr; + bool isExamFileOk = true; + in >> m_userName; + getLevelFromStream(in, *(m_level), examVersionToLevel(ev)); + in >> m_tune; + in >> m_totalTime; + in >> questNr >> m_averReactTime >> m_mistNr; + if (examVersionNr(ev) >= 2) { + in >> m_halfMistNr >> m_penaltysNr >> m_isFinished; + } else { // exam version 1 + m_halfMistNr = 0; + m_penaltysNr = 0; + m_isFinished = false; } - m_userName = xml.attributes().value(QStringLiteral("user")).toString(); - if (m_userName.isEmpty() || m_userName.size() > 30) { - qDebug() << "[Texam] Exam has wrong user name"; - return false; + while (!in.atEnd()) { + TQAunit qaUnit; + if (!getTQAunitFromStream(in, qaUnit)) + isExamFileOk = false; + if ((qaUnit.questionAs == TQAtype::e_asName || qaUnit.answerAs == TQAtype::e_asName) && qaUnit.styleOfQuestion() < 0) { + qaUnit.setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), qaUnit.styleOfAnswer()); + } /** In old versions, style was set to 0 so now it gives styleOfQuestion = -1 + * Also in transition Nootka versions it was left unchanged. + * Unfixed it invokes stupid names in charts. + * We are fixing it by insert user preferred style of naming */ + if (qaUnit.time <= maxAnswerTime || ev == examVersion) { // add to m_answList + m_answList << new TQAunit(qaUnit); + grabFromLastUnit(); + } else { // add to m_blackList + m_blackList << qaUnit; + } + } + if (!checkQuestionNumber(questNr)) { + isExamFileOk = false; + } + if (examVersionNr(ev) >= 2 && (m_tmpMist != m_mistNr || m_tmpHalf != m_halfMistNr)) { + m_mistNr = m_tmpMist; // we try to fix exam file to give proper number of mistakes + m_halfMistNr = m_tmpHalf; + isExamFileOk = false; + } else { + m_mistNr = m_tmpMist; // transition to exam version 2 + } + if (ev == examVersion) { + convertToVersion2(); + m_halfMistNr = m_tmpHalf; + } + return isExamFileOk; +} + +bool Texam::loadFromXml(QXmlStreamReader &xml) +{ + bool ok = true; + int questNr = 0; + if (xml.readNextStartElement()) { + if (xml.name() != QLatin1String("exam")) { + qDebug() << "[Texam] There is no 'exam' key in that XML"; + return false; + } + m_userName = xml.attributes().value(QStringLiteral("user")).toString(); + if (m_userName.isEmpty() || m_userName.size() > 30) { + qDebug() << "[Texam] Exam has wrong user name"; + return false; + } } - } - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("head")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("level")) { - auto err = m_level->loadFromXml(xml); - if (err != Tlevel::e_level_OK) { - qDebug() << "[Texam] Exam has wrong level definition" << static_cast<int>(err); + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("head")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("level")) { + auto err = m_level->loadFromXml(xml); + if (err != Tlevel::e_level_OK) { + qDebug() << "[Texam] Exam has wrong level definition" << static_cast<int>(err); + ok = false; + } + } else if (xml.name() == QLatin1String("tuning")) { + if (!m_tune.fromXml(xml, true)) { + qDebug() << "[Texam] Exam has wrong tuning"; + ok = false; + } + } else if (xml.name() == QLatin1String("totalTime")) + m_totalTime = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("questNr")) + questNr = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("averReactTime")) + m_averReactTime = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("mistNr")) + m_mistNr = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("halfMistNr")) + m_halfMistNr = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("penaltysNr")) + m_penaltysNr = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("finished")) + m_isFinished = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("exercise")) { + m_isExercise = true; + xml.skipCurrentElement(); + } else + Tlevel::skipCurrentXmlKey(xml); + } + } else if (xml.name() == QLatin1String("answers")) { + if (!readAnswerFromXml(m_answList, xml)) ok = false; - } - } else if (xml.name() == QLatin1String("tuning")) { - if (!m_tune.fromXml(xml, true)) { - qDebug() << "[Texam] Exam has wrong tuning"; + } else if (xml.name() == QLatin1String("penalties")) { + if (!readPenaltyFromXml(m_blackList, xml)) ok = false; - } - } else if (xml.name() == QLatin1String("totalTime")) - m_totalTime = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("questNr")) - questNr = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("averReactTime")) - m_averReactTime = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("mistNr")) - m_mistNr = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("halfMistNr")) - m_halfMistNr = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("penaltysNr")) - m_penaltysNr = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("finished")) - m_isFinished = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("exercise")) { - m_isExercise = true; - xml.skipCurrentElement(); - } else - Tlevel::skipCurrentXmlKey(xml); - } - } else if (xml.name() == QLatin1String("answers")) { - if (!readAnswerFromXml(m_answList, xml)) - ok = false; - } else if (xml.name() == QLatin1String("penalties")) { - if (!readPenaltyFromXml(m_blackList, xml)) - ok = false; - } else if (xml.name() == QLatin1String("black")) { - m_blackNumbers.clear(); - while (xml.readNextStartElement()) { - if (xml.name() == "n") - m_blackNumbers << xml.readElementText().toInt(); - else + } else if (xml.name() == QLatin1String("black")) { + m_blackNumbers.clear(); + while (xml.readNextStartElement()) { + if (xml.name() == "n") + m_blackNumbers << xml.readElementText().toInt(); + else + Tlevel::skipCurrentXmlKey(xml); + } + } else Tlevel::skipCurrentXmlKey(xml); - } - } else - Tlevel::skipCurrentXmlKey(xml); - } - if (!checkQuestionNumber(questNr)) { - ok = false; - } - if (m_tmpMist != m_mistNr || m_tmpHalf != m_halfMistNr) { - if (m_tmpMist != m_mistNr) - qDebug() << "[Texam] Mistakes number do not match. Exam file corrupted!" << m_tmpMist << m_mistNr; - else if (m_tmpHalf != m_halfMistNr) - qDebug() << "[Texam] 'Not bad' number do not match. Exam file corrupted!" << m_tmpHalf << m_halfMistNr; - m_mistNr = m_tmpMist; // we try to fix exam file to give proper number of mistakes - m_halfMistNr = m_tmpHalf; - ok = false; - } - return ok; + } + if (!checkQuestionNumber(questNr)) { + ok = false; + } + if (m_tmpMist != m_mistNr || m_tmpHalf != m_halfMistNr) { + if (m_tmpMist != m_mistNr) + qDebug() << "[Texam] Mistakes number do not match. Exam file corrupted!" << m_tmpMist << m_mistNr; + else if (m_tmpHalf != m_halfMistNr) + qDebug() << "[Texam] 'Not bad' number do not match. Exam file corrupted!" << m_tmpHalf << m_halfMistNr; + m_mistNr = m_tmpMist; // we try to fix exam file to give proper number of mistakes + m_halfMistNr = m_tmpHalf; + ok = false; + } + return ok; } - -Texam::EerrorType Texam::saveToFile(const QString& fileName) { - if (!fileName.isEmpty()) - setFileName(fileName); // m_fileName becomes fileName - if (m_fileName.isEmpty()) - return e_noFileName; - - QFile file(m_fileName); - if (file.open(QIODevice::WriteOnly)) { - QDataStream out(&file); - out.setVersion(QDataStream::Qt_5_9); - out << currentVersion; - QByteArray arrayXML; - QXmlStreamWriter xml(&arrayXML); - // xml.setAutoFormatting(true); - xml.writeStartDocument(); - xml.writeComment("\nXML file of Nootka exam data.\nhttps://nootka.sf.net\nThis file should never be opened in other software then Nootka.\nProbably you are doing something illegal!"); - writeToXml(xml); - xml.writeEndDocument(); - - out << qCompress(arrayXML); - - file.close(); - } else { - QMessageBox::critical(nullptr, QString(), QObject::tr("Cannot save exam file:\n%1").arg(QString::fromLocal8Bit(qPrintable(file.errorString())))); - return e_cant_open; - } - qDebug() << "[Texam] Exam saved to:" << m_fileName; - return e_file_OK; +Texam::EerrorType Texam::saveToFile(const QString &fileName) +{ + if (!fileName.isEmpty()) + setFileName(fileName); // m_fileName becomes fileName + if (m_fileName.isEmpty()) + return e_noFileName; + + QFile file(m_fileName); + if (file.open(QIODevice::WriteOnly)) { + QDataStream out(&file); + out.setVersion(QDataStream::Qt_5_9); + out << currentVersion; + QByteArray arrayXML; + QXmlStreamWriter xml(&arrayXML); + // xml.setAutoFormatting(true); + xml.writeStartDocument(); + xml.writeComment( + "\nXML file of Nootka exam data.\nhttps://nootka.sf.net\nThis file should never be opened in other software then Nootka.\nProbably you are doing " + "something illegal!"); + writeToXml(xml); + xml.writeEndDocument(); + + out << qCompress(arrayXML); + + file.close(); + } else { + QMessageBox::critical(nullptr, QString(), QObject::tr("Cannot save exam file:\n%1").arg(QString::fromLocal8Bit(qPrintable(file.errorString())))); + return e_cant_open; + } + qDebug() << "[Texam] Exam saved to:" << m_fileName; + return e_file_OK; } - -void Texam::writeToXml(QXmlStreamWriter& xml) { - xml.writeStartElement(QStringLiteral("exam")); +void Texam::writeToXml(QXmlStreamWriter &xml) +{ + xml.writeStartElement(QStringLiteral("exam")); xml.writeAttribute(QStringLiteral("user"), m_userName); xml.writeStartElement(QStringLiteral("head")); - m_level->writeToXml(xml); - m_tune.toXml(xml, true); - xml.writeTextElement(QStringLiteral("totalTime"), QVariant(m_totalTime).toString()); - xml.writeTextElement(QStringLiteral("questNr"), QVariant(count()).toString()); - xml.writeTextElement(QStringLiteral("averReactTime"), QVariant(m_averReactTime).toString()); - xml.writeTextElement(QStringLiteral("mistNr"), QVariant(m_mistNr).toString()); - xml.writeTextElement(QStringLiteral("halfMistNr"), QVariant(m_halfMistNr).toString()); - xml.writeTextElement(QStringLiteral("penaltysNr"), QVariant(m_penaltysNr).toString()); - xml.writeTextElement(QStringLiteral("finished"), QVariant(m_isFinished).toString()); - if (isExercise()) + m_level->writeToXml(xml); + m_tune.toXml(xml, true); + xml.writeTextElement(QStringLiteral("totalTime"), QVariant(m_totalTime).toString()); + xml.writeTextElement(QStringLiteral("questNr"), QVariant(count()).toString()); + xml.writeTextElement(QStringLiteral("averReactTime"), QVariant(m_averReactTime).toString()); + xml.writeTextElement(QStringLiteral("mistNr"), QVariant(m_mistNr).toString()); + xml.writeTextElement(QStringLiteral("halfMistNr"), QVariant(m_halfMistNr).toString()); + xml.writeTextElement(QStringLiteral("penaltysNr"), QVariant(m_penaltysNr).toString()); + xml.writeTextElement(QStringLiteral("finished"), QVariant(m_isFinished).toString()); + if (isExercise()) xml.writeEmptyElement(QStringLiteral("exercise")); xml.writeEndElement(); // head xml.writeStartElement(QStringLiteral("answers")); for (int i = 0; i < count(); ++i) - m_answList[i]->toXml(xml); + m_answList[i]->toXml(xml); xml.writeEndElement(); // answers if (m_blackList.size()) { xml.writeStartElement(QStringLiteral("penalties")); for (int i = 0; i < m_blackList.size(); ++i) - m_blackList[i].toXml(xml); + m_blackList[i].toXml(xml); xml.writeEndElement(); // penalties } else if (m_blackNumbers.size()) { xml.writeStartElement(QStringLiteral("black")); for (int i = 0; i < m_blackNumbers.size(); ++i) - xml.writeTextElement(QStringLiteral("n"), QString::number(m_blackNumbers[i])); + xml.writeTextElement(QStringLiteral("n"), QString::number(m_blackNumbers[i])); xml.writeEndElement(); // penalties } - xml.writeEndElement(); // exam + xml.writeEndElement(); // exam } - -void Texam::newAttempt() { - curQ()->newAttempt(); - if (curQ()->attemptsCount() > 1) { // unset answered and revert mistakes - user tries once more - if (curQ()->isNotSoBad()) - m_halfMistNr--; - else if (curQ()->isWrong()) - m_mistNr--; - else - qDebug() << "[Texam] A new attempt called for correct answer!"; - curQ()->unsetAnswered(); - } -} - - -void Texam::sumarizeAnswer() { - curQ()->updateEffectiveness(); - curQ()->time = qMin(maxAnswerTime, curQ()->time); // when user think too much - if (melodies()) { - m_workTime += curQ()->lastAttempt()->totalTime(); - if (!curQ()->isWrong()) { - if (curQ()->effectiveness() < 50) - curQ()->setMistake(TQAunit::e_veryPoor); - else if (curQ()->effectiveness() < 70) - curQ()->setMistake(TQAunit::e_poorEffect); +void Texam::newAttempt() +{ + curQ()->newAttempt(); + if (curQ()->attemptsCount() > 1) { // unset answered and revert mistakes - user tries once more + if (curQ()->isNotSoBad()) + m_halfMistNr--; + else if (curQ()->isWrong()) + m_mistNr--; + else + qDebug() << "[Texam] A new attempt called for correct answer!"; + curQ()->unsetAnswered(); } - m_attempts++; - } - updateAverageReactTime(true); - if (melodies()) { - if (curQ()->isNotSoBad()) - m_halfMistNr++; - else if (curQ()->isWrong()) - m_mistNr++; - } else { - addPenalties(); // for melodies it should be invoked after ensuring that answer was finished - if (!isExercise()) - updateBlackCount(); - m_workTime += curQ()->time; - } - updateEffectiveness(); } - -void Texam::addPenalties() { - if (!curQ()->isCorrect()) { - if (melodies()) // for any kind of mistake add one more random melody or mistaken one if not random and melody set - m_blackNumbers.append(m_level->isMelodySet() && !m_level->randOrderInSet ? count() - 1 : -1); - if (curQ()->isNotSoBad()) { - if (!isExercise() && !isFinished()) - m_penaltysNr++; - if (!melodies()) - m_halfMistNr++; +void Texam::sumarizeAnswer() +{ + curQ()->updateEffectiveness(); + curQ()->time = qMin(maxAnswerTime, curQ()->time); // when user think too much + if (melodies()) { + m_workTime += curQ()->lastAttempt()->totalTime(); + if (!curQ()->isWrong()) { + if (curQ()->effectiveness() < 50) + curQ()->setMistake(TQAunit::e_veryPoor); + else if (curQ()->effectiveness() < 70) + curQ()->setMistake(TQAunit::e_poorEffect); + } + m_attempts++; + } + updateAverageReactTime(true); + if (melodies()) { + if (curQ()->isNotSoBad()) + m_halfMistNr++; + else if (curQ()->isWrong()) + m_mistNr++; } else { - if (melodies()) - m_blackNumbers.append(count() - 1); // repeat current melody in some further question - if (!isExercise() && !isFinished()) - m_penaltysNr += 2; - if (!melodies()) - m_mistNr++; + addPenalties(); // for melodies it should be invoked after ensuring that answer was finished + if (!isExercise()) + updateBlackCount(); + m_workTime += curQ()->time; } - } -} - - -void Texam::updateEffectiveness() { - qreal sum = 0.0; - for (int i = 0; i < count(); ++i) - sum += answList()->at(i)->effectiveness(); - m_effectivenes = sum / (qreal)count(); + updateEffectiveness(); } - -void Texam::updateAverageReactTime(bool skipWrong) { - int totalTime = 0; - int cnt = 0; - for (int i = 0; i < count(); ++i) { - if (!skipWrong || (skipWrong && !m_answList[i]->isWrong())) { - totalTime += m_answList[i]->time; - cnt++; +void Texam::addPenalties() +{ + if (!curQ()->isCorrect()) { + if (melodies()) // for any kind of mistake add one more random melody or mistaken one if not random and melody set + m_blackNumbers.append(m_level->isMelodySet() && !m_level->randOrderInSet ? count() - 1 : -1); + if (curQ()->isNotSoBad()) { + if (!isExercise() && !isFinished()) + m_penaltysNr++; + if (!melodies()) + m_halfMistNr++; + } else { + if (melodies()) + m_blackNumbers.append(count() - 1); // repeat current melody in some further question + if (!isExercise() && !isFinished()) + m_penaltysNr += 2; + if (!melodies()) + m_mistNr++; + } } - } - if (cnt) - m_averReactTime = totalTime / cnt; - else - m_averReactTime = 0; } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -void Texam::updateBlackCount() { - m_blackCount = 0; - if (m_blackList.size()) { - for (int i = 0; i < m_blackList.size(); i++) - m_blackCount += (m_blackList[i].time - maxAnswerTime); - } +void Texam::updateEffectiveness() +{ + qreal sum = 0.0; + for (int i = 0; i < count(); ++i) + sum += answList()->at(i)->effectiveness(); + m_effectivenes = sum / (qreal)count(); } - -bool Texam::readPenaltyFromXml(QList<TQAunit>& blackList, QXmlStreamReader& xml) { - bool ok = true; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("u")) { - blackList << TQAunit(this); - if (!blackList.last().fromXml(xml)) { - qDebug() << "[Texam] Exam has wrong unit" << blackList.size(); - blackList.removeLast(); - ok = false; +void Texam::updateAverageReactTime(bool skipWrong) +{ + int totalTime = 0; + int cnt = 0; + for (int i = 0; i < count(); ++i) { + if (!skipWrong || (skipWrong && !m_answList[i]->isWrong())) { + totalTime += m_answList[i]->time; + cnt++; } - } else - Tlevel::skipCurrentXmlKey(xml); - } - return ok; + } + if (cnt) + m_averReactTime = totalTime / cnt; + else + m_averReactTime = 0; } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -bool Texam::readAnswerFromXml(QList<TQAunit*>& list, QXmlStreamReader& xml) { - bool ok = true; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("u")) { - list << new TQAunit(this); - if (list.last()->fromXml(xml)) { - grabFromLastUnit(); - if (melodies()) - m_attempts += curQ()->attemptsCount(); - } else { - qDebug() << "[Texam] Exam has wrong unit" << list.size(); - list.removeLast(); - ok = false; - } - } else - Tlevel::skipCurrentXmlKey(xml); - } - return ok; +void Texam::updateBlackCount() +{ + m_blackCount = 0; + if (m_blackList.size()) { + for (int i = 0; i < m_blackList.size(); i++) + m_blackCount += (m_blackList[i].time - maxAnswerTime); + } } - -void Texam::grabFromLastUnit() { - m_workTime += curQ()->time; - if (!curQ()->isCorrect()) { - if (curQ()->isWrong()) - m_tmpMist++; - else - m_tmpHalf++; // not so bad answer +bool Texam::readPenaltyFromXml(QList<TQAunit> &blackList, QXmlStreamReader &xml) +{ + bool ok = true; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("u")) { + blackList << TQAunit(this); + if (!blackList.last().fromXml(xml)) { + qDebug() << "[Texam] Exam has wrong unit" << blackList.size(); + blackList.removeLast(); + ok = false; + } + } else + Tlevel::skipCurrentXmlKey(xml); } - if (!curQ()->isWrong()) - m_okTime += curQ()->time; + return ok; } - -bool Texam::checkQuestionNumber(int questNr) { - bool ok = true; - if (questNr != m_answList.size()) { - qDebug() << "[Texam] Exam questions number read from file" << questNr - << "and those calculated" << m_answList.size() << "do not match. Exam file corrupted."; - ok = false; - } - return ok; +bool Texam::readAnswerFromXml(QList<TQAunit *> &list, QXmlStreamReader &xml) +{ + bool ok = true; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("u")) { + list << new TQAunit(this); + if (list.last()->fromXml(xml)) { + grabFromLastUnit(); + if (melodies()) + m_attempts += curQ()->attemptsCount(); + } else { + qDebug() << "[Texam] Exam has wrong unit" << list.size(); + list.removeLast(); + ok = false; + } + } else + Tlevel::skipCurrentXmlKey(xml); + } + return ok; } - -void Texam::clearAnswList() { - for (int i = 0; i < m_answList.size(); ++i) - delete m_answList[i]; - m_answList.clear(); +void Texam::grabFromLastUnit() +{ + m_workTime += curQ()->time; + if (!curQ()->isCorrect()) { + if (curQ()->isWrong()) + m_tmpMist++; + else + m_tmpHalf++; // not so bad answer + } + if (!curQ()->isWrong()) + m_okTime += curQ()->time; } +bool Texam::checkQuestionNumber(int questNr) +{ + bool ok = true; + if (questNr != m_answList.size()) { + qDebug() << "[Texam] Exam questions number read from file" << questNr << "and those calculated" << m_answList.size() + << "do not match. Exam file corrupted."; + ok = false; + } + return ok; +} -void Texam::transposeAfterBassDropped() { - if (m_tune.type() == Ttune::Custom) - m_tune.riseOctaveUp(); - m_level->convFromDropedBass(); - for (int a = 0; a < m_answList.size(); ++a) - m_answList[a]->riseOctaveUp(); - for (int b = 0; b < m_blackList.size(); ++b) - m_blackList[b].riseOctaveUp(); +void Texam::clearAnswList() +{ + for (int i = 0; i < m_answList.size(); ++i) + delete m_answList[i]; + m_answList.clear(); } +void Texam::transposeAfterBassDropped() +{ + if (m_tune.type() == Ttune::Custom) + m_tune.riseOctaveUp(); + m_level->convFromDropedBass(); + for (int a = 0; a < m_answList.size(); ++a) + m_answList[a]->riseOctaveUp(); + for (int b = 0; b < m_blackList.size(); ++b) + m_blackList[b].riseOctaveUp(); +} /** * This method exist for backward compatibility but is has very rare usage in 'modern' Nootka times */ -void Texam::convertToVersion2() { - bool hasStyle = false; - Tnote::EnameStyle randStyles[3]; - if (m_level->canBeName()) { - // version 1 didn't put proper Tnote::EnameStyle to file - we fixing it - hasStyle = true; - qDebug() << "[Texam] Fixing styles of note names in file"; - if (m_level->requireStyle) { // prepare styles array to imitate switching - randStyles[0] = Tnote::e_italiano_Si; - if (GLOB->seventhIsB()) { - randStyles[1] = Tnote::e_english_Bb; - randStyles[2] = Tnote::e_nederl_Bis; - } else { - randStyles[1] = Tnote::e_norsk_Hb; - randStyles[2] = Tnote::e_deutsch_His; - } - } - } - - for (int i = 0; i < m_answList.size(); i++) { - if (m_answList[i]->time > maxAnswerTime) // fix too long times from version 1 if any - m_answList[i]->time = maxAnswerTime; - // version 1 didn't put proper Tnote::EnameStyle to file - we fixing it - if (hasStyle) { - if (m_level->requireStyle) { - if (m_answList[i]->questionAs == TQAtype::e_asName && m_answList[i]->answerAs == TQAtype::e_asName) { - Tnote::EnameStyle qSt = randStyles[QRandomGenerator::global()->bounded(3)]; - Tnote::EnameStyle aSt; - if (qSt == Tnote::e_italiano_Si) - aSt = randStyles[QRandomGenerator::global()->bounded(2) +1]; - else - aSt = Tnote::e_italiano_Si; - m_answList[i]->setStyle(qSt, aSt); - } else if (m_answList[i]->questionAs == TQAtype::e_asName) { - m_answList[i]->setStyle(randStyles[QRandomGenerator::global()->bounded(3)], static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle())); - } else { - if (m_answList[i]->questionAs == TQAtype::e_asName) - m_answList[i]->setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), randStyles[QRandomGenerator::global()->bounded(3)]); - } - } else // fixed style - we changing to user preferred - m_answList[i]->setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle())); +void Texam::convertToVersion2() +{ + bool hasStyle = false; + Tnote::EnameStyle randStyles[3]; + if (m_level->canBeName()) { + // version 1 didn't put proper Tnote::EnameStyle to file - we fixing it + hasStyle = true; + qDebug() << "[Texam] Fixing styles of note names in file"; + if (m_level->requireStyle) { // prepare styles array to imitate switching + randStyles[0] = Tnote::e_italiano_Si; + if (GLOB->seventhIsB()) { + randStyles[1] = Tnote::e_english_Bb; + randStyles[2] = Tnote::e_nederl_Bis; + } else { + randStyles[1] = Tnote::e_norsk_Hb; + randStyles[2] = Tnote::e_deutsch_His; + } + } } - if (!m_answList[i]->isCorrect()) { - quint16 penCnt = 0; // counts of penalties - if (m_answList[i]->isWrong()) { - if (i < (m_answList.size() -1) && areQuestTheSame(m_answList[i], m_answList[i+1])) { - // there was next question repeated - if (m_answList[i+1]->isCorrect()) // and was correct - penCnt = 65501; // so add one penalty - else // when again wrong - penCnt = 65502; // add two - // The next loop will add next two penalties !! - } else // question was not repeated - penCnt = 65502; - } else { // not so bad - if (i < (m_answList.size() -1) && areQuestTheSame(m_answList[i], m_answList[i+1])) { - // there was next question repeated - if (m_answList[i+1]->isCorrect()) // and was correct - penCnt = 0; - else - penCnt = 65501; - } - } - if (penCnt) { - m_blackList << *m_answList[i]; - m_blackList.last().time = penCnt; - m_penaltysNr += (penCnt - 65500); - } + for (int i = 0; i < m_answList.size(); i++) { + if (m_answList[i]->time > maxAnswerTime) // fix too long times from version 1 if any + m_answList[i]->time = maxAnswerTime; + // version 1 didn't put proper Tnote::EnameStyle to file - we fixing it + if (hasStyle) { + if (m_level->requireStyle) { + if (m_answList[i]->questionAs == TQAtype::e_asName && m_answList[i]->answerAs == TQAtype::e_asName) { + Tnote::EnameStyle qSt = randStyles[QRandomGenerator::global()->bounded(3)]; + Tnote::EnameStyle aSt; + if (qSt == Tnote::e_italiano_Si) + aSt = randStyles[QRandomGenerator::global()->bounded(2) + 1]; + else + aSt = Tnote::e_italiano_Si; + m_answList[i]->setStyle(qSt, aSt); + } else if (m_answList[i]->questionAs == TQAtype::e_asName) { + m_answList[i]->setStyle(randStyles[QRandomGenerator::global()->bounded(3)], static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle())); + } else { + if (m_answList[i]->questionAs == TQAtype::e_asName) + m_answList[i]->setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), randStyles[QRandomGenerator::global()->bounded(3)]); + } + } else // fixed style - we changing to user preferred + m_answList[i]->setStyle(static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle()), static_cast<Tnote::EnameStyle>(GLOB->noteNameStyle())); + } + + if (!m_answList[i]->isCorrect()) { + quint16 penCnt = 0; // counts of penalties + if (m_answList[i]->isWrong()) { + if (i < (m_answList.size() - 1) && areQuestTheSame(m_answList[i], m_answList[i + 1])) { + // there was next question repeated + if (m_answList[i + 1]->isCorrect()) // and was correct + penCnt = 65501; // so add one penalty + else // when again wrong + penCnt = 65502; // add two + // The next loop will add next two penalties !! + } else // question was not repeated + penCnt = 65502; + } else { // not so bad + if (i < (m_answList.size() - 1) && areQuestTheSame(m_answList[i], m_answList[i + 1])) { + // there was next question repeated + if (m_answList[i + 1]->isCorrect()) // and was correct + penCnt = 0; + else + penCnt = 65501; + } + } + if (penCnt) { + m_blackList << *m_answList[i]; + m_blackList.last().time = penCnt; + m_penaltysNr += (penCnt - 65500); + } + } } - } } - - - diff --git a/src/libs/core/exam/texam.h b/src/libs/core/exam/texam.h index 726cb652bd0e64126382a2fd005b3b3c5556d6d3..5f9084b44e2d4c5113dafe6b83d493e5a82db914 100644 --- a/src/libs/core/exam/texam.h +++ b/src/libs/core/exam/texam.h @@ -19,306 +19,302 @@ #ifndef TEXAM_H #define TEXAM_H - +#include "music/ttune.h" #include "nootkacoreglobal.h" -#include <QtCore/qxmlstream.h> -#include <QtCore/qpointer.h> #include "tqaunit.h" -#include "music/ttune.h" - +#include <QtCore/qpointer.h> +#include <QtCore/qxmlstream.h> class Tlevel; - /** -* This class describes instance of an exam. -*/ + * This class describes instance of an exam. + */ class NOOTKACORE_EXPORT Texam { - public: /** - * An exam constructor. - * @p Tlevel has to be pointer to existing exam level - */ - explicit Texam(Tlevel *l, const QString& userName); - ~Texam(); + * An exam constructor. + * @p Tlevel has to be pointer to existing exam level + */ + explicit Texam(Tlevel *l, const QString &userName); + ~Texam(); /** Magic numbers in exam file to identify it.*/ - static const qint32 examVersion; - static const qint32 examVersion2; - static const qint32 currentVersion; - - /** - * Returns true when given value match to exam versions. - */ - static bool isExamVersion(qint32 ver); - - /** - * Returns number of exam version like 1, 2 or so or -1 when given version is not valid. - */ - static int examVersionNr(qint32 ver); - - /** - * Returns true when given value 'could be' or 'it is' some version of exam file. - * This way exams created with newer Nootka version can be detected. - */ - static bool couldBeExam(qint32 ver); - - /** - * Generates exam version identifier number from given simple version number (1, 2 etc.) - * It doesn't verify does given number make sense! - */ - static qint32 getVersionId(quint8 verNr) { return examVersion + (verNr - 1) * 2; } - - /** - * Returns level version number used in given exam version - * It doesn't check is given exam version valid!. - */ - static qint32 examVersionToLevel(qint32 examVer); - - /** - * Returns nice formatted time (1:05:15.3). Time is in seconds multiplied by 10. - * When @p withUnit is true adds s (seconds) unit - */ - static QString formatReactTime(quint16 timeX10, bool withUnit = false); - - /** - * Maximal time of an answer = 65500. Values over are equal to it. - * 65501 & 65502 are for counting probes in blackList - */ - static const quint16 maxAnswerTime; - - /** - * Compares given questions are they the same. - */ - static bool areQuestTheSame(TQAunit* q1, TQAunit* q2); - - /** - * @p EerrorType are possible errors during opening and saving exam file. - */ - enum EerrorType { - e_file_OK = 0, - e_file_not_valid, /**< occurs when @p examVersion is different */ - e_file_corrupted, /**< when data in file is corrupted */ - e_cant_open, /**< problems with reading file */ - e_noFileName, - e_newerVersion /**< when file version is newer than this Nootka version can support */ - }; - - Tlevel* level() { return m_level; } - void setLevel(Tlevel *l); - Ttune tune() { return m_tune; } - void setTune(Ttune &tune) { m_tune = tune; } - - /** - * @p TRUE when level for melodies, otherwise it is single note. - * It is determined through @p setLevel() - */ - bool melodies() const { return m_melody; } - - /** - * Total number of attempts when melodies. Updated in @p sumarizeAnswer() - */ - quint32 attempts() const { return m_attempts; } - bool isExercise() const { return m_isExercise; } - - /** - * Makes exam an exercise (without penalties). Also sets default file name for exercises. - * It possible to set an exercise only for empty exam (without questions in the list yet). - */ - void setExercise(); - - quint32 totalTime() const { return m_totalTime; } - void setTotalTime(quint32 total) { m_totalTime = total; } - - /** - * Adds @p TQAunit object at the end of the questions list. - */ - void addQuestion(TQAunit &question) { m_answList << new TQAunit(question); } - - /** - * Checks the last TQAunit on the list, updates its effectiveness - * actualizes the exam effectiveness. - * Remember to add penalties to black list before invoke this !!! - */ - void sumarizeAnswer(); - - /** - * Check was answer correct and adds penalty(s) if necessary. - * For 'single note' answer it is invoked with @p sumarizeAnswer() - * but for melodies user has to decide about new attempt or new question first, - * so it has to be invoked only when new question is prepared. - * BE SURE IT IS CALLED ONCE PER WHOLE QUESTION! - */ - void addPenalties(); - - /** - * The last q/a unit - */ - TQAunit* curQ() { return m_answList.last(); } - QList<TQAunit*>* answList() { return &m_answList; } - - /** - * Returns number of questions/answers in en exam. - */ - int count() const { return m_answList.size(); } - - /** - * Returns number of committed mistakes in en exam. - */ - quint16 mistakes() const { return m_mistNr; } - - /** - * Number of 'not bad' answer in the exam. - */ - quint16 halfMistaken() const { return m_halfMistNr; } - - /** - * Number of correct answers - */ - quint16 corrects() const { return (count()) - mistakes() - halfMistaken(); } - - /** - * Average answer time - */ - quint16 averageReactonTime() const { return m_averReactTime; } - void setAverageReactonTime(quint16 avTime) { m_averReactTime = avTime; } - - /** - * Hides last question in the list. It is allowed only if question is not answered. - */ - void skipLast(bool skip); - - /** - * Iterates all questions to calculate average reaction time. - * When @p skipWrong - time of wrong answers is not calculated into. - */ - void updateAverageReactTime(bool skipWrong = false); - - /** - * Total time spent for answering without breaks between questions - */ - quint16 workTime() const { return qRound(static_cast<qreal>(m_workTime) / 10.0); } - QString userName() const { return m_userName; } - QString fileName() const { return m_fileName; } - void setFileName(const QString& fileName); - - /** - * Pointer to list with penalty questions. - */ - QList<TQAunit>* blacList() { return &m_blackList; } - - /** - * Pointer to list with numbers of question in which mistakes were committed. - */ - QList<int>* blackNumbers() { return &m_blackNumbers; } - - /** - * Number of penalties during whole exam - */ - int penalty() { return m_penaltysNr; } - - /** - * Remained questions in black list - */ - int blackCount() const { return m_blackCount; } - bool isFinished() const { return m_isFinished; } - - /** - * Sets exam as finished and there is no way back. - */ - void setFinished() { m_isFinished = true; } - void increasePenaltys(int penaltyNr) { m_penaltysNr += penaltyNr; } - - EerrorType loadFromFile(const QString &fileName); - EerrorType saveToFile(const QString& fileName = QString()); - - void writeToXml(QXmlStreamWriter& xml); - bool loadFromXml(QXmlStreamReader& xml); - bool loadFromBin(QDataStream& in, quint32 ev); - - /** - * For single note it is 100% - correct, 50% - 'not bad' and 0% - wrong - * For melody (attempt) it is calculated as average of all notes in the attempt - * and average of all attempts in single TQAunit. - * There are Tattempt::updateEffectiveness() and TQAunit::updateEffectiveness() - * to refresh their effectiveness but it is done automatically when melodies are compared. - * Current exam effectiveness is updated after sumarizeAnswer() - * but @p updateEffectiveness() can do it again. - */ - qreal effectiveness() { return m_effectivenes; } - - /** - * Iterates all answers to obtain average effectiveness. - */ - void updateEffectiveness(); - - /** - * Returns a reference to question/answer unit nr @param index. - * Beware! Index value is not checked - */ - TQAunit* question(unsigned int index) { return m_answList[index]; } - - void newAttempt(); + static const qint32 examVersion; + static const qint32 examVersion2; + static const qint32 currentVersion; + + /** + * Returns true when given value match to exam versions. + */ + static bool isExamVersion(qint32 ver); + + /** + * Returns number of exam version like 1, 2 or so or -1 when given version is not valid. + */ + static int examVersionNr(qint32 ver); + + /** + * Returns true when given value 'could be' or 'it is' some version of exam file. + * This way exams created with newer Nootka version can be detected. + */ + static bool couldBeExam(qint32 ver); + + /** + * Generates exam version identifier number from given simple version number (1, 2 etc.) + * It doesn't verify does given number make sense! + */ + static qint32 getVersionId(quint8 verNr) { return examVersion + (verNr - 1) * 2; } + + /** + * Returns level version number used in given exam version + * It doesn't check is given exam version valid!. + */ + static qint32 examVersionToLevel(qint32 examVer); + + /** + * Returns nice formatted time (1:05:15.3). Time is in seconds multiplied by 10. + * When @p withUnit is true adds s (seconds) unit + */ + static QString formatReactTime(quint16 timeX10, bool withUnit = false); + + /** + * Maximal time of an answer = 65500. Values over are equal to it. + * 65501 & 65502 are for counting probes in blackList + */ + static const quint16 maxAnswerTime; + + /** + * Compares given questions are they the same. + */ + static bool areQuestTheSame(TQAunit *q1, TQAunit *q2); + + /** + * @p EerrorType are possible errors during opening and saving exam file. + */ + enum EerrorType { + e_file_OK = 0, + e_file_not_valid, /**< occurs when @p examVersion is different */ + e_file_corrupted, /**< when data in file is corrupted */ + e_cant_open, /**< problems with reading file */ + e_noFileName, + e_newerVersion /**< when file version is newer than this Nootka version can support */ + }; + + Tlevel *level() { return m_level; } + void setLevel(Tlevel *l); + Ttune tune() { return m_tune; } + void setTune(Ttune &tune) { m_tune = tune; } + + /** + * @p TRUE when level for melodies, otherwise it is single note. + * It is determined through @p setLevel() + */ + bool melodies() const { return m_melody; } + + /** + * Total number of attempts when melodies. Updated in @p sumarizeAnswer() + */ + quint32 attempts() const { return m_attempts; } + bool isExercise() const { return m_isExercise; } + + /** + * Makes exam an exercise (without penalties). Also sets default file name for exercises. + * It possible to set an exercise only for empty exam (without questions in the list yet). + */ + void setExercise(); + + quint32 totalTime() const { return m_totalTime; } + void setTotalTime(quint32 total) { m_totalTime = total; } + + /** + * Adds @p TQAunit object at the end of the questions list. + */ + void addQuestion(TQAunit &question) { m_answList << new TQAunit(question); } + + /** + * Checks the last TQAunit on the list, updates its effectiveness + * actualizes the exam effectiveness. + * Remember to add penalties to black list before invoke this !!! + */ + void sumarizeAnswer(); + + /** + * Check was answer correct and adds penalty(s) if necessary. + * For 'single note' answer it is invoked with @p sumarizeAnswer() + * but for melodies user has to decide about new attempt or new question first, + * so it has to be invoked only when new question is prepared. + * BE SURE IT IS CALLED ONCE PER WHOLE QUESTION! + */ + void addPenalties(); + + /** + * The last q/a unit + */ + TQAunit *curQ() { return m_answList.last(); } + QList<TQAunit *> *answList() { return &m_answList; } + + /** + * Returns number of questions/answers in en exam. + */ + int count() const { return m_answList.size(); } + + /** + * Returns number of committed mistakes in en exam. + */ + quint16 mistakes() const { return m_mistNr; } + + /** + * Number of 'not bad' answer in the exam. + */ + quint16 halfMistaken() const { return m_halfMistNr; } + + /** + * Number of correct answers + */ + quint16 corrects() const { return (count())-mistakes() - halfMistaken(); } + + /** + * Average answer time + */ + quint16 averageReactonTime() const { return m_averReactTime; } + void setAverageReactonTime(quint16 avTime) { m_averReactTime = avTime; } + + /** + * Hides last question in the list. It is allowed only if question is not answered. + */ + void skipLast(bool skip); + + /** + * Iterates all questions to calculate average reaction time. + * When @p skipWrong - time of wrong answers is not calculated into. + */ + void updateAverageReactTime(bool skipWrong = false); + + /** + * Total time spent for answering without breaks between questions + */ + quint16 workTime() const { return qRound(static_cast<qreal>(m_workTime) / 10.0); } + QString userName() const { return m_userName; } + QString fileName() const { return m_fileName; } + void setFileName(const QString &fileName); + + /** + * Pointer to list with penalty questions. + */ + QList<TQAunit> *blacList() { return &m_blackList; } + + /** + * Pointer to list with numbers of question in which mistakes were committed. + */ + QList<int> *blackNumbers() { return &m_blackNumbers; } + + /** + * Number of penalties during whole exam + */ + int penalty() { return m_penaltysNr; } + + /** + * Remained questions in black list + */ + int blackCount() const { return m_blackCount; } + bool isFinished() const { return m_isFinished; } + + /** + * Sets exam as finished and there is no way back. + */ + void setFinished() { m_isFinished = true; } + void increasePenaltys(int penaltyNr) { m_penaltysNr += penaltyNr; } + + EerrorType loadFromFile(const QString &fileName); + EerrorType saveToFile(const QString &fileName = QString()); + + void writeToXml(QXmlStreamWriter &xml); + bool loadFromXml(QXmlStreamReader &xml); + bool loadFromBin(QDataStream &in, quint32 ev); + + /** + * For single note it is 100% - correct, 50% - 'not bad' and 0% - wrong + * For melody (attempt) it is calculated as average of all notes in the attempt + * and average of all attempts in single TQAunit. + * There are Tattempt::updateEffectiveness() and TQAunit::updateEffectiveness() + * to refresh their effectiveness but it is done automatically when melodies are compared. + * Current exam effectiveness is updated after sumarizeAnswer() + * but @p updateEffectiveness() can do it again. + */ + qreal effectiveness() { return m_effectivenes; } + + /** + * Iterates all answers to obtain average effectiveness. + */ + void updateEffectiveness(); + + /** + * Returns a reference to question/answer unit nr @param index. + * Beware! Index value is not checked + */ + TQAunit *question(unsigned int index) { return m_answList[index]; } + + void newAttempt(); protected: - /** - * Iterates through m_blackList to calculate number - */ - void updateBlackCount(); + /** + * Iterates through m_blackList to calculate number + */ + void updateBlackCount(); - /** - * Grabs answers with mistakes and creates black list - */ - void convertToVersion2(); + /** + * Grabs answers with mistakes and creates black list + */ + void convertToVersion2(); /** * Reads TQAunit from given XML and appends it at the end of given list or prints error and return @p FALSE */ - bool readAnswerFromXml(QList<TQAunit*>& list, QXmlStreamReader& xml); - bool readPenaltyFromXml(QList<TQAunit>& blackList, QXmlStreamReader& xml); + bool readAnswerFromXml(QList<TQAunit *> &list, QXmlStreamReader &xml); + bool readPenaltyFromXml(QList<TQAunit> &blackList, QXmlStreamReader &xml); - /** - * increases some counter variables to obtain checking values - */ - void grabFromLastUnit(); + /** + * increases some counter variables to obtain checking values + */ + void grabFromLastUnit(); - /** - * Compares number of units with number got from file and prints message if doesn't match. - */ - bool checkQuestionNumber(int questNr); + /** + * Compares number of units with number got from file and prints message if doesn't match. + */ + bool checkQuestionNumber(int questNr); - /** - * Wipes @p m_answList and all units pointed by it. - */ - void clearAnswList(); + /** + * Wipes @p m_answList and all units pointed by it. + */ + void clearAnswList(); - /** - * Transposes entire exam one octave up - * level, notes and melodies in all question units, - * tuning stored in the exam. - * Any occurrence of @p Tclef::Bass_F_8down is changed into @p Tclef::Bass_F - */ - void transposeAfterBassDropped(); + /** + * Transposes entire exam one octave up + * level, notes and melodies in all question units, + * tuning stored in the exam. + * Any occurrence of @p Tclef::Bass_F_8down is changed into @p Tclef::Bass_F + */ + void transposeAfterBassDropped(); private: - QString m_fileName, m_userName; - Tlevel *m_level; - QList<TQAunit*> m_answList; - QList<TQAunit> m_blackList; - QList<int> m_blackNumbers; /**< List of question numbers in which mistakes were committed. */ - Ttune m_tune; - quint32 m_totalTime, m_attempts; - quint16 m_mistNr, m_tmpMist, m_averReactTime, m_workTime, m_halfMistNr, m_tmpHalf; - bool m_isFinished, m_melody, m_isExercise; - int m_penaltysNr; - int m_blackCount; - int m_okTime; /**< time of correct and notBad answers to calculate average */ - qreal m_effectivenes; - TQAunit *m_skippedUnit; + QString m_fileName, m_userName; + Tlevel *m_level; + QList<TQAunit *> m_answList; + QList<TQAunit> m_blackList; + QList<int> m_blackNumbers; /**< List of question numbers in which mistakes were committed. */ + Ttune m_tune; + quint32 m_totalTime, m_attempts; + quint16 m_mistNr, m_tmpMist, m_averReactTime, m_workTime, m_halfMistNr, m_tmpHalf; + bool m_isFinished, m_melody, m_isExercise; + int m_penaltysNr; + int m_blackCount; + int m_okTime; /**< time of correct and notBad answers to calculate average */ + qreal m_effectivenes; + TQAunit *m_skippedUnit; }; -Q_DECLARE_METATYPE(Texam*) +Q_DECLARE_METATYPE(Texam *) #endif // TEXAM_H diff --git a/src/libs/core/exam/textrans.h b/src/libs/core/exam/textrans.h index 64cc3bec63c550c936621323a5a473b4b0ce80a7..361df5da317b93a136f1f78f143f0e921f34db3a 100644 --- a/src/libs/core/exam/textrans.h +++ b/src/libs/core/exam/textrans.h @@ -19,10 +19,9 @@ #ifndef TEXTRANS_H #define TEXTRANS_H - -#include <nootkacoreglobal.h> #include <QApplication> #include <QString> +#include <nootkacoreglobal.h> /** * This is static texts providing translations of common texts used in exams/exercises @@ -30,133 +29,115 @@ */ class NOOTKACORE_EXPORT TexTrans { - public: - TexTrans() {}; - + TexTrans() { }; - /** - * Average time taken to answer - */ -static const QString averAnsverTimeTxt() { - return QApplication::translate("TexamView", "Average time taken to answer"); - } + /** + * Average time taken to answer + */ + static const QString averAnsverTimeTxt() { return QApplication::translate("TexamView", "Average time taken to answer"); } -static const QString inSecondsTxt() { return QApplication::translate("TexamView", "[in seconds]"); } // [in seconds] + static const QString inSecondsTxt() { return QApplication::translate("TexamView", "[in seconds]"); } // [in seconds] /** * Total time */ -static const QString totalTimetxt() { return QApplication::translate("TexamView", "Total time"); } + static const QString totalTimetxt() { return QApplication::translate("TexamView", "Total time"); } /** * Effectiveness */ -static const QString effectTxt() { return QApplication::translate("TexamView", "Effectiveness"); } + static const QString effectTxt() { return QApplication::translate("TexamView", "Effectiveness"); } /** * Number of correct answers */ -static const QString corrAnswersNrTxt() { - return QApplication::translate("TexamView", "Number of correct answers"); - } + static const QString corrAnswersNrTxt() { return QApplication::translate("TexamView", "Number of correct answers"); } - /** - * Time for an answer - */ -static const QString reactTimeTxt() { return QApplication::translate("TexamView", "Time for an answer"); } + /** + * Time for an answer + */ + static const QString reactTimeTxt() { return QApplication::translate("TexamView", "Time for an answer"); } /** * Number of mistakes */ -static const QString mistakesNrTxt() { return QApplication::translate("TexamView", "Number of mistakes"); } + static const QString mistakesNrTxt() { return QApplication::translate("TexamView", "Number of mistakes"); } /** * Number of not bad answers */ -static const QString halfMistakenTxt() { - return QApplication::translate("TexamView", "'Not bad' answers"); - } - - /** - * (counted as half of a mistake) - */ -static const QString halfMistakenAddTxt() { - return QApplication::translate("TexamView", "(counted as half of a mistake)"); - } + static const QString halfMistakenTxt() { return QApplication::translate("TexamView", "'Not bad' answers"); } + /** + * (counted as half of a mistake) + */ + static const QString halfMistakenAddTxt() { return QApplication::translate("TexamView", "(counted as half of a mistake)"); } -static const QString examFilterTxt() { return QApplication::translate("TstartExamDlg", "Exam results") + " (*.noo)" ; } + static const QString examFilterTxt() { return QApplication::translate("TstartExamDlg", "Exam results") + " (*.noo)"; } /** * Load an exam file */ -static const QString loadExamFileTxt() { - return QApplication::translate("TstartExamDlg", "Load an exam file"); - } + static const QString loadExamFileTxt() { return QApplication::translate("TstartExamDlg", "Load an exam file"); } /** * Get more levels <a>from Nootka home page</a> */ -static QString moreLevelLinkTxt() { - return QApplication::translate("LevelsPage" ,"Get more levels <a href=\"%1\">from Nootka home page</a>") - .arg("https://nootka.sourceforge.io/index.php?C=down#levels"); - } + static QString moreLevelLinkTxt() + { + return QApplication::translate("LevelsPage", "Get more levels <a href=\"%1\">from Nootka home page</a>") + .arg("https://nootka.sourceforge.io/index.php?C=down#levels"); + } /** * play melody */ -static QString playMelodyTxt() { return QApplication::translate("Texam", "play melody"); } + static QString playMelodyTxt() { return QApplication::translate("Texam", "play melody"); } /** * write melody */ -static QString writeMelodyTxt() { return QApplication::translate("Texam", "write melody"); } + static QString writeMelodyTxt() { return QApplication::translate("Texam", "write melody"); } /** * attempt */ -static QString attemptTxt() { return QApplication::translate("Texam", "attempt"); } + static QString attemptTxt() { return QApplication::translate("Texam", "attempt"); } /** * 7 attempts (or other number given as a parameter) */ -static QString attemptsTxt(int aCount) { return QApplication::translate("Texam", "%n attempt(s)", "like: '1 attempt' or '121 attempts'", aCount); } + static QString attemptsTxt(int aCount) { return QApplication::translate("Texam", "%n attempt(s)", "like: '1 attempt' or '121 attempts'", aCount); } /** * Play a melody written in a score */ -static QString playDescTxt() { return QApplication::translate("Texam", "Play a melody written in a score"); } + static QString playDescTxt() { return QApplication::translate("Texam", "Play a melody written in a score"); } /** * Listen to a melody and write it on a score */ -static QString writeDescTxt() { return QApplication::translate("Texam", "Listen to a melody and write it on a score"); } + static QString writeDescTxt() { return QApplication::translate("Texam", "Listen to a melody and write it on a score"); } /** * question */ -static QString questionTxt() { return QApplication::translate("QuestionsBox", "question"); } + static QString questionTxt() { return QApplication::translate("QuestionsBox", "question"); } /** * questions */ -static QString questionsTxt() { return QApplication::translate("QuestionsBox", "questions"); } + static QString questionsTxt() { return QApplication::translate("QuestionsBox", "questions"); } /** * answer */ -static QString answerTxt() { return QApplication::translate("QuestionsBox", "answer"); } + static QString answerTxt() { return QApplication::translate("QuestionsBox", "answer"); } /** * answers */ -static QString answersTxt() { return QApplication::translate("QuestionsBox", "answers"); } - + static QString answersTxt() { return QApplication::translate("QuestionsBox", "answers"); } }; #endif // TEXTRANS_H - - - - - diff --git a/src/libs/core/exam/tlevel.cpp b/src/libs/core/exam/tlevel.cpp index d1430a0a89d96918bcd2bdbe7aaf6a38644e41bd..b28b3e742e8dcec5d6ead6e75695b008c8263300 100644 --- a/src/libs/core/exam/tlevel.cpp +++ b/src/libs/core/exam/tlevel.cpp @@ -22,13 +22,12 @@ #include <taudioparams.h> #include <tscoreparams.h> +#include <QtCore/qdebug.h> #include <QtCore/qfile.h> -#include <QtWidgets/qmessagebox.h> -#include <QtWidgets/qapplication.h> -#include <QtCore/qxmlstream.h> #include <QtCore/qvariant.h> -#include <QtCore/qdebug.h> - +#include <QtCore/qxmlstream.h> +#include <QtWidgets/qapplication.h> +#include <QtWidgets/qmessagebox.h> /*static-------------------------------------------------------------------------------------------*/ /** @@ -51,625 +50,620 @@ const qint32 Tlevel::levelVersion = 0x95121701; const qint32 Tlevel::currentVersion = 0x95121709; // Version 5 - -int Tlevel::levelVersionNr(qint32 ver) { - if ((ver - levelVersion) % 2) - return -1; // invalid when rest of division is 1 - return ((ver - levelVersion) / 2) + 1 ; +int Tlevel::levelVersionNr(qint32 ver) +{ + if ((ver - levelVersion) % 2) + return -1; // invalid when rest of division is 1 + return ((ver - levelVersion) / 2) + 1; } - -bool Tlevel::isLevelVersion(quint32 ver) { - if (levelVersionNr(ver) <= levelVersionNr(currentVersion)) - return true; - else - return false; +bool Tlevel::isLevelVersion(quint32 ver) +{ + if (levelVersionNr(ver) <= levelVersionNr(currentVersion)) + return true; + else + return false; } - -bool Tlevel::couldBeLevel(qint32 ver) { - int givenVersion = levelVersionNr(ver); - if (givenVersion >= 1 && givenVersion <= 127) - return true; - else - return false; +bool Tlevel::couldBeLevel(qint32 ver) +{ + int givenVersion = levelVersionNr(ver); + if (givenVersion >= 1 && givenVersion <= 127) + return true; + else + return false; } - /** * TlevelSelector context of translate() is used for backward compatibility with translations */ -void Tlevel::fileIOerrorMsg(QFile& f) { - if (!f.fileName().isEmpty()) - QMessageBox::critical(nullptr, QLatin1String(" "), QApplication::translate("TlevelSelector", - "Cannot open file\n %1 \n for reading").arg(f.fileName())); - else - QMessageBox::critical(nullptr, QLatin1String(" "), QApplication::translate("TlevelSelector", "No file name specified")); +void Tlevel::fileIOerrorMsg(QFile &f) +{ + if (!f.fileName().isEmpty()) + QMessageBox::critical(nullptr, QLatin1String(" "), QApplication::translate("TlevelSelector", "Cannot open file\n %1 \n for reading").arg(f.fileName())); + else + QMessageBox::critical(nullptr, QLatin1String(" "), QApplication::translate("TlevelSelector", "No file name specified")); } - -void Tlevel::fretFromXml(QXmlStreamReader& xml, char& fr, Tlevel::EerrorType& err) { - fr = (char)QVariant(xml.readElementText()).toInt(); - if (fr < 0 || fr > 24) { // max frets number - fr = 0; - qDebug() << "[Tlevel] Fret number in" << xml.name() << "was wrong but fixed"; - err = Tlevel::e_levelFixed; - } +void Tlevel::fretFromXml(QXmlStreamReader &xml, char &fr, Tlevel::EerrorType &err) +{ + fr = (char)QVariant(xml.readElementText()).toInt(); + if (fr < 0 || fr > 24) { // max frets number + fr = 0; + qDebug() << "[Tlevel] Fret number in" << xml.name() << "was wrong but fixed"; + err = Tlevel::e_levelFixed; + } } - -void Tlevel::skipCurrentXmlKey(QXmlStreamReader& xml) { - qDebug() << "[Tlevel] Unrecognized key:" << xml.name(); - xml.skipCurrentElement(); +void Tlevel::skipCurrentXmlKey(QXmlStreamReader &xml) +{ + qDebug() << "[Tlevel] Unrecognized key:" << xml.name(); + xml.skipCurrentElement(); } /*end static--------------------------------------------------------------------------------------*/ -Tlevel::Tlevel() : - hasInstrToFix(false) -{ -// level parameters - name = QObject::tr("master of masters"); - desc = QObject::tr("All possible options are turned on"); - bool hasGuitar = GLOB->instrument().isGuitar(); -// QUESTIONS - questionAs = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); - answersAs[0] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); - answersAs[1] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); - answersAs[2] = TQAtype(true, true, GLOB->instrument().isGuitar(), false); - answersAs[3] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); - requireOctave = true; - requireStyle = true; +Tlevel::Tlevel() + : hasInstrToFix(false) +{ + // level parameters + name = QObject::tr("master of masters"); + desc = QObject::tr("All possible options are turned on"); + bool hasGuitar = GLOB->instrument().isGuitar(); + // QUESTIONS + questionAs = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); + answersAs[0] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); + answersAs[1] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); + answersAs[2] = TQAtype(true, true, GLOB->instrument().isGuitar(), false); + answersAs[3] = TQAtype(true, true, GLOB->instrument().type() != Tinstrument::NoInstrument, true); + requireOctave = true; + requireStyle = true; /** * variables isNoteLo, isNoteHi and isFretHi are not used - it has no sense. * Since version 0.8.90 isNoteLo and isNoteHi are merged into Tclef. * It can store multiple clefs (maybe in unknown future it will be used) * 0 - no clef and up to 15 different clefs. */ - clef = Tclef(GLOB->S->clef); - - instrument = GLOB->instrument().type(); - onlyLowPos = false; - onlyCurrKey = false; - intonation = GLOB->A->intonation; -// ACCIDENTALS - withSharps = true; - withFlats = true; - withDblAcc = true; - useKeySign = true; - isSingleKey = false; - loKey = TkeySignature(-7); - hiKey = TkeySignature(7); // key range (7b to 7#) - manualKey = true; - forceAccids = true; - showStrNr = hasGuitar; -// MELODIES - melodyLen = 1; - endsOnTonic = true; - requireInTempo = true; - howGetMelody = e_randFromRange; - randOrderInSet = true; - repeatNrInSet = 1; - // notesList is clean here -// RHYTHMS - basicRhythms = 0; - dotsRhythms = 0; - meters = 0; - rhythmDiversity = 5; - barNumber = 4; - variableBarNr = true; - useRests = false; - useTies = false; -// RANGE - for non guitar Tglobals will returns scale determined by clef - loNote = GLOB->loString(); - hiNote = Tnote(GLOB->hiString().chromatic() + GLOB->GfretsNumber); - loFret = 0; - hiFret = GLOB->GfretsNumber; - for (int i = 0; i < 6; i++) { - if (i <= GLOB->Gtune()->stringNr()) - usedStrings[i] = true; - else - usedStrings[i] = false; - } -} - - -bool getLevelFromStream(QDataStream& in, Tlevel& lev, qint32 ver) { - bool ok = true; - in >> lev.name >> lev.desc; - in >> lev.questionAs; - in >> lev.answersAs[0] >> lev.answersAs[1] >> lev.answersAs[2] >> lev.answersAs[3]; - in >> lev.withSharps >> lev.withFlats >> lev.withDblAcc; - quint8 sharedByte; - in >> lev.useKeySign >> sharedByte; - lev.isSingleKey = (bool)(sharedByte % 2); - lev.intonation = sharedByte / 2; - ok = getKeyFromStream(in, lev.loKey); - ok = getKeyFromStream(in, lev.hiKey); - in >> lev.manualKey >> lev.forceAccids; - in >> lev.requireOctave >> lev.requireStyle; -// RANGE - ok = getNoteFromStream(in, lev.loNote); - ok = getNoteFromStream(in, lev.hiNote); -/** Merged to quint16 since version 0.8.90 */ - quint16 testClef; - in >> testClef; - qint8 lo, hi; - in >> lo >> hi; - if (lo < 0 || lo > 24) { // max frets number - lo = 0; - ok = false; - } - if (hi < 0 || hi > 24) { // max frets number - hi = GLOB->GfretsNumber; - ok = false; - } - lev.loFret = char(lo); - lev.hiFret = char(hi); -/** Previously is was bool type */ - quint8 instr; - in >> instr; - in >> lev.usedStrings[0] >> lev.usedStrings[1] >> lev.usedStrings[2] - >> lev.usedStrings[3] >> lev.usedStrings[4] >> lev.usedStrings[5]; - in >> lev.onlyLowPos >> lev.onlyCurrKey >> lev.showStrNr; - if (ver == lev.levelVersion) { // first version of level file structure - lev.clef = lev.fixClef(testClef); // determining/fixing a clef from first version - lev.instrument = lev.fixInstrument(instr); // determining/fixing an instrument type - } else { - lev.clef = Tclef((Tclef::EclefType)testClef); - lev.instrument = (Tinstrument::Etype)instr; - } - lev.melodyLen = 1; // Those parameters was deployed in XML files - lev.endsOnTonic = false; // By settings those values their will be ignored - lev.requireInTempo = false; - return ok; -} - - -Tlevel::EerrorType Tlevel::qaTypeFromXml(QXmlStreamReader& xml) { - TQAtype qa; - EerrorType er = e_level_OK; - int id = qa.fromXml(xml); - if (id == -1) { - questionAs = qa; - if (!questionAs.isOnScore() && !questionAs.isName() && !questionAs.isOnInstr() && !questionAs.isSound()) { - qDebug() << "There are not any questions in a level. It makes no sense."; + clef = Tclef(GLOB->scoreParams->clef); + + instrument = GLOB->instrument().type(); + onlyLowPos = false; + onlyCurrKey = false; + intonation = GLOB->audioParams->intonation; + // ACCIDENTALS + withSharps = true; + withFlats = true; + withDblAcc = true; + useKeySign = true; + isSingleKey = false; + loKey = TkeySignature(-7); + hiKey = TkeySignature(7); // key range (7b to 7#) + manualKey = true; + forceAccids = true; + showStrNr = hasGuitar; + // MELODIES + melodyLen = 1; + endsOnTonic = true; + requireInTempo = true; + howGetMelody = e_randFromRange; + randOrderInSet = true; + repeatNrInSet = 1; + // notesList is clean here + // RHYTHMS + basicRhythms = 0; + dotsRhythms = 0; + meters = 0; + rhythmDiversity = 5; + barNumber = 4; + variableBarNr = true; + useRests = false; + useTies = false; + // RANGE - for non guitar Tglobals will returns scale determined by clef + loNote = GLOB->loString(); + hiNote = Tnote(GLOB->hiString().chromatic() + GLOB->GfretsNumber); + loFret = 0; + hiFret = GLOB->GfretsNumber; + for (int i = 0; i < 6; i++) { + if (i <= GLOB->Gtune()->stringNr()) + usedStrings[i] = true; + else + usedStrings[i] = false; + } +} + +bool getLevelFromStream(QDataStream &in, Tlevel &lev, qint32 ver) +{ + bool ok = true; + in >> lev.name >> lev.desc; + in >> lev.questionAs; + in >> lev.answersAs[0] >> lev.answersAs[1] >> lev.answersAs[2] >> lev.answersAs[3]; + in >> lev.withSharps >> lev.withFlats >> lev.withDblAcc; + quint8 sharedByte; + in >> lev.useKeySign >> sharedByte; + lev.isSingleKey = (bool)(sharedByte % 2); + lev.intonation = sharedByte / 2; + ok = getKeyFromStream(in, lev.loKey); + ok = getKeyFromStream(in, lev.hiKey); + in >> lev.manualKey >> lev.forceAccids; + in >> lev.requireOctave >> lev.requireStyle; + // RANGE + ok = getNoteFromStream(in, lev.loNote); + ok = getNoteFromStream(in, lev.hiNote); + /** Merged to quint16 since version 0.8.90 */ + quint16 testClef; + in >> testClef; + qint8 lo, hi; + in >> lo >> hi; + if (lo < 0 || lo > 24) { // max frets number + lo = 0; + ok = false; + } + if (hi < 0 || hi > 24) { // max frets number + hi = GLOB->GfretsNumber; + ok = false; + } + lev.loFret = char(lo); + lev.hiFret = char(hi); + /** Previously is was bool type */ + quint8 instr; + in >> instr; + in >> lev.usedStrings[0] >> lev.usedStrings[1] >> lev.usedStrings[2] >> lev.usedStrings[3] >> lev.usedStrings[4] >> lev.usedStrings[5]; + in >> lev.onlyLowPos >> lev.onlyCurrKey >> lev.showStrNr; + if (ver == lev.levelVersion) { // first version of level file structure + lev.clef = lev.fixClef(testClef); // determining/fixing a clef from first version + lev.instrument = lev.fixInstrument(instr); // determining/fixing an instrument type + } else { + lev.clef = Tclef((Tclef::EclefType)testClef); + lev.instrument = (Tinstrument::Etype)instr; + } + lev.melodyLen = 1; // Those parameters was deployed in XML files + lev.endsOnTonic = false; // By settings those values their will be ignored + lev.requireInTempo = false; + return ok; +} + +Tlevel::EerrorType Tlevel::qaTypeFromXml(QXmlStreamReader &xml) +{ + TQAtype qa; + EerrorType er = e_level_OK; + int id = qa.fromXml(xml); + if (id == -1) { + questionAs = qa; + if (!questionAs.isOnScore() && !questionAs.isName() && !questionAs.isOnInstr() && !questionAs.isSound()) { + qDebug() << "There are not any questions in a level. It makes no sense."; + return e_otherError; + } + } else if (id >= 0 && id < 4) { + answersAs[id] = qa; + // verify every answersAs context and set corresponding questionAs to false when all were unset (false) + if (questionAs.isOnScore() && (!answersAs[0].isOnScore() && !answersAs[0].isName() && !answersAs[0].isOnInstr() && !answersAs[0].isSound())) { + er = e_levelFixed; + questionAs.setOnScore(false); + } + if (questionAs.isName() && (!answersAs[1].isOnScore() && !answersAs[1].isName() && !answersAs[1].isOnInstr() && !answersAs[1].isSound())) { + er = e_levelFixed; + questionAs.setAsName(false); + } + if (questionAs.isOnInstr() && (!answersAs[2].isOnScore() && !answersAs[2].isName() && !answersAs[2].isOnInstr() && !answersAs[2].isSound())) { + er = e_levelFixed; + questionAs.setOnInstr(false); + } + if (questionAs.isSound() && (!answersAs[3].isOnScore() && !answersAs[3].isName() && !answersAs[3].isOnInstr() && !answersAs[3].isSound())) { + er = e_levelFixed; + questionAs.setOnScore(false); + } + } + return er; +} + +Tlevel::EerrorType Tlevel::loadFromXml(QXmlStreamReader &xml) +{ + EerrorType er = e_level_OK; + + if (xml.name() != QLatin1String("level")) { + qDebug() << "[Tlevel] There is no 'level' key in that XML"; + return e_noLevelInXml; + } + name = xml.attributes().value(QLatin1String("name")).toString(); + if (name.isEmpty()) { + qDebug() << "[Tlevel] Level key has empty 'name' attribute"; return e_otherError; - } - } else if (id >= 0 && id < 4) { - answersAs[id] = qa; - // verify every answersAs context and set corresponding questionAs to false when all were unset (false) - if (questionAs.isOnScore() && - (!answersAs[0].isOnScore() && !answersAs[0].isName() && !answersAs[0].isOnInstr() && !answersAs[0].isSound())) { - er = e_levelFixed; - questionAs.setOnScore(false); - } - if (questionAs.isName() && - (!answersAs[1].isOnScore() && !answersAs[1].isName() && !answersAs[1].isOnInstr() && !answersAs[1].isSound())) { - er = e_levelFixed; - questionAs.setAsName(false); - } - if (questionAs.isOnInstr() && - (!answersAs[2].isOnScore() && !answersAs[2].isName() && !answersAs[2].isOnInstr() && !answersAs[2].isSound())) { - er = e_levelFixed; - questionAs.setOnInstr(false); - } - if (questionAs.isSound() && - (!answersAs[3].isOnScore() && !answersAs[3].isName() && !answersAs[3].isOnInstr() && !answersAs[3].isSound())) { - er = e_levelFixed; - questionAs.setOnScore(false); - } - } - return er; -} - - -Tlevel::EerrorType Tlevel::loadFromXml(QXmlStreamReader& xml) { - EerrorType er = e_level_OK; - - if (xml.name() != QLatin1String("level")) { - qDebug() << "[Tlevel] There is no 'level' key in that XML"; - return e_noLevelInXml; - } - name = xml.attributes().value(QLatin1String("name")).toString(); - if (name.isEmpty()) { - qDebug() << "[Tlevel] Level key has empty 'name' attribute"; - return e_otherError; - } - melodySet.clear(); - randOrderInSet = true; - repeatNrInSet = 1; - - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("description")) - desc = xml.readElementText(); - else if (xml.name() == QLatin1String("nameTR")) - name = QApplication::translate("Levels", xml.readElementText().toLocal8Bit()); - else if (xml.name() == QLatin1String("descriptionTR")) - desc = QApplication::translate("Levels", xml.readElementText().toLocal8Bit()); - // QUESTIONS - else if (xml.name() == QLatin1String("questions")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("qaType")) { - er = qaTypeFromXml(xml); - if (er == e_otherError) - return er; - } else if (xml.name() == QLatin1String("requireOctave")) - requireOctave = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("requireStyle")) - requireStyle = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("showStrNr")) - showStrNr = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("clef")) { - clef.setClef(Tclef::EclefType(QVariant(xml.readElementText()).toInt())); - if (clef.name().isEmpty()) { // when clef has improper/unsupported value its name returns empty string - qDebug() << "[Tlevel] Level had wrong/undefined clef. It was fixed to treble dropped."; - clef.setClef(Tclef::Treble_G_8down); - er = e_levelFixed; + } + melodySet.clear(); + randOrderInSet = true; + repeatNrInSet = 1; + + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("description")) + desc = xml.readElementText(); + else if (xml.name() == QLatin1String("nameTR")) + name = QApplication::translate("Levels", xml.readElementText().toLocal8Bit()); + else if (xml.name() == QLatin1String("descriptionTR")) + desc = QApplication::translate("Levels", xml.readElementText().toLocal8Bit()); + // QUESTIONS + else if (xml.name() == QLatin1String("questions")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("qaType")) { + er = qaTypeFromXml(xml); + if (er == e_otherError) + return er; + } else if (xml.name() == QLatin1String("requireOctave")) + requireOctave = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("requireStyle")) + requireStyle = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("showStrNr")) + showStrNr = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("clef")) { + clef.setClef(Tclef::EclefType(QVariant(xml.readElementText()).toInt())); + if (clef.name().isEmpty()) { // when clef has improper/unsupported value its name returns empty string + qDebug() << "[Tlevel] Level had wrong/undefined clef. It was fixed to treble dropped."; + clef.setClef(Tclef::Treble_G_8down); + er = e_levelFixed; + } + } else if (xml.name() == QLatin1String("instrument")) { + instrument = Tinstrument::Etype(QVariant(xml.readElementText()).toInt()); + if (Tinstrument::staticName(instrument).isEmpty()) { + qDebug() << "[Tlevel] Level had wrong instrument type. It was fixed to classical guitar."; + instrument = Tinstrument::ClassicalGuitar; + er = e_levelFixed; + } + } else if (xml.name() == QLatin1String("onlyLowPos")) + onlyLowPos = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("onlyCurrKey")) + onlyCurrKey = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("intonation")) + intonation = QVariant(xml.readElementText()).toInt(); + else + skipCurrentXmlKey(xml); + } + } else if (xml.name() == QLatin1String("melodies")) { + // Melodies + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("melodyLength")) + melodyLen = qBound(1, QVariant(xml.readElementText()).toInt(), 100); + else if (xml.name() == ("endsOnTonic")) + endsOnTonic = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("requireInTempo")) + requireInTempo = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("randType")) + howGetMelody = static_cast<EhowGetMelody>(xml.readElementText().toInt()); + else if (xml.name() == QLatin1String("keyOfrandList")) { + xml.readNextStartElement(); + keyOfrandList.fromXml(xml); + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("noteList")) { + notesList.clear(); + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("n")) { + notesList << Tnote(); + notesList.last().fromXml(xml); + if (!notesList.last().isValid()) // skip empty notes + notesList.removeLast(); + } else + skipCurrentXmlKey(xml); + } + } else if (xml.name() == QLatin1String("randOrderInSet")) + randOrderInSet = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("repeatNrInSet")) { + auto ris = QVariant(xml.readElementText()).toInt(); + if (ris < 1 || ris > 15) { + ris = qBound(1, ris, 15); + qDebug() << "[Tlevel] value of melody repeats was wrong and fixed to" << ris; + } + repeatNrInSet = static_cast<quint8>(ris); + } else if (xml.name() == QLatin1String("melody")) { + auto t = xml.attributes().value(QLatin1String("title")).toString(); + auto c = xml.attributes().value(QLatin1String("composer")).toString(); + melodySet << Tmelody(); + melodySet.last().fromXml(xml); // TODO: no validation here, could be vulnerable + melodySet.last().setTitle(t); + melodySet.last().setComposer(c); + } else + skipCurrentXmlKey(xml); } - } else if (xml.name() == QLatin1String("instrument")) { - instrument = Tinstrument::Etype(QVariant(xml.readElementText()).toInt()); - if (Tinstrument::staticName(instrument).isEmpty()) { - qDebug() << "[Tlevel] Level had wrong instrument type. It was fixed to classical guitar."; - instrument = Tinstrument::ClassicalGuitar; - er = e_levelFixed; + } else if (xml.name() == QLatin1String("accidentals")) { + while (xml.readNextStartElement()) { + // qDebug() << "accidentals->" << xml.name(); + if (xml.name() == QLatin1String("withSharps")) + withSharps = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("withFlats")) + withFlats = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("withDblAcc")) + withDblAcc = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("useKeySign")) + useKeySign = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("loKey")) { + xml.readNextStartElement(); + loKey.fromXml(xml); + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("hiKey")) { + xml.readNextStartElement(); + hiKey.fromXml(xml); + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("isSingleKey")) + isSingleKey = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("manualKey")) + manualKey = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("forceAccids")) + forceAccids = QVariant(xml.readElementText()).toBool(); + else + skipCurrentXmlKey(xml); } - } else if (xml.name() == QLatin1String("onlyLowPos")) - onlyLowPos = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("onlyCurrKey")) - onlyCurrKey = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("intonation")) - intonation = QVariant(xml.readElementText()).toInt(); - else - skipCurrentXmlKey(xml); - } - } else if (xml.name() == QLatin1String("melodies")) { - // Melodies - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("melodyLength")) - melodyLen = qBound(1, QVariant(xml.readElementText()).toInt(), 100); - else if (xml.name() == ("endsOnTonic")) - endsOnTonic = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("requireInTempo")) - requireInTempo = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("randType")) - howGetMelody = static_cast<EhowGetMelody>(xml.readElementText().toInt()); - else if (xml.name() == QLatin1String("keyOfrandList")) { - xml.readNextStartElement(); - keyOfrandList.fromXml(xml); - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("noteList")) { - notesList.clear(); - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("n")) { - notesList << Tnote(); - notesList.last().fromXml(xml); - if (!notesList.last().isValid()) // skip empty notes - notesList.removeLast(); + } else if (xml.name() == QLatin1String("rhythms")) { + // RHYTHMS + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("meters")) + meters = static_cast<quint16>(QVariant(xml.readElementText()).toInt()); + else if (xml.name() == QLatin1String("basic")) + basicRhythms = static_cast<quint32>(QVariant(xml.readElementText()).toUInt()); + else if (xml.name() == QLatin1String("dots")) + dotsRhythms = static_cast<quint32>(QVariant(xml.readElementText()).toUInt()); + else if (xml.name() == QLatin1String("diversity")) + rhythmDiversity = static_cast<quint8>(QVariant(xml.readElementText()).toInt()); + else if (xml.name() == QLatin1String("bars")) { + variableBarNr = xml.attributes().value(QLatin1String("variable")) == QLatin1String("true"); + barNumber = static_cast<quint8>(QVariant(xml.readElementText()).toInt()); + } else if (xml.name() == QLatin1String("randomBars")) + variableBarNr = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("rests")) + useRests = QVariant(xml.readElementText()).toBool(); + else if (xml.name() == QLatin1String("ties")) + useTies = QVariant(xml.readElementText()).toBool(); + else + skipCurrentXmlKey(xml); + } + } else if (xml.name() == QLatin1String("range")) { + // RANGE + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("loFret")) + fretFromXml(xml, loFret, er); + else if (xml.name() == QLatin1String("hiFret")) + fretFromXml(xml, hiFret, er); + else if (xml.name() == QLatin1String("loNote")) + loNote.fromXml(xml); + else if (xml.name() == QLatin1String("hiNote")) + hiNote.fromXml(xml); + else if (xml.name() == QLatin1String("useString")) { + int id = xml.attributes().value(QLatin1String("number")).toInt(); + if (id > 0 && id < 7) + usedStrings[id - 1] = QVariant(xml.readElementText()).toBool(); } else skipCurrentXmlKey(xml); - } - } else if (xml.name() == QLatin1String("randOrderInSet")) - randOrderInSet = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("repeatNrInSet")) { - auto ris = QVariant(xml.readElementText()).toInt(); - if (ris < 1 || ris > 15) { - ris = qBound(1, ris, 15); - qDebug() << "[Tlevel] value of melody repeats was wrong and fixed to" << ris; - } - repeatNrInSet = static_cast<quint8>(ris); - } else if (xml.name() == QLatin1String("melody")) { - auto t = xml.attributes().value(QLatin1String("title")).toString(); - auto c = xml.attributes().value(QLatin1String("composer")).toString(); - melodySet << Tmelody(); - melodySet.last().fromXml(xml); // TODO: no validation here, could be vulnerable - melodySet.last().setTitle(t); - melodySet.last().setComposer(c); - } else - skipCurrentXmlKey(xml); - } - } else if (xml.name() == QLatin1String("accidentals")) { - while (xml.readNextStartElement()) { -// qDebug() << "accidentals->" << xml.name(); - if (xml.name() == QLatin1String("withSharps")) - withSharps = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("withFlats")) - withFlats = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("withDblAcc")) - withDblAcc = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("useKeySign")) - useKeySign = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("loKey")) { - xml.readNextStartElement(); - loKey.fromXml(xml); - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("hiKey")) { - xml.readNextStartElement(); - hiKey.fromXml(xml); - xml.skipCurrentElement(); - } - else if (xml.name() == QLatin1String("isSingleKey")) - isSingleKey = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("manualKey")) - manualKey = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("forceAccids")) - forceAccids = QVariant(xml.readElementText()).toBool(); - else + } + } else skipCurrentXmlKey(xml); + } + if (name.size() > 35) { + name = name.left(35); + qDebug() << "[Tlevel] Name of a level was reduced to 35 characters:" << name; + } + if (canBeInstr() && fixFretRange() == e_levelFixed) { + er = e_levelFixed; + qDebug() << "[Tlevel] Lowest fret in the range was bigger than the highest one. Fixed"; + } + if (useKeySign && !isSingleKey && fixKeyRange() == e_levelFixed) { + er = e_levelFixed; + qDebug() << "[Tlevel] Lowest key in the range was higher than the highest one. Fixed"; + } + if (loNote.note() == 0 || hiNote.note() == 0) { + qDebug() << "[Tlevel] Note range is wrong."; + return e_otherError; + } else if (fixNoteRange() == e_levelFixed) { + er = e_levelFixed; + qDebug() << "[Tlevel] Lowest note in the range was higher than the highest one. Fixed"; + } + if (notesList.isEmpty()) { + if (howGetMelody == e_randFromList) { + qDebug() << "[Tlevel] list of notes is empty but e_randFromList is set"; + howGetMelody = e_randFromRange; } - } else if (xml.name() == QLatin1String("rhythms")) { - // RHYTHMS - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("meters")) - meters = static_cast<quint16>(QVariant(xml.readElementText()).toInt()); - else if (xml.name() == QLatin1String("basic")) - basicRhythms = static_cast<quint32>(QVariant(xml.readElementText()).toUInt()); - else if (xml.name() == QLatin1String("dots")) - dotsRhythms = static_cast<quint32>(QVariant(xml.readElementText()).toUInt()); - else if (xml.name() == QLatin1String("diversity")) - rhythmDiversity = static_cast<quint8>(QVariant(xml.readElementText()).toInt()); - else if (xml.name() == QLatin1String("bars")) { - variableBarNr = xml.attributes().value(QLatin1String("variable")) == QLatin1String("true"); - barNumber = static_cast<quint8>(QVariant(xml.readElementText()).toInt()); - } else if (xml.name() == QLatin1String("randomBars")) - variableBarNr = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("rests")) - useRests = QVariant(xml.readElementText()).toBool(); - else if (xml.name() == QLatin1String("ties")) - useTies = QVariant(xml.readElementText()).toBool(); - else - skipCurrentXmlKey(xml); + } else { + if (howGetMelody == e_randFromRange) { + qDebug() << "[Tlevel] has list of notes but e_randFromRange is set. List will be cleaned"; + notesList.clear(); } - } else if (xml.name() == QLatin1String("range")) { - // RANGE - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("loFret")) - fretFromXml(xml, loFret, er); - else if (xml.name() == QLatin1String("hiFret")) - fretFromXml(xml, hiFret, er); - else if (xml.name() == QLatin1String("loNote")) - loNote.fromXml(xml); - else if (xml.name() == QLatin1String("hiNote")) - hiNote.fromXml(xml); - else if (xml.name() == QLatin1String("useString")) { - int id = xml.attributes().value(QLatin1String("number")).toInt(); - if (id > 0 && id < 7) - usedStrings[id - 1] = QVariant(xml.readElementText()).toBool(); - } else - skipCurrentXmlKey(xml); + } + if (howGetMelody == e_melodyFromSet) { + if (melodySet.isEmpty()) { + qDebug() << "[Tlevel] is melody set but list of melodies is empty. Level corrupted!!!"; + er = e_otherError; } - } else - skipCurrentXmlKey(xml); - } - if (name.size() > 35) { - name = name.left(35); - qDebug() << "[Tlevel] Name of a level was reduced to 35 characters:" << name; - } - if (canBeInstr() && fixFretRange() == e_levelFixed) { - er = e_levelFixed; - qDebug() << "[Tlevel] Lowest fret in the range was bigger than the highest one. Fixed"; - } - if (useKeySign && !isSingleKey && fixKeyRange() == e_levelFixed) { - er = e_levelFixed; - qDebug() << "[Tlevel] Lowest key in the range was higher than the highest one. Fixed"; - } - if (loNote.note() == 0 || hiNote.note() == 0) { - qDebug() << "[Tlevel] Note range is wrong."; - return e_otherError; - } else if (fixNoteRange() == e_levelFixed) { - er = e_levelFixed; - qDebug() << "[Tlevel] Lowest note in the range was higher than the highest one. Fixed"; - } - if (notesList.isEmpty()) { - if (howGetMelody == e_randFromList) { - qDebug() << "[Tlevel] list of notes is empty but e_randFromList is set"; - howGetMelody = e_randFromRange; - } - } else { - if (howGetMelody == e_randFromRange) { - qDebug() << "[Tlevel] has list of notes but e_randFromRange is set. List will be cleaned"; - notesList.clear(); - } - } - if (howGetMelody == e_melodyFromSet) { - if (melodySet.isEmpty()) { - qDebug() << "[Tlevel] is melody set but list of melodies is empty. Level corrupted!!!"; - er = e_otherError; - } - } else { - if (!randOrderInSet) { - qDebug() << "[Tlevel] is not a melody set but question order is set. Back to random."; - randOrderInSet = true; - er = e_levelFixed; - } - if (repeatNrInSet > 1) { - qDebug() << "[Tlevel] is not a melody set but number of repeats was set. Fixed!"; - repeatNrInSet = 1; - er = e_levelFixed; - } - } - if (xml.hasError()) { - qDebug() << "[Tlevel] level has error" << xml.errorString() << xml.lineNumber(); - return e_otherError; - } - return er; -} - - -void Tlevel::writeToXml(QXmlStreamWriter& xml) { - xml.writeStartElement(QLatin1String("level")); - bool nameTr = false, descTr = false; - if (name.startsWith(QLatin1String("tr("))) { - nameTr = true; - name = name.mid(3); - } - if (desc.startsWith(QLatin1String("tr("))) { - descTr = true; - desc = desc.mid(3); - } + } else { + if (!randOrderInSet) { + qDebug() << "[Tlevel] is not a melody set but question order is set. Back to random."; + randOrderInSet = true; + er = e_levelFixed; + } + if (repeatNrInSet > 1) { + qDebug() << "[Tlevel] is not a melody set but number of repeats was set. Fixed!"; + repeatNrInSet = 1; + er = e_levelFixed; + } + } + if (xml.hasError()) { + qDebug() << "[Tlevel] level has error" << xml.errorString() << xml.lineNumber(); + return e_otherError; + } + return er; +} + +void Tlevel::writeToXml(QXmlStreamWriter &xml) +{ + xml.writeStartElement(QLatin1String("level")); + bool nameTr = false, descTr = false; + if (name.startsWith(QLatin1String("tr("))) { + nameTr = true; + name = name.mid(3); + } + if (desc.startsWith(QLatin1String("tr("))) { + descTr = true; + desc = desc.mid(3); + } xml.writeAttribute(QLatin1String("name"), name); if (nameTr) - xml.writeTextElement(QLatin1String("nameTR"), name); + xml.writeTextElement(QLatin1String("nameTR"), name); if (descTr) - xml.writeTextElement(QLatin1String("descriptionTR"), desc); + xml.writeTextElement(QLatin1String("descriptionTR"), desc); else - xml.writeTextElement(QLatin1String("description"), desc); - // QUESTIONS + xml.writeTextElement(QLatin1String("description"), desc); + // QUESTIONS xml.writeStartElement(QLatin1String("questions")); - questionAs.toXml(-1, xml); - for (int i = 0; i < 4; i++) + questionAs.toXml(-1, xml); + for (int i = 0; i < 4; i++) answersAs[i].toXml(i, xml); - xml.writeTextElement(QLatin1String("requireOctave"), QVariant(requireOctave).toString()); - xml.writeTextElement(QLatin1String("requireStyle"), QVariant(requireStyle).toString()); - xml.writeTextElement(QLatin1String("showStrNr"), QVariant(showStrNr).toString()); - xml.writeTextElement(QLatin1String("clef"), QVariant(static_cast<int>(clef.type())).toString()); - xml.writeTextElement(QLatin1String("instrument"), QVariant(static_cast<int>(instrument)).toString()); - xml.writeTextElement(QLatin1String("onlyLowPos"), QVariant(onlyLowPos).toString()); - xml.writeTextElement(QLatin1String("onlyCurrKey"), QVariant(onlyCurrKey).toString()); - xml.writeTextElement(QLatin1String("intonation"), QVariant(intonation).toString()); + xml.writeTextElement(QLatin1String("requireOctave"), QVariant(requireOctave).toString()); + xml.writeTextElement(QLatin1String("requireStyle"), QVariant(requireStyle).toString()); + xml.writeTextElement(QLatin1String("showStrNr"), QVariant(showStrNr).toString()); + xml.writeTextElement(QLatin1String("clef"), QVariant(static_cast<int>(clef.type())).toString()); + xml.writeTextElement(QLatin1String("instrument"), QVariant(static_cast<int>(instrument)).toString()); + xml.writeTextElement(QLatin1String("onlyLowPos"), QVariant(onlyLowPos).toString()); + xml.writeTextElement(QLatin1String("onlyCurrKey"), QVariant(onlyCurrKey).toString()); + xml.writeTextElement(QLatin1String("intonation"), QVariant(intonation).toString()); xml.writeEndElement(); - // ACCIDENTALS + // ACCIDENTALS xml.writeStartElement(QLatin1String("accidentals")); - xml.writeTextElement(QLatin1String("withSharps"), QVariant(withSharps).toString()); - xml.writeTextElement(QLatin1String("withFlats"), QVariant(withFlats).toString()); - xml.writeTextElement(QLatin1String("withDblAcc"), QVariant(withDblAcc).toString()); - xml.writeTextElement(QLatin1String("useKeySign"), QVariant(useKeySign).toString()); - xml.writeStartElement(QLatin1String("loKey")); - loKey.toXml(xml); - xml.writeEndElement(); // loKey - xml.writeStartElement(QLatin1String("hiKey")); - hiKey.toXml(xml); - xml.writeEndElement(); // hiKey - xml.writeTextElement(QLatin1String("isSingleKey"), QVariant(isSingleKey).toString()); - xml.writeTextElement(QLatin1String("manualKey"), QVariant(manualKey).toString()); - xml.writeTextElement(QLatin1String("forceAccids"), QVariant(forceAccids).toString()); + xml.writeTextElement(QLatin1String("withSharps"), QVariant(withSharps).toString()); + xml.writeTextElement(QLatin1String("withFlats"), QVariant(withFlats).toString()); + xml.writeTextElement(QLatin1String("withDblAcc"), QVariant(withDblAcc).toString()); + xml.writeTextElement(QLatin1String("useKeySign"), QVariant(useKeySign).toString()); + xml.writeStartElement(QLatin1String("loKey")); + loKey.toXml(xml); + xml.writeEndElement(); // loKey + xml.writeStartElement(QLatin1String("hiKey")); + hiKey.toXml(xml); + xml.writeEndElement(); // hiKey + xml.writeTextElement(QLatin1String("isSingleKey"), QVariant(isSingleKey).toString()); + xml.writeTextElement(QLatin1String("manualKey"), QVariant(manualKey).toString()); + xml.writeTextElement(QLatin1String("forceAccids"), QVariant(forceAccids).toString()); xml.writeEndElement(); // accidentals - // MELODIES + // MELODIES xml.writeStartElement(QLatin1String("melodies")); - xml.writeTextElement(QLatin1String("melodyLength"), QVariant(melodyLen).toString()); - xml.writeTextElement(QLatin1String("endsOnTonic"), QVariant(endsOnTonic).toString()); - xml.writeTextElement(QLatin1String("requireInTempo"), QVariant(requireInTempo).toString()); - if (howGetMelody != e_randFromRange) { // write it only when needed + xml.writeTextElement(QLatin1String("melodyLength"), QVariant(melodyLen).toString()); + xml.writeTextElement(QLatin1String("endsOnTonic"), QVariant(endsOnTonic).toString()); + xml.writeTextElement(QLatin1String("requireInTempo"), QVariant(requireInTempo).toString()); + if (howGetMelody != e_randFromRange) { // write it only when needed xml.writeTextElement(QLatin1String("randType"), QVariant(static_cast<quint8>(howGetMelody)).toString()); if (howGetMelody == e_randFromList) { xml.writeStartElement(QLatin1String("keyOfrandList")); - keyOfrandList.toXml(xml); + keyOfrandList.toXml(xml); xml.writeEndElement(); // keyOfrandList xml.writeStartElement(QLatin1String("noteList")); for (int n = 0; n < notesList.count(); ++n) - notesList[n].toXml(xml, QLatin1String("n")); // XML note wrapped into <n> tag + notesList[n].toXml(xml, QLatin1String("n")); // XML note wrapped into <n> tag xml.writeEndElement(); // noteList } else if (howGetMelody == e_melodyFromSet) { xml.writeTextElement(QLatin1String("randOrderInSet"), QVariant(randOrderInSet).toString()); xml.writeTextElement(QLatin1String("repeatNrInSet"), QVariant(repeatNrInSet).toString()); for (int m = 0; m < melodySet.count(); ++m) { - xml.writeStartElement(QLatin1String("melody")); - Tmelody& mel = melodySet[m]; - if (!mel.title().isEmpty()) - xml.writeAttribute(QLatin1String("title"), mel.title()); - if (!mel.composer().isEmpty()) - xml.writeAttribute(QLatin1String("composer"), mel.composer()); - melodySet[m].toXml(xml); - xml.writeEndElement(); // melody + xml.writeStartElement(QLatin1String("melody")); + Tmelody &mel = melodySet[m]; + if (!mel.title().isEmpty()) + xml.writeAttribute(QLatin1String("title"), mel.title()); + if (!mel.composer().isEmpty()) + xml.writeAttribute(QLatin1String("composer"), mel.composer()); + melodySet[m].toXml(xml); + xml.writeEndElement(); // melody } } - } + } xml.writeEndElement(); // melodies - // RHYTHMS + // RHYTHMS if (useRhythms()) { // store only when enabled - xml.writeStartElement(QLatin1String("rhythms")); + xml.writeStartElement(QLatin1String("rhythms")); xml.writeTextElement(QLatin1String("meters"), QVariant(meters).toString()); xml.writeTextElement(QLatin1String("basic"), QVariant(basicRhythms).toString()); xml.writeTextElement(QLatin1String("dots"), QVariant(dotsRhythms).toString()); xml.writeTextElement(QLatin1String("diversity"), QVariant(rhythmDiversity).toString()); xml.writeStartElement(QLatin1String("bars")); - xml.writeAttribute(QLatin1String("variable"), QVariant(variableBarNr).toString()); - xml.writeCharacters(QVariant(barNumber).toString()); + xml.writeAttribute(QLatin1String("variable"), QVariant(variableBarNr).toString()); + xml.writeCharacters(QVariant(barNumber).toString()); xml.writeEndElement(); // bars xml.writeTextElement(QLatin1String("rests"), QVariant(useRests).toString()); xml.writeTextElement(QLatin1String("ties"), QVariant(useTies).toString()); - xml.writeEndElement(); // rhythms + xml.writeEndElement(); // rhythms } - // RANGE + // RANGE xml.writeStartElement(QLatin1String("range")); - xml.writeTextElement(QLatin1String("loFret"), QVariant(static_cast<qint8>(loFret)).toString()); - xml.writeTextElement(QLatin1String("hiFret"), QVariant(static_cast<qint8>(hiFret)).toString()); - loNote.toXml(xml, QLatin1String("loNote")); - hiNote.toXml(xml, QLatin1String("hiNote")); - for (int i = 0; i < 6; i++) { + xml.writeTextElement(QLatin1String("loFret"), QVariant(static_cast<qint8>(loFret)).toString()); + xml.writeTextElement(QLatin1String("hiFret"), QVariant(static_cast<qint8>(hiFret)).toString()); + loNote.toXml(xml, QLatin1String("loNote")); + hiNote.toXml(xml, QLatin1String("hiNote")); + for (int i = 0; i < 6; i++) { xml.writeStartElement(QLatin1String("useString")); xml.writeAttribute(QLatin1String("number"), QVariant(i + 1).toString()); xml.writeCharacters(QVariant(usedStrings[i]).toString()); xml.writeEndElement(); // useString - } + } xml.writeEndElement(); // range - xml.writeEndElement(); // level -} - - -bool Tlevel::saveToFile(Tlevel& level, const QString& levelFile) { - QFile file(levelFile); - if (file.open(QIODevice::WriteOnly)) { - QDataStream out(&file); - out.setVersion(QDataStream::Qt_5_9); - out << currentVersion; - QByteArray arrayXML; - QXmlStreamWriter xml(&arrayXML); - -// xml.setAutoFormatting(true); -// xml.setAutoFormattingIndent(2); - xml.writeStartDocument(); - xml.writeComment(QStringLiteral("\nXML file of Nootka exam level.\n" - "https://nootka.sourceforge.io\n" - "It is strongly recommended to do not edit this file manually.\n" - "Use Nootka level creator instead!\n")); - level.writeToXml(xml); - xml.writeEndDocument(); - - out << qCompress(arrayXML); - - file.close(); - return true; - } else - return false; -} - -//################################################################################################# -//################### FIXES FOR LEVEL CONTENT ############################################ -//################################################################################################# - -void Tlevel::convFromDropedBass() { - if (clef.type() == Tclef::Bass_F_8down) - clef.setClef(Tclef::Bass_F); - - loNote.riseOctaveUp(); - hiNote.riseOctaveUp(); - if (!notesList.isEmpty()) { - for (int n = 0; n < notesList.count(); ++n) - notesList[n].riseOctaveUp(); - } - if (!melodySet.isEmpty()) { - for (int m = 0; m < melodySet.count(); ++m) { - auto mel = &melodySet[m]; - if (mel->clef() == Tclef::Bass_F_8down) - mel->setClef(Tclef::Bass_F); - for (int n = 0; n < mel->length(); ++n) - mel->note(n)->p().riseOctaveUp(); - } - } + xml.writeEndElement(); // level +} + +bool Tlevel::saveToFile(Tlevel &level, const QString &levelFile) +{ + QFile file(levelFile); + if (file.open(QIODevice::WriteOnly)) { + QDataStream out(&file); + out.setVersion(QDataStream::Qt_5_9); + out << currentVersion; + QByteArray arrayXML; + QXmlStreamWriter xml(&arrayXML); + + // xml.setAutoFormatting(true); + // xml.setAutoFormattingIndent(2); + xml.writeStartDocument(); + xml.writeComment( + QStringLiteral("\nXML file of Nootka exam level.\n" + "https://nootka.sourceforge.io\n" + "It is strongly recommended to do not edit this file manually.\n" + "Use Nootka level creator instead!\n")); + level.writeToXml(xml); + xml.writeEndDocument(); + + out << qCompress(arrayXML); + + file.close(); + return true; + } else + return false; } +// ################################################################################################# +// ################### FIXES FOR LEVEL CONTENT ############################################ +// ################################################################################################# + +void Tlevel::convFromDropedBass() +{ + if (clef.type() == Tclef::Bass_F_8down) + clef.setClef(Tclef::Bass_F); + + loNote.riseOctaveUp(); + hiNote.riseOctaveUp(); + if (!notesList.isEmpty()) { + for (int n = 0; n < notesList.count(); ++n) + notesList[n].riseOctaveUp(); + } + if (!melodySet.isEmpty()) { + for (int m = 0; m < melodySet.count(); ++m) { + auto mel = &melodySet[m]; + if (mel->clef() == Tclef::Bass_F_8down) + mel->setClef(Tclef::Bass_F); + for (int n = 0; n < mel->length(); ++n) + mel->note(n)->p().riseOctaveUp(); + } + } +} -Tclef Tlevel::fixClef(quint16 cl) { +Tclef Tlevel::fixClef(quint16 cl) +{ if (cl == 0) // For backward compatibility - 'no clef' never occurs return Tclef(Tclef::Treble_G_8down); // and versions before 0.8.90 kept here 0 if (cl == 1) { - Tnote lowest(6, -2, 0); - if (canBeInstr() || loNote.chromatic() < lowest.chromatic() ) - return Tclef(Tclef::Treble_G_8down); // surely: 1 = e_treble_G was not intended here - else - return Tclef(Tclef::Treble_G); + Tnote lowest(6, -2, 0); + if (canBeInstr() || loNote.chromatic() < lowest.chromatic()) + return Tclef(Tclef::Treble_G_8down); // surely: 1 = e_treble_G was not intended here + else + return Tclef(Tclef::Treble_G); } if (cl != 2 && cl != 4 && cl != 8 && cl != 16 && cl != 32 && cl != 64 && cl != 128) { qDebug() << "[Tlevel] Fixed clef type. Previous value was:" << cl; @@ -679,10 +673,10 @@ Tclef Tlevel::fixClef(quint16 cl) { return Tclef((Tclef::EclefType)cl); } - -Tinstrument::Etype Tlevel::fixInstrument(quint8 instr) { - // Value 255 comes from transition version 0.8.90 - 0.8.95 and means no instrument, - // however it is invalid because it ignores guitarists and doesn't play exams/exercises on proper instrument +Tinstrument::Etype Tlevel::fixInstrument(quint8 instr) +{ + // Value 255 comes from transition version 0.8.90 - 0.8.95 and means no instrument, + // however it is invalid because it ignores guitarists and doesn't play exams/exercises on proper instrument if (instr == 255) { if (canBeInstr() || canBeSound()) { hasInstrToFix = true; @@ -696,230 +690,213 @@ Tinstrument::Etype Tlevel::fixInstrument(quint8 instr) { else return Tinstrument::NoInstrument; } else if (instr < 4) { // simple cast to detect an instrument - return (Tinstrument::Etype)instr; + return (Tinstrument::Etype)instr; } else { qDebug() << "[Tlevel] Tlevel::instrument had some stupid value. FIXED"; return GLOB->instrument().type(); } } - -Tinstrument::Etype Tlevel::detectInstrument(Tinstrument::Etype currInstr) { - if (canBeInstr()) { // it has to be some kind of guitar - if (currInstr != Tinstrument::NoInstrument) - return currInstr; - else // if current instrument isn't guitar force classical - return Tinstrument::ClassicalGuitar; - } else if (canBeSound()) // prefer current instrument for it +Tinstrument::Etype Tlevel::detectInstrument(Tinstrument::Etype currInstr) +{ + if (canBeInstr()) { // it has to be some kind of guitar + if (currInstr != Tinstrument::NoInstrument) + return currInstr; + else // if current instrument isn't guitar force classical + return Tinstrument::ClassicalGuitar; + } else if (canBeSound()) // prefer current instrument for it return currInstr; - else // no guitar & no sound - instrument type really has no matter + else // no guitar & no sound - instrument type really has no matter return Tinstrument::NoInstrument; } - -//###################### HELPERS ################################################################ +// ###################### HELPERS ################################################################ /** * Checking is any question enabled first and then checking appropriate answer type. * Despite of level creator disables all questions with empty answers (set to false) - * better check this again to avoid further problems. + * better check this again to avoid further problems. */ -bool Tlevel::canBeScore() const { - if (questionAs.isOnScore() || - (questionAs.isName() && answersAs[TQAtype::e_asName].isOnScore()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnScore()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore()) ) - return true; - else - return false; -} - -bool Tlevel::canBeName() const { - if (questionAs.isName() || - (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isName()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isName()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isName()) ) - return true; - else - return false; -} - -bool Tlevel::canBeInstr() const { - if (questionAs.isOnInstr() || - (questionAs.isName() && answersAs[TQAtype::e_asName].isOnInstr()) || - (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnInstr()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnInstr()) ) - return true; - else - return false; -} - -bool Tlevel::canBeSound() const { - if (questionAs.isSound() || - (questionAs.isName() && answersAs[TQAtype::e_asName].isSound()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isSound()) || - (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound()) ) - return true; - else - return false; +bool Tlevel::canBeScore() const +{ + if (questionAs.isOnScore() || (questionAs.isName() && answersAs[TQAtype::e_asName].isOnScore()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnScore()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore())) + return true; + else + return false; +} + +bool Tlevel::canBeName() const +{ + if (questionAs.isName() || (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isName()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isName()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isName())) + return true; + else + return false; +} + +bool Tlevel::canBeInstr() const +{ + if (questionAs.isOnInstr() || (questionAs.isName() && answersAs[TQAtype::e_asName].isOnInstr()) + || (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnInstr()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnInstr())) + return true; + else + return false; +} + +bool Tlevel::canBeSound() const +{ + if (questionAs.isSound() || (questionAs.isName() && answersAs[TQAtype::e_asName].isSound()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isSound()) || (questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound())) + return true; + else + return false; } /** * To be sure, a melody is possible we checking not only notes number * but question-answer types as well, even if creator doesn't allow for wrong sets. */ -bool Tlevel::canBeMelody() const { - if (melodyLen > 1 && - ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isSound())) ) - return true; - else - return false; +bool Tlevel::canBeMelody() const +{ + if (melodyLen > 1 + && ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore()) + || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isSound()))) + return true; + else + return false; } - /** * Checking questions would be skipped because Level creator avoids selecting answer without question. * Unfortunately built-in levels are not so perfect. */ -bool Tlevel::answerIsNote() const { - if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnScore()) || - (questionAs.isName() && answersAs[TQAtype::e_asName].isOnScore()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnScore()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore()) ) +bool Tlevel::answerIsNote() const +{ + if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnScore()) || (questionAs.isName() && answersAs[TQAtype::e_asName].isOnScore()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnScore()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnScore())) return true; - else + else return false; } -bool Tlevel::answerIsName() const { - if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isName()) || - (questionAs.isName() && answersAs[TQAtype::e_asName].isName()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isName()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isName()) ) +bool Tlevel::answerIsName() const +{ + if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isName()) || (questionAs.isName() && answersAs[TQAtype::e_asName].isName()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isName()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isName())) return true; - else + else return false; } -bool Tlevel::answerIsGuitar() const { - if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnInstr()) || - (questionAs.isName() && answersAs[TQAtype::e_asName].isOnInstr()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnInstr()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnInstr()) ) +bool Tlevel::answerIsGuitar() const +{ + if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isOnInstr()) || (questionAs.isName() && answersAs[TQAtype::e_asName].isOnInstr()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isOnInstr()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isOnInstr())) return true; - else + else return false; } -bool Tlevel::answerIsSound() const { - if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound()) || - (questionAs.isName() && answersAs[TQAtype::e_asName].isSound()) || - (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isSound()) || - (questionAs.isSound() && answersAs[TQAtype::e_asSound].isSound()) ) +bool Tlevel::answerIsSound() const +{ + if ((questionAs.isOnScore() && answersAs[TQAtype::e_onScore].isSound()) || (questionAs.isName() && answersAs[TQAtype::e_asName].isSound()) + || (questionAs.isOnInstr() && answersAs[TQAtype::e_onInstr].isSound()) || (questionAs.isSound() && answersAs[TQAtype::e_asSound].isSound())) return true; - else + else return false; } - -bool Tlevel::inScaleOf(int loNoteNr, int hiNoteNr) const { - int loNr = loNote.chromatic(); - int hiNr = hiNote.chromatic(); - if (loNr >= loNoteNr && loNr <= hiNoteNr && hiNr >= loNoteNr && hiNr <= hiNoteNr) - return true; - else - return false; +bool Tlevel::inScaleOf(int loNoteNr, int hiNoteNr) const +{ + int loNr = loNote.chromatic(); + int hiNr = hiNote.chromatic(); + if (loNr >= loNoteNr && loNr <= hiNoteNr && hiNr >= loNoteNr && hiNr <= hiNoteNr) + return true; + else + return false; } - -bool Tlevel::inScaleOf() { - return inScaleOf(GLOB->loString().chromatic(), GLOB->hiNote().chromatic()); +bool Tlevel::inScaleOf() +{ + return inScaleOf(GLOB->loString().chromatic(), GLOB->hiNote().chromatic()); } - -bool Tlevel::adjustFretsToScale(char& loF, char& hiF) { - if (!inScaleOf()) // when note range is not in an instrument scale - return false; // get rid - makes no sense to further check - - int lowest = GLOB->GfretsNumber, highest = 0; - for (int no = loNote.chromatic(); no <= hiNote.chromatic(); no++) { - if (!withFlats && !withSharps) - if (Tnote(no).alter()) // skip note with accidental when not available in the level - continue; - int tmpLow = GLOB->GfretsNumber; - for(int st = 0 ; st < GLOB->Gtune()->stringNr(); st++) { - if (!usedStrings[st]) - continue; - int diff = no - GLOB->Gtune()->str(GLOB->strOrder(st) + 1).chromatic(); - if (diff >= 0 && diff <= static_cast<int>(GLOB->GfretsNumber)) { // found - lowest = qMin<int>(lowest, diff); - tmpLow = qMin<int>(tmpLow, diff); - } +bool Tlevel::adjustFretsToScale(char &loF, char &hiF) +{ + if (!inScaleOf()) // when note range is not in an instrument scale + return false; // get rid - makes no sense to further check + + int lowest = GLOB->GfretsNumber, highest = 0; + for (int no = loNote.chromatic(); no <= hiNote.chromatic(); no++) { + if (!withFlats && !withSharps) + if (Tnote(no).alter()) // skip note with accidental when not available in the level + continue; + int tmpLow = GLOB->GfretsNumber; + for (int st = 0; st < GLOB->Gtune()->stringNr(); st++) { + if (!usedStrings[st]) + continue; + int diff = no - GLOB->Gtune()->str(GLOB->strOrder(st) + 1).chromatic(); + if (diff >= 0 && diff <= static_cast<int>(GLOB->GfretsNumber)) { // found + lowest = qMin<int>(lowest, diff); + tmpLow = qMin<int>(tmpLow, diff); + } + } + highest = qMax<int>(highest, tmpLow); } - highest = qMax<int>(highest, tmpLow); - } - loF = static_cast<char>(lowest); - hiF = static_cast<char>(highest); - return true; + loF = static_cast<char>(lowest); + hiF = static_cast<char>(highest); + return true; } - /** * FIXME * It is possible that first melody in the list will be without meter whether another one will have one */ -bool Tlevel::useRhythms() const { - return canBeMelody() && ((meters && (dotsRhythms || basicRhythms)) || (isMelodySet() && melodySet.first().meter()->meter() != Tmeter::NoMeter)); +bool Tlevel::useRhythms() const +{ + return canBeMelody() && ((meters && (dotsRhythms || basicRhythms)) || (isMelodySet() && melodySet.first().meter()->meter() != Tmeter::NoMeter)); } - -int Tlevel::keysInRange() const { - if (!useKeySign || isSingleKey) - return 1; - if (hiKey.value() - loKey.value() < 0) { - qDebug() << "[Tlevel] FIXME! Key range is invalid!"; - return 1; - } - return hiKey.value() - loKey.value() + 1; +int Tlevel::keysInRange() const +{ + if (!useKeySign || isSingleKey) + return 1; + if (hiKey.value() - loKey.value() < 0) { + qDebug() << "[Tlevel] FIXME! Key range is invalid!"; + return 1; + } + return hiKey.value() - loKey.value() + 1; } - -Tlevel::EerrorType Tlevel::fixFretRange() { - if (loFret > hiFret) { - char tmpFret = loFret; - loFret = hiFret; - hiFret = tmpFret; - return e_levelFixed; - } - return e_level_OK; +Tlevel::EerrorType Tlevel::fixFretRange() +{ + if (loFret > hiFret) { + char tmpFret = loFret; + loFret = hiFret; + hiFret = tmpFret; + return e_levelFixed; + } + return e_level_OK; } - -Tlevel::EerrorType Tlevel::fixNoteRange() { - if (loNote.chromatic() > hiNote.chromatic()) { - Tnote tmpNote = loNote; - loNote = hiNote; - hiNote = tmpNote; - return e_levelFixed; - } - return e_level_OK; +Tlevel::EerrorType Tlevel::fixNoteRange() +{ + if (loNote.chromatic() > hiNote.chromatic()) { + Tnote tmpNote = loNote; + loNote = hiNote; + hiNote = tmpNote; + return e_levelFixed; + } + return e_level_OK; } - -Tlevel::EerrorType Tlevel::fixKeyRange() { - if (loKey.value() > hiKey.value()) { - char tmpKey = loKey.value(); - loKey = hiKey; - hiKey = TkeySignature(tmpKey); - return e_levelFixed; - } - return e_level_OK; +Tlevel::EerrorType Tlevel::fixKeyRange() +{ + if (loKey.value() > hiKey.value()) { + char tmpKey = loKey.value(); + loKey = hiKey; + hiKey = TkeySignature(tmpKey); + return e_levelFixed; + } + return e_level_OK; } - - - - - - - diff --git a/src/libs/core/exam/tlevel.h b/src/libs/core/exam/tlevel.h index bfa089c2c6f713f11420490e84706e806228aac8..ab6971a5f88830ebf4ca0a5ee4195a49fe986a01 100644 --- a/src/libs/core/exam/tlevel.h +++ b/src/libs/core/exam/tlevel.h @@ -19,286 +19,281 @@ #ifndef TEXAMLEVEL_H #define TEXAMLEVEL_H -#include <nootkacoreglobal.h> #include "tqatype.h" -#include <music/tnote.h> -#include <music/tmelody.h> -#include <music/tinstrument.h> #include <QtCore/qdatastream.h> - +#include <music/tinstrument.h> +#include <music/tmelody.h> +#include <music/tnote.h> +#include <nootkacoreglobal.h> class QXmlStreamWriter; class QFile; - /** * This class describes exam level. */ class NOOTKACORE_EXPORT Tlevel { public: - /** - * default constructor creates a "complex" level (master of masters) - */ - Tlevel(); - - /** - * Possible errors during reading level file or XML key. - */ - enum EerrorType { - e_level_OK = 0, - e_levelFixed, /**< level parameters were fixed */ - e_noLevelInXml, /**< when XML stream has no <level> tag */ - e_otherError - }; - - /** - * Describes how to generate melody - */ - enum EhowGetMelody : quint8 { - e_randFromRange = 1, /**< melody is composed from random notes in level range */ - e_randFromList = 2, /**< melody is composed from list of selected notes (@p notesList) */ - e_melodyFromSet = 4 /**< melody from set of melodies (either randomized or asked in the list order). (@p melodySet) */ - }; - -//------------------------- Managing level versions ------------------------------------------------------ - static const qint32 levelVersion; /**< First version, also with early using of intonation and instruments */ - static const qint32 currentVersion; /**< Current level version identifier */ - - /** - * Returns true when given value match to level versions. - */ - static bool isLevelVersion(quint32 ver); - - /** - * Generates level version identifier number from given simple version number (1, 2 etc.) - * It doesn't verify does given number make sense! - */ - static qint32 getVersionId(quint8 verNr) { return levelVersion + (verNr - 1) * 2; } - - /** - * Returns number of level version like 1, 2 or so - */ - static int levelVersionNr(qint32 ver); - - /** - * Returns true when given value 'could be' or 'it is' some version of exam level. - * This way level created with newer Nootka version can be detected. - */ - static bool couldBeLevel(qint32 ver); - - /** - * Saves level to given file and returns true for success or opposite. - */ - static bool saveToFile(Tlevel &level, const QString& levelFile); - - /** - * Shows message box with error if file cannot be opened. - */ - static void fileIOerrorMsg(QFile &f); - - /** - * Reads fret number from current XML key to @fr reference and verifies it. - * Setts error type when error occurs or lives @p err unchanged when OK. - */ - static void fretFromXml(QXmlStreamReader& xml, char& fr, Tlevel::EerrorType& err); - - static void skipCurrentXmlKey(QXmlStreamReader& xml); - -//--------------------------- level parameters ------------------------------------------------------------ - QString name; /**< Level name */ - QString desc; /**< description */ -// QUESTIONS - TQAtype questionAs; - TQAtype answersAs[4]; - bool forceAccids; - bool requireOctave; - bool requireStyle; - bool showStrNr; /**< Shows a string number in questions */ - quint8 intonation; /**< This shares byte with isSingleKey in file */ - Tclef clef; - Tinstrument::Etype instrument; - bool onlyLowPos; - bool onlyCurrKey; -// ACCIDENTALS - bool withSharps, withFlats, withDblAcc; - bool useKeySign; - bool isSingleKey; - TkeySignature loKey, hiKey; /**< range of key signature */ - bool manualKey; /**< manually selecting key in answers */ -// MELODIES - quint16 melodyLen; /**< Notes count in a melody */ - bool endsOnTonic; - bool requireInTempo; - EhowGetMelody howGetMelody; /**< How melody is composed (from range, from notes list or from set of melodies) */ - QList<Tnote> notesList; /**< List with notes from which melody is composed */ - TkeySignature keyOfrandList; /**< Key signature of note list for composing random melodies */ - QList<Tmelody> melodySet; /**< List of defined melodies when @p howGetMelody is @p e_melodyFromSet */ - bool randOrderInSet; /**< order of melodies in the set, @p TRUE means random */ - quint8 repeatNrInSet; /**< How many times a question is repeated with a melody */ -// RHYTHMS - quint32 basicRhythms; - quint32 dotsRhythms; - quint16 meters; /**< Bits of time signatures used in the level */ - quint8 rhythmDiversity; - quint8 barNumber; - bool variableBarNr; /**< Whether bar number grows or not as exam/exercise is progressing */ - bool useRests; - bool useTies; -// RANGE - Tnote loNote; /**< Lowest level note */ - Tnote hiNote; /**< Highest level note */ - char loFret, hiFret; - bool usedStrings[6]; - - - /** - * Indicates when instrument read from file needs user action to be properly obtained. - * It occurs when read value is 255 for level version 1 - */ - bool hasInstrToFix; - -// some helpers - /** - * @p TRUE if answer or question is note on a score - */ - bool canBeScore() const ; - - /** - * @p TRUE if answer or question is note name - */ - bool canBeName() const; - - /** - * @p TRUE if answer or question can be on an instrument - */ - bool canBeInstr() const; - - /** - * @p TRUE if answer or question is played or sang sound - */ - bool canBeSound() const ; - - /** - * @p TRUE when question/answer has more notes and have appropriate types - */ - bool canBeMelody() const; - - /** - * @p TRUE if answer is note on a score in any question type - */ - bool answerIsNote() const; - - /** - * @p TRUE if answer is note name in any question type - */ - bool answerIsName() const; - - /** - * @p TRUE if answer is position on the guitar in any question type - */ - bool answerIsGuitar() const; - - /** - * @p TRUE if answer is played sound in any question type - */ - bool answerIsSound() const; - - /** - * @p TRUE when the level is set of melodies - */ - bool isMelodySet() const { return howGetMelody == e_melodyFromSet && !melodySet.isEmpty(); } - - /** - * @p TRUE when level note range is in given number range represented scale of instrument. - */ - bool inScaleOf(int loNoteNr, int hiNoteNr) const; - - /** - * Overloaded method with scale in Tnote objects - */ - bool inScaleOf(const Tnote &loN, const Tnote &hiN) const { return inScaleOf(loN.chromatic(), hiN.chromatic()); } - - /** - * Overloaded method where instrument scale is taken from @p Tglobals - */ - bool inScaleOf(); - - /** - * Examines level scale, note by note to find lowest and highest frets used. - * Obtained range is returned through references. - * Returns true when both frets are in instrument capabilities, - * or false if not - then references remind untouched. - * This method doesn't change any level value. - */ - bool adjustFretsToScale(char& loF, char& hiF); - - /** - * Writes level parameters into 'level' node of XML stream - */ - void writeToXml(QXmlStreamWriter& xml); - - /** - * Reads level from XML stream - */ - EerrorType loadFromXml(QXmlStreamReader& xml); - - /** - * Reads 'qaType' key from XML. Determines level var by id and sets it - */ - EerrorType qaTypeFromXml(QXmlStreamReader& xml); - - /** - * @p TRUE when all rhythms parameters (any meter, any rhythmic group and bar number) are set - */ - bool useRhythms() const; - - /** - * Returns number of key signatures in the range of keys - * or @p 1 if no keys in use, or just a single key - */ - int keysInRange() const; - -//------------------------- to fix a level --------------------------------------------------- - - /** - * Since Nootka 2 (versions 1.5 and above) dropped bass clef - * was changed to simply bass clef (without any drop), - * but with instrument transposition one octave down. - * So all notes (melodies, range and so) HAVE TO BE transposed as well - * by one octave up (12 semitones). - */ - void convFromDropedBass(); - - /** - * Returns detected clef from level versions before 0.8.90 - */ - Tclef fixClef(quint16 cl); - - /** - * Fixes instrument value taken from file (stream) created before level Version 2 - */ - Tinstrument::Etype fixInstrument(quint8 instr); - - /** - * Checks what kind of instrument should be used by level. - * In contrary to fixInstrument() method it doesn't check Tglobals state of instrument - * just takes given parameter. - */ - Tinstrument::Etype detectInstrument(Tinstrument::Etype currInstr); - - Tlevel::EerrorType fixFretRange(); /**< When loFret is bigger than hiFret it swaps their values */ - Tlevel::EerrorType fixNoteRange(); /**< When loNote is higher than hiNote it swaps their values */ - Tlevel::EerrorType fixKeyRange(); /**< When loKey is bigger than hiKey it swaps their values */ + /** + * default constructor creates a "complex" level (master of masters) + */ + Tlevel(); -}; + /** + * Possible errors during reading level file or XML key. + */ + enum EerrorType { + e_level_OK = 0, + e_levelFixed, /**< level parameters were fixed */ + e_noLevelInXml, /**< when XML stream has no <level> tag */ + e_otherError + }; /** - * Reads level data from given stream to @p lev. Respects @p ver - version + * Describes how to generate melody */ -NOOTKACORE_EXPORT bool getLevelFromStream(QDataStream& in, Tlevel& lev, qint32 ver); + enum EhowGetMelody : quint8 { + e_randFromRange = 1, /**< melody is composed from random notes in level range */ + e_randFromList = 2, /**< melody is composed from list of selected notes (@p notesList) */ + e_melodyFromSet = 4 /**< melody from set of melodies (either randomized or asked in the list order). (@p melodySet) */ + }; -Q_DECLARE_METATYPE(Tlevel*) + //------------------------- Managing level versions ------------------------------------------------------ + static const qint32 levelVersion; /**< First version, also with early using of intonation and instruments */ + static const qint32 currentVersion; /**< Current level version identifier */ -#endif // TEXAMLEVEL_H + /** + * Returns true when given value match to level versions. + */ + static bool isLevelVersion(quint32 ver); + + /** + * Generates level version identifier number from given simple version number (1, 2 etc.) + * It doesn't verify does given number make sense! + */ + static qint32 getVersionId(quint8 verNr) { return levelVersion + (verNr - 1) * 2; } + + /** + * Returns number of level version like 1, 2 or so + */ + static int levelVersionNr(qint32 ver); + + /** + * Returns true when given value 'could be' or 'it is' some version of exam level. + * This way level created with newer Nootka version can be detected. + */ + static bool couldBeLevel(qint32 ver); + + /** + * Saves level to given file and returns true for success or opposite. + */ + static bool saveToFile(Tlevel &level, const QString &levelFile); + + /** + * Shows message box with error if file cannot be opened. + */ + static void fileIOerrorMsg(QFile &f); + + /** + * Reads fret number from current XML key to @fr reference and verifies it. + * Setts error type when error occurs or lives @p err unchanged when OK. + */ + static void fretFromXml(QXmlStreamReader &xml, char &fr, Tlevel::EerrorType &err); + + static void skipCurrentXmlKey(QXmlStreamReader &xml); + + //--------------------------- level parameters ------------------------------------------------------------ + QString name; /**< Level name */ + QString desc; /**< description */ + // QUESTIONS + TQAtype questionAs; + TQAtype answersAs[4]; + bool forceAccids; + bool requireOctave; + bool requireStyle; + bool showStrNr; /**< Shows a string number in questions */ + quint8 intonation; /**< This shares byte with isSingleKey in file */ + Tclef clef; + Tinstrument::Etype instrument; + bool onlyLowPos; + bool onlyCurrKey; + // ACCIDENTALS + bool withSharps, withFlats, withDblAcc; + bool useKeySign; + bool isSingleKey; + TkeySignature loKey, hiKey; /**< range of key signature */ + bool manualKey; /**< manually selecting key in answers */ + // MELODIES + quint16 melodyLen; /**< Notes count in a melody */ + bool endsOnTonic; + bool requireInTempo; + EhowGetMelody howGetMelody; /**< How melody is composed (from range, from notes list or from set of melodies) */ + QList<Tnote> notesList; /**< List with notes from which melody is composed */ + TkeySignature keyOfrandList; /**< Key signature of note list for composing random melodies */ + QList<Tmelody> melodySet; /**< List of defined melodies when @p howGetMelody is @p e_melodyFromSet */ + bool randOrderInSet; /**< order of melodies in the set, @p TRUE means random */ + quint8 repeatNrInSet; /**< How many times a question is repeated with a melody */ + // RHYTHMS + quint32 basicRhythms; + quint32 dotsRhythms; + quint16 meters; /**< Bits of time signatures used in the level */ + quint8 rhythmDiversity; + quint8 barNumber; + bool variableBarNr; /**< Whether bar number grows or not as exam/exercise is progressing */ + bool useRests; + bool useTies; + // RANGE + Tnote loNote; /**< Lowest level note */ + Tnote hiNote; /**< Highest level note */ + char loFret, hiFret; + bool usedStrings[6]; + + /** + * Indicates when instrument read from file needs user action to be properly obtained. + * It occurs when read value is 255 for level version 1 + */ + bool hasInstrToFix; + + // some helpers + /** + * @p TRUE if answer or question is note on a score + */ + bool canBeScore() const; + + /** + * @p TRUE if answer or question is note name + */ + bool canBeName() const; + + /** + * @p TRUE if answer or question can be on an instrument + */ + bool canBeInstr() const; + + /** + * @p TRUE if answer or question is played or sang sound + */ + bool canBeSound() const; + + /** + * @p TRUE when question/answer has more notes and have appropriate types + */ + bool canBeMelody() const; + + /** + * @p TRUE if answer is note on a score in any question type + */ + bool answerIsNote() const; + + /** + * @p TRUE if answer is note name in any question type + */ + bool answerIsName() const; + + /** + * @p TRUE if answer is position on the guitar in any question type + */ + bool answerIsGuitar() const; + + /** + * @p TRUE if answer is played sound in any question type + */ + bool answerIsSound() const; + + /** + * @p TRUE when the level is set of melodies + */ + bool isMelodySet() const { return howGetMelody == e_melodyFromSet && !melodySet.isEmpty(); } + + /** + * @p TRUE when level note range is in given number range represented scale of instrument. + */ + bool inScaleOf(int loNoteNr, int hiNoteNr) const; + + /** + * Overloaded method with scale in Tnote objects + */ + bool inScaleOf(const Tnote &loN, const Tnote &hiN) const { return inScaleOf(loN.chromatic(), hiN.chromatic()); } + + /** + * Overloaded method where instrument scale is taken from @p Tglobals + */ + bool inScaleOf(); + + /** + * Examines level scale, note by note to find lowest and highest frets used. + * Obtained range is returned through references. + * Returns true when both frets are in instrument capabilities, + * or false if not - then references remind untouched. + * This method doesn't change any level value. + */ + bool adjustFretsToScale(char &loF, char &hiF); + + /** + * Writes level parameters into 'level' node of XML stream + */ + void writeToXml(QXmlStreamWriter &xml); + + /** + * Reads level from XML stream + */ + EerrorType loadFromXml(QXmlStreamReader &xml); + /** + * Reads 'qaType' key from XML. Determines level var by id and sets it + */ + EerrorType qaTypeFromXml(QXmlStreamReader &xml); + + /** + * @p TRUE when all rhythms parameters (any meter, any rhythmic group and bar number) are set + */ + bool useRhythms() const; + + /** + * Returns number of key signatures in the range of keys + * or @p 1 if no keys in use, or just a single key + */ + int keysInRange() const; + + //------------------------- to fix a level --------------------------------------------------- + + /** + * Since Nootka 2 (versions 1.5 and above) dropped bass clef + * was changed to simply bass clef (without any drop), + * but with instrument transposition one octave down. + * So all notes (melodies, range and so) HAVE TO BE transposed as well + * by one octave up (12 semitones). + */ + void convFromDropedBass(); + + /** + * Returns detected clef from level versions before 0.8.90 + */ + Tclef fixClef(quint16 cl); + + /** + * Fixes instrument value taken from file (stream) created before level Version 2 + */ + Tinstrument::Etype fixInstrument(quint8 instr); + + /** + * Checks what kind of instrument should be used by level. + * In contrary to fixInstrument() method it doesn't check Tglobals state of instrument + * just takes given parameter. + */ + Tinstrument::Etype detectInstrument(Tinstrument::Etype currInstr); + + Tlevel::EerrorType fixFretRange(); /**< When loFret is bigger than hiFret it swaps their values */ + Tlevel::EerrorType fixNoteRange(); /**< When loNote is higher than hiNote it swaps their values */ + Tlevel::EerrorType fixKeyRange(); /**< When loKey is bigger than hiKey it swaps their values */ +}; + +/** + * Reads level data from given stream to @p lev. Respects @p ver - version + */ +NOOTKACORE_EXPORT bool getLevelFromStream(QDataStream &in, Tlevel &lev, qint32 ver); + +Q_DECLARE_METATYPE(Tlevel *) + +#endif // TEXAMLEVEL_H diff --git a/src/libs/core/exam/tqagroup.cpp b/src/libs/core/exam/tqagroup.cpp index d1334fce717f96fe834cc717e8e9a0483de57ec7..a669d1f65fb112eab9dea65ceeff51e52d1d20a5 100644 --- a/src/libs/core/exam/tqagroup.cpp +++ b/src/libs/core/exam/tqagroup.cpp @@ -16,34 +16,32 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tqagroup.h" - -void TQAgroup::toXml(QXmlStreamWriter& xml, const QString& tag) { - xml.writeStartElement(tag); +void TQAgroup::toXml(QXmlStreamWriter &xml, const QString &tag) +{ + xml.writeStartElement(tag); if (note.isValid()) - note.toXml(xml, QLatin1String("n")); // n like note + note.toXml(xml, QLatin1String("n")); // n like note if (!technical.isEmpty()) - technical.toXml(xml, QLatin1String("t")); // t like technical (string, finger, bowing, etc.) -// if (gr.pos().str() > 0) // DEPRECATED: Nootka 1.X used only TfingerPos -// gr.pos().toXml(xml, QLatin1String("p")); // p like position - xml.writeEndElement(); + technical.toXml(xml, QLatin1String("t")); // t like technical (string, finger, bowing, etc.) + // if (gr.pos().str() > 0) // DEPRECATED: Nootka 1.X used only TfingerPos + // gr.pos().toXml(xml, QLatin1String("p")); // p like position + xml.writeEndElement(); } - -bool TQAgroup::fromXml(QXmlStreamReader& xml) { - bool ok = true; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("n")) - note.fromXml(xml); - else if (xml.name() == QLatin1String("p")) // to properly read older *.noo versions from Nootka 1.X - pos().fromXml(xml); - else if (xml.name() == QLatin1String("t")) - technical.fromXml(xml); - else - xml.skipCurrentElement(); - } - return ok; +bool TQAgroup::fromXml(QXmlStreamReader &xml) +{ + bool ok = true; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("n")) + note.fromXml(xml); + else if (xml.name() == QLatin1String("p")) // to properly read older *.noo versions from Nootka 1.X + pos().fromXml(xml); + else if (xml.name() == QLatin1String("t")) + technical.fromXml(xml); + else + xml.skipCurrentElement(); + } + return ok; } - diff --git a/src/libs/core/exam/tqagroup.h b/src/libs/core/exam/tqagroup.h index cfcbd18daa8f5ca45c4b95b2538e974bcb47dfe2..57624ce979bf0e831da692329d50b98586012206 100644 --- a/src/libs/core/exam/tqagroup.h +++ b/src/libs/core/exam/tqagroup.h @@ -19,11 +19,9 @@ #ifndef TQAGROUP_H #define TQAGROUP_H - -#include "nootkacoreglobal.h" -#include "music/ttechnical.h" #include "music/tnote.h" - +#include "music/ttechnical.h" +#include "nootkacoreglobal.h" /** * This is simple class with @@ -34,20 +32,17 @@ */ class NOOTKACORE_EXPORT TQAgroup { - public: + TfingerPos &pos() { return technical.fingerPos(); } - TfingerPos& pos() { return technical.fingerPos(); } + Ttechnical technical; + Tnote note; - Ttechnical technical; - Tnote note; - - /** - * Save @p TQAgroup to XML. (not valid) note and position are skipped. - */ - void toXml(QXmlStreamWriter& xml, const QString& tag = QLatin1String("qa")); - bool fromXml(QXmlStreamReader& xml); + /** + * Save @p TQAgroup to XML. (not valid) note and position are skipped. + */ + void toXml(QXmlStreamWriter &xml, const QString &tag = QLatin1String("qa")); + bool fromXml(QXmlStreamReader &xml); }; - #endif // TQAGROUP_H diff --git a/src/libs/core/exam/tqatype.cpp b/src/libs/core/exam/tqatype.cpp index 4925a3696774719a5e157da66bbacc5d2ee23ce0..2fee69026587970552ce4f38ff33984933b4a9d8 100644 --- a/src/libs/core/exam/tqatype.cpp +++ b/src/libs/core/exam/tqatype.cpp @@ -16,73 +16,71 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tqatype.h" -#include <QtCore/qxmlstream.h> -#include <QtCore/qvariant.h> #include <QtCore/qmath.h> #include <QtCore/qrandom.h> - +#include <QtCore/qvariant.h> +#include <QtCore/qxmlstream.h> TQAtype::TQAtype(bool _onScore, bool _asName, bool _onInstr, bool _asSound) { - m_value = (_onScore ? 1 : 0) + (_asName ? 2 : 0) + (_onInstr ? 4 : 0) + (_asSound ? 8 : 0); + m_value = (_onScore ? 1 : 0) + (_asName ? 2 : 0) + (_onInstr ? 4 : 0) + (_asSound ? 8 : 0); } - -TQAtype::TQAtype(int val) { - m_value = static_cast<quint8>(val); +TQAtype::TQAtype(int val) +{ + m_value = static_cast<quint8>(val); } +TQAtype::Etype TQAtype::next() +{ + do { + m_index++; + if (m_index == 4) + m_index = 0; + } while (!(m_value & static_cast<quint8>(qPow(2.0, static_cast<qreal>(m_index))))); -TQAtype::Etype TQAtype::next() { - do { - m_index++; - if (m_index == 4) - m_index = 0; - } while (!(m_value & static_cast<quint8>(qPow(2.0, static_cast<qreal>(m_index))))); - - return static_cast<Etype>(m_index); + return static_cast<Etype>(m_index); } - -TQAtype::Etype TQAtype::randNext() { - m_index = QRandomGenerator::global()->bounded(4) - 1; - return next(); +TQAtype::Etype TQAtype::randNext() +{ + m_index = QRandomGenerator::global()->bounded(4) - 1; + return next(); } - -void TQAtype::toXml(int id, QXmlStreamWriter& xml) { - xml.writeStartElement(QStringLiteral("qaType")); +void TQAtype::toXml(int id, QXmlStreamWriter &xml) +{ + xml.writeStartElement(QStringLiteral("qaType")); xml.writeAttribute(QStringLiteral("id"), QVariant(id).toString()); xml.writeAttribute(QStringLiteral("score"), QVariant(isOnScore()).toString()); xml.writeAttribute(QStringLiteral("name"), QVariant(isName()).toString()); xml.writeAttribute(QStringLiteral("guitar"), QVariant(isOnInstr()).toString()); xml.writeAttribute(QStringLiteral("sound"), QVariant(isSound()).toString()); - xml.writeEndElement(); // qaType + xml.writeEndElement(); // qaType } - -int TQAtype::fromXml(QXmlStreamReader& xml) { - int id = QVariant(xml.attributes().value(QStringLiteral("id")).toString()).toInt(); - setOnScore(QVariant(xml.attributes().value(QStringLiteral("score")).toString()).toBool()); - setAsName(QVariant(xml.attributes().value(QStringLiteral("name")).toString()).toBool()); - setOnInstr(QVariant(xml.attributes().value(QStringLiteral("guitar")).toString()).toBool()); - setAsSound(QVariant(xml.attributes().value(QStringLiteral("sound")).toString()).toBool()); - xml.skipCurrentElement(); - return id; +int TQAtype::fromXml(QXmlStreamReader &xml) +{ + int id = QVariant(xml.attributes().value(QStringLiteral("id")).toString()).toInt(); + setOnScore(QVariant(xml.attributes().value(QStringLiteral("score")).toString()).toBool()); + setAsName(QVariant(xml.attributes().value(QStringLiteral("name")).toString()).toBool()); + setOnInstr(QVariant(xml.attributes().value(QStringLiteral("guitar")).toString()).toBool()); + setAsSound(QVariant(xml.attributes().value(QStringLiteral("sound")).toString()).toBool()); + xml.skipCurrentElement(); + return id; } - -QDataStream &operator << (QDataStream &out,TQAtype &qatype) { - out << qatype.isOnScore() << qatype.isName() << qatype.isOnInstr() << qatype.isSound(); - return out; +QDataStream &operator<<(QDataStream &out, TQAtype &qatype) +{ + out << qatype.isOnScore() << qatype.isName() << qatype.isOnInstr() << qatype.isSound(); + return out; } - -QDataStream &operator >> (QDataStream &in, TQAtype &qatype) { - bool b[4]; - in >> b[0] >> b[1] >> b[2] >> b[3]; - qatype = TQAtype(b[0], b[1], b[2], b[3]); - return in; +QDataStream &operator>>(QDataStream &in, TQAtype &qatype) +{ + bool b[4]; + in >> b[0] >> b[1] >> b[2] >> b[3]; + qatype = TQAtype(b[0], b[1], b[2], b[3]); + return in; } diff --git a/src/libs/core/exam/tqatype.h b/src/libs/core/exam/tqatype.h index 608bf273aa5c5f1d5253ab01aa77b8a4b053c9b9..cbb3b4e1f13715b8605157867375c2a7e9c71f5c 100644 --- a/src/libs/core/exam/tqatype.h +++ b/src/libs/core/exam/tqatype.h @@ -16,18 +16,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TQATYPE_H #define TQATYPE_H -#include <nootkacoreglobal.h> #include <QtCore/qdatastream.h> - +#include <nootkacoreglobal.h> class QXmlStreamReader; class QXmlStreamWriter; - /** * Question or Answer type witch says is appropriate type enabled or not. * @@ -38,52 +35,49 @@ class QXmlStreamWriter; */ class NOOTKACORE_EXPORT TQAtype { - public: - TQAtype() {} - TQAtype(bool _onScore, bool _asName, bool _asFretPos, bool _asSound); - TQAtype(int val); - - enum Etype : qint8 { e_onScore = 0, e_asName = 1, e_onInstr = 2, e_asSound = 3 }; - - // Those strange numbers are bit reverse values to unset one - void setOnScore(bool onScore) { m_value = onScore ? m_value | 1 : m_value & 14; } - void setAsName(bool isName) { m_value = isName ? m_value | 2 : m_value & 13; } - void setOnInstr(bool onInstr) { m_value = onInstr ? m_value | 4 : m_value & 11; } - void setAsSound(bool isSound) { m_value = isSound ? m_value | 8 : m_value & 7; } - - bool isOnScore() const { return m_value & 1; } - bool isName() const { return m_value & 2; } - bool isOnInstr() const { return m_value & 4; } - bool isSound() const { return m_value & 8; } - - /** - * Number that represents bits combination: - * @li as note : 1, @li as name : 2, @li on instrument : 4, @li as sound : 8 - */ - int value() const { return static_cast<int>(m_value); } - void setValue(int v) { m_value = static_cast<quint8>(v); } - - Etype next(); - Etype randNext(); - - /** - * Adds 'qaType' key to given XML stream with types as bool attributes. For instance: - * <qaType id="-1" score="true" name="true" guitar="true" sound="false"/> - * @p id is to intricate qaType keys in file. - */ - void toXml(int id, QXmlStreamWriter& xml); - - int fromXml(QXmlStreamReader& xml); + TQAtype() { } + TQAtype(bool _onScore, bool _asName, bool _asFretPos, bool _asSound); + TQAtype(int val); + + enum Etype : qint8 { e_onScore = 0, e_asName = 1, e_onInstr = 2, e_asSound = 3 }; + + // Those strange numbers are bit reverse values to unset one + void setOnScore(bool onScore) { m_value = onScore ? m_value | 1 : m_value & 14; } + void setAsName(bool isName) { m_value = isName ? m_value | 2 : m_value & 13; } + void setOnInstr(bool onInstr) { m_value = onInstr ? m_value | 4 : m_value & 11; } + void setAsSound(bool isSound) { m_value = isSound ? m_value | 8 : m_value & 7; } + + bool isOnScore() const { return m_value & 1; } + bool isName() const { return m_value & 2; } + bool isOnInstr() const { return m_value & 4; } + bool isSound() const { return m_value & 8; } + + /** + * Number that represents bits combination: + * @li as note : 1, @li as name : 2, @li on instrument : 4, @li as sound : 8 + */ + int value() const { return static_cast<int>(m_value); } + void setValue(int v) { m_value = static_cast<quint8>(v); } + + Etype next(); + Etype randNext(); + + /** + * Adds 'qaType' key to given XML stream with types as bool attributes. For instance: + * <qaType id="-1" score="true" name="true" guitar="true" sound="false"/> + * @p id is to intricate qaType keys in file. + */ + void toXml(int id, QXmlStreamWriter &xml); + + int fromXml(QXmlStreamReader &xml); private: - quint8 m_value = 0; - qint8 m_index = 0; - + quint8 m_value = 0; + qint8 m_index = 0; }; -NOOTKACORE_EXPORT QDataStream &operator << (QDataStream &out, TQAtype &qatype); -NOOTKACORE_EXPORT QDataStream &operator >> (QDataStream &in, TQAtype &qatype); - +NOOTKACORE_EXPORT QDataStream &operator<<(QDataStream &out, TQAtype &qatype); +NOOTKACORE_EXPORT QDataStream &operator>>(QDataStream &in, TQAtype &qatype); #endif // TQATYPE_H diff --git a/src/libs/core/exam/tqaunit.cpp b/src/libs/core/exam/tqaunit.cpp index 1e374d46f5311efe0c90a64fcbf3a2ca7b49d557..91a5926b31746a76052f08dadc6ecfae387950b3 100644 --- a/src/libs/core/exam/tqaunit.cpp +++ b/src/libs/core/exam/tqaunit.cpp @@ -17,165 +17,189 @@ ***************************************************************************/ #include "tqaunit.h" +#include "music/tmelody.h" #include "tattempt.h" #include "texam.h" #include "tlevel.h" -#include "music/tmelody.h" -#include <cmath> -#include <QtCore/qxmlstream.h> #include <QtCore/qdebug.h> +#include <QtCore/qxmlstream.h> +#include <cmath> +// ###################################################################### +// ####################### CONSTRUCTORS ########################### +// ###################################################################### -//###################################################################### -//####################### CONSTRUCTORS ########################### -//###################################################################### - - -TQAunit::TQAunit(Texam* exam) +TQAunit::TQAunit(Texam *exam) { - qa.pos(); - style = 50; // (2 + 1) * 16 + 2 // 2 is e_italiano_Si cast to int - p_valid = 0; // correct in assume - time = 0; - qa_2.pos(); - m_melody = nullptr; - attemptList = nullptr; - m_effectiveness = 0.0; - m_exam = exam; - m_srcMelody = e_srcNoMelody; - p_idOfMelody = -1; - m_answered = false; + qa.pos(); + style = 50; // (2 + 1) * 16 + 2 // 2 is e_italiano_Si cast to int + p_valid = 0; // correct in assume + time = 0; + qa_2.pos(); + m_melody = nullptr; + attemptList = nullptr; + m_effectiveness = 0.0; + m_exam = exam; + m_srcMelody = e_srcNoMelody; + p_idOfMelody = -1; + m_answered = false; } - -void TQAunit::copy(const TQAunit& otherUnit) { - questionAs = otherUnit.questionAs; - answerAs = otherUnit.answerAs; - qa = otherUnit.qa; - setStyle(otherUnit.styleOfQuestion(), otherUnit.styleOfAnswer()); - setMistake(otherUnit.mistake()); - qa_2 = otherUnit.qa_2; - key = otherUnit.key; - time = otherUnit.time; - if (otherUnit.melody() || otherUnit.attemptsCount()) { - deleteMelody(); - m_melody = nullptr; - attemptList = nullptr; - p_idOfMelody = -1; - m_srcMelody = e_srcNoMelody; - } else { - m_melody = otherUnit.melody(); - attemptList = otherUnit.attemptList; - p_idOfMelody = otherUnit.p_idOfMelody; - m_srcMelody = otherUnit.melodySource(); - } - m_answered = otherUnit.answered(); - m_effectiveness = otherUnit.effectiveness(); - m_exam = otherUnit.exam(); +void TQAunit::copy(const TQAunit &otherUnit) +{ + questionAs = otherUnit.questionAs; + answerAs = otherUnit.answerAs; + qa = otherUnit.qa; + setStyle(otherUnit.styleOfQuestion(), otherUnit.styleOfAnswer()); + setMistake(otherUnit.mistake()); + qa_2 = otherUnit.qa_2; + key = otherUnit.key; + time = otherUnit.time; + if (otherUnit.melody() || otherUnit.attemptsCount()) { + deleteMelody(); + m_melody = nullptr; + attemptList = nullptr; + p_idOfMelody = -1; + m_srcMelody = e_srcNoMelody; + } else { + m_melody = otherUnit.melody(); + attemptList = otherUnit.attemptList; + p_idOfMelody = otherUnit.p_idOfMelody; + m_srcMelody = otherUnit.melodySource(); + } + m_answered = otherUnit.answered(); + m_effectiveness = otherUnit.effectiveness(); + m_exam = otherUnit.exam(); } - TQAunit::~TQAunit() { - if (attemptList) { - for (int i = 0; i < attemptList->size(); ++i) - delete attemptList->at(i); - delete attemptList; - } - deleteMelody(); + if (attemptList) { + for (int i = 0; i < attemptList->size(); ++i) + delete attemptList->at(i); + delete attemptList; + } + deleteMelody(); } -//###################################################################### -//####################### PUBLIC ########################### -//###################################################################### +// ###################################################################### +// ####################### PUBLIC ########################### +// ###################################################################### -void TQAunit::setMistake(Emistake mis) { - switch (mis) { - case e_correct: p_valid = 0; break; - case e_wrongAccid: p_valid |= 1; break; - case e_wrongKey: p_valid |= 2; break; - case e_wrongOctave: p_valid |= 4; break; - case e_wrongStyle: p_valid |= 8; break; - case e_wrongPos: p_valid |= 16; break; - case e_wrongString: p_valid |= 32; break; - case e_wrongIntonation: p_valid |= 128; break; - case e_littleNotes: p_valid |= 256; break; - case e_poorEffect: p_valid |= 512; break; - case e_wrongTempo: p_valid |= e_wrongTempo; break; - case e_veryPoor: p_valid = 1024; break; // If this kind of mistakes are committed... - case e_wrongNote: p_valid = 64; break; // all above has no sense so '=' instead '|=' is correct - case e_wrongRhythm: p_valid |= e_wrongRhythm; break; - } +void TQAunit::setMistake(Emistake mis) +{ + switch (mis) { + case e_correct: + p_valid = 0; + break; + case e_wrongAccid: + p_valid |= 1; + break; + case e_wrongKey: + p_valid |= 2; + break; + case e_wrongOctave: + p_valid |= 4; + break; + case e_wrongStyle: + p_valid |= 8; + break; + case e_wrongPos: + p_valid |= 16; + break; + case e_wrongString: + p_valid |= 32; + break; + case e_wrongIntonation: + p_valid |= 128; + break; + case e_littleNotes: + p_valid |= 256; + break; + case e_poorEffect: + p_valid |= 512; + break; + case e_wrongTempo: + p_valid |= e_wrongTempo; + break; + case e_veryPoor: + p_valid = 1024; + break; // If this kind of mistakes are committed... + case e_wrongNote: + p_valid = 64; + break; // all above has no sense so '=' instead '|=' is correct + case e_wrongRhythm: + p_valid |= e_wrongRhythm; + break; + } } - -void TQAunit::newAttempt() { - if (!attemptList) - attemptList = new QList<Tattempt*>; - (*attemptList) << new Tattempt(); +void TQAunit::newAttempt() +{ + if (!attemptList) + attemptList = new QList<Tattempt *>; + (*attemptList) << new Tattempt(); } - -int TQAunit::totalPlayBacks() { - int result = 0; - if (attemptList) { - for (int i = 0; i < attemptList->size(); ++i) - result += attemptList->at(i)->playedCount(); - } - return result; +int TQAunit::totalPlayBacks() +{ + int result = 0; + if (attemptList) { + for (int i = 0; i < attemptList->size(); ++i) + result += attemptList->at(i)->playedCount(); + } + return result; } - - -void TQAunit::addMelody(const QString& title) { - deleteMelody(); - m_srcMelody = e_srcThisUnit; - m_melody = new Tmelody(title); +void TQAunit::addMelody(const QString &title) +{ + deleteMelody(); + m_srcMelody = e_srcThisUnit; + m_melody = new Tmelody(title); } - -void TQAunit::addMelody(Tmelody* mel, TQAunit::EmelodySrc source, int id) { - deleteMelody(); - m_srcMelody = source; - m_melody = mel; - p_idOfMelody = id; +void TQAunit::addMelody(Tmelody *mel, TQAunit::EmelodySrc source, int id) +{ + deleteMelody(); + m_srcMelody = source; + m_melody = mel; + p_idOfMelody = id; } - - -void TQAunit::updateEffectiveness() { - if (attemptList && attemptsCount()) { // melody - double fee = pow(ATTEMPT_FEE, (double)(attemptsCount() - 1)); // decrease effectiveness by 4% for every additional attempt - m_effectiveness = lastAttempt()->effectiveness() * fee; // first attempt - power = 0, fee is 1 - } else { // single note - m_effectiveness = CORRECT_EFF; - if (isNotSoBad()) - m_effectiveness = NOTBAD_EFF; - else if (isWrong()) - m_effectiveness = 0.0; - } +void TQAunit::updateEffectiveness() +{ + if (attemptList && attemptsCount()) { // melody + double fee = pow(ATTEMPT_FEE, (double)(attemptsCount() - 1)); // decrease effectiveness by 4% for every additional attempt + m_effectiveness = lastAttempt()->effectiveness() * fee; // first attempt - power = 0, fee is 1 + } else { // single note + m_effectiveness = CORRECT_EFF; + if (isNotSoBad()) + m_effectiveness = NOTBAD_EFF; + else if (isWrong()) + m_effectiveness = 0.0; + } } - -void TQAunit::toXml(QXmlStreamWriter& xml) { - xml.writeStartElement(QLatin1String("u")); //u like unit +void TQAunit::toXml(QXmlStreamWriter &xml) +{ + xml.writeStartElement(QLatin1String("u")); // u like unit if (qa.note.isValid() || qa.pos().isValid()) - qa.toXml(xml); + qa.toXml(xml); xml.writeTextElement(QLatin1String("q"), QVariant((qint8)questionAs).toString()); xml.writeTextElement(QLatin1String("a"), QVariant((qint8)answerAs).toString()); xml.writeTextElement(QLatin1String("s"), QVariant(style).toString()); if (key.value() || key.isMinor()) // skip key signature when C-major (default), fromXml() will guess it - key.toXml(xml); + key.toXml(xml); xml.writeTextElement(QLatin1String("t"), QVariant(time).toString()); if (time == 0) - qDebug() << "[TQAunit] Answer time is 0 - faster than light speed?"; + qDebug() << "[TQAunit] Answer time is 0 - faster than light speed?"; xml.writeTextElement("m", QVariant(mistake()).toString()); if (!answered()) // in most cases saved unit is answered - xml.writeTextElement("answered", QVariant(answered()).toString()); + xml.writeTextElement("answered", QVariant(answered()).toString()); if (qa_2.note.isValid() || qa_2.pos().isValid()) qa_2.toXml(xml, QLatin1String("qa2")); if (melody()) { - xml.writeStartElement(QLatin1String("melody")); + xml.writeStartElement(QLatin1String("melody")); if (m_srcMelody == e_srcThisUnit) { xml.writeAttribute(QLatin1String("title"), melody()->title()); melody()->toXml(xml); @@ -183,129 +207,129 @@ void TQAunit::toXml(QXmlStreamWriter& xml) { xml.writeAttribute(QLatin1String("qNr"), QVariant(p_idOfMelody).toString()); else if (m_srcMelody == e_srcLevelSet) xml.writeAttribute(QLatin1String("id"), QVariant(p_idOfMelody).toString()); - xml.writeEndElement(); - xml.writeStartElement(QLatin1String("attempts")); - for (int i = 0; i < attemptsCount(); ++i) { - if (!attempt(i)->isEmpty()) // do not save empty attempts - attempt(i)->toXml(xml); - } - xml.writeEndElement(); // attempts - } - xml.writeEndElement(); // u + xml.writeEndElement(); + xml.writeStartElement(QLatin1String("attempts")); + for (int i = 0; i < attemptsCount(); ++i) { + if (!attempt(i)->isEmpty()) // do not save empty attempts + attempt(i)->toXml(xml); + } + xml.writeEndElement(); // attempts + } + xml.writeEndElement(); // u } /** We no need to worry about reset TQAunit because it is invoked just after its constructor - it is clean already. */ -bool TQAunit::fromXml(QXmlStreamReader& xml) { - bool ok = true; - m_answered = true; // this state is common and not saved, so if <answered> key exists it is false then. - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("qa")) - qa.fromXml(xml); - else if (xml.name() == QLatin1String("qa2")) - qa_2.fromXml(xml); - else if (xml.name() == QLatin1String("q")) - questionAs = (TQAtype::Etype)xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("a")) - answerAs = (TQAtype::Etype)xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("s")) - style = (TQAtype::Etype)xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("key")) - key.fromXml(xml); - else if (xml.name() == QLatin1String("t")) - time = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("m")) - p_valid = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("answered")) - m_answered = QVariant(xml.readElementText()).toBool(); // or m_answered = false - else if (xml.name() == QLatin1String("melody")) { - if (xml.attributes().hasAttribute(QLatin1String("title"))) { - addMelody(xml.attributes().value(QLatin1String("title")).toString()); - if (!melody()->fromXml(xml)) { - qDebug() << "[TQAunit] has wrong melody"; - ok = false; - delete m_melody; - m_melody = nullptr; - } - } else if (xml.attributes().hasAttribute(QLatin1String("qNr"))) { - int qNr = xml.attributes().value(QLatin1String("qNr")).toInt(); - if (qNr < m_exam->count()) - addMelody(m_exam->answList()->at(qNr)->melody(), e_srcOtherUnit, qNr); - else { - ok = false; - qDebug() << "[TQAunit] has a melody that points to question number which doesn't exist in exam list."; - } - xml.skipCurrentElement(); - } else if (xml.attributes().hasAttribute(QLatin1String("id"))) { - int id = xml.attributes().value(QLatin1String("id")).toInt(); - if (id >= 0 && id < m_exam->level()->melodySet.size()) { - addMelody(&m_exam->level()->melodySet[id], e_srcLevelSet, id); - } else { - ok = false; - qDebug() << "[TQAunit] id" << id << "points to not existing melody in the level:" << m_exam->level()->name; - } - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("attempts")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("a")) { - newAttempt(); - lastAttempt()->fromXml(xml); - if (lastAttempt()->isEmpty()) { // Empty attempts are never written - they never occurs - qDebug() << "[TQAunit] has wrong attempt" << attemptsCount(); - ok = false; - attemptList->removeLast(); - } - } else - xml.skipCurrentElement(); - } +bool TQAunit::fromXml(QXmlStreamReader &xml) +{ + bool ok = true; + m_answered = true; // this state is common and not saved, so if <answered> key exists it is false then. + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("qa")) + qa.fromXml(xml); + else if (xml.name() == QLatin1String("qa2")) + qa_2.fromXml(xml); + else if (xml.name() == QLatin1String("q")) + questionAs = (TQAtype::Etype)xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("a")) + answerAs = (TQAtype::Etype)xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("s")) + style = (TQAtype::Etype)xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("key")) + key.fromXml(xml); + else if (xml.name() == QLatin1String("t")) + time = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("m")) + p_valid = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("answered")) + m_answered = QVariant(xml.readElementText()).toBool(); // or m_answered = false + else if (xml.name() == QLatin1String("melody")) { + if (xml.attributes().hasAttribute(QLatin1String("title"))) { + addMelody(xml.attributes().value(QLatin1String("title")).toString()); + if (!melody()->fromXml(xml)) { + qDebug() << "[TQAunit] has wrong melody"; + ok = false; + delete m_melody; + m_melody = nullptr; + } + } else if (xml.attributes().hasAttribute(QLatin1String("qNr"))) { + int qNr = xml.attributes().value(QLatin1String("qNr")).toInt(); + if (qNr < m_exam->count()) + addMelody(m_exam->answList()->at(qNr)->melody(), e_srcOtherUnit, qNr); + else { + ok = false; + qDebug() << "[TQAunit] has a melody that points to question number which doesn't exist in exam list."; + } + xml.skipCurrentElement(); + } else if (xml.attributes().hasAttribute(QLatin1String("id"))) { + int id = xml.attributes().value(QLatin1String("id")).toInt(); + if (id >= 0 && id < m_exam->level()->melodySet.size()) { + addMelody(&m_exam->level()->melodySet[id], e_srcLevelSet, id); + } else { + ok = false; + qDebug() << "[TQAunit] id" << id << "points to not existing melody in the level:" << m_exam->level()->name; + } + xml.skipCurrentElement(); + } + } else if (xml.name() == QLatin1String("attempts")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("a")) { + newAttempt(); + lastAttempt()->fromXml(xml); + if (lastAttempt()->isEmpty()) { // Empty attempts are never written - they never occurs + qDebug() << "[TQAunit] has wrong attempt" << attemptsCount(); + ok = false; + attemptList->removeLast(); + } + } else + xml.skipCurrentElement(); + } + } else + xml.skipCurrentElement(); } - else - xml.skipCurrentElement(); - } - updateEffectiveness(); - return ok; + updateEffectiveness(); + return ok; } - -void TQAunit::deleteMelody() { - if (m_melody && m_srcMelody == e_srcThisUnit) - delete m_melody; +void TQAunit::deleteMelody() +{ + if (m_melody && m_srcMelody == e_srcThisUnit) + delete m_melody; } - -bool getTQAunitFromStream(QDataStream &in, TQAunit &qaUnit) { - bool ok = true; - ok = getNoteFromStream(in, qaUnit.qa.note); - in >> qaUnit.qa.pos(); - qint8 qu, an; - in >> qu >> an; - qaUnit.questionAs = (TQAtype::Etype)qu; - qaUnit.answerAs = (TQAtype::Etype)an; - in >> qaUnit.style; - ok = getKeyFromStream(in, qaUnit.key); - in >> qaUnit.time; -// getNoteFromStream() is too smart and doesn't allow null Tnote(0,0,0) -// We have to cheat it.... - bool ok2 = getNoteFromStream(in, qaUnit.qa_2.note); - if (!ok2) - qaUnit.qa_2.note = Tnote(0,0,0); - in >> qaUnit.qa_2.pos(); - quint8 valid_uint8; - in >> valid_uint8; - qaUnit.p_valid = valid_uint8; -// in >> qaUnit.valid; - qaUnit.updateEffectiveness(); - return ok; +bool getTQAunitFromStream(QDataStream &in, TQAunit &qaUnit) +{ + bool ok = true; + ok = getNoteFromStream(in, qaUnit.qa.note); + in >> qaUnit.qa.pos(); + qint8 qu, an; + in >> qu >> an; + qaUnit.questionAs = (TQAtype::Etype)qu; + qaUnit.answerAs = (TQAtype::Etype)an; + in >> qaUnit.style; + ok = getKeyFromStream(in, qaUnit.key); + in >> qaUnit.time; + // getNoteFromStream() is too smart and doesn't allow null Tnote(0,0,0) + // We have to cheat it.... + bool ok2 = getNoteFromStream(in, qaUnit.qa_2.note); + if (!ok2) + qaUnit.qa_2.note = Tnote(0, 0, 0); + in >> qaUnit.qa_2.pos(); + quint8 valid_uint8; + in >> valid_uint8; + qaUnit.p_valid = valid_uint8; + // in >> qaUnit.valid; + qaUnit.updateEffectiveness(); + return ok; } - -void TQAunit::riseOctaveUp() { - qa.note.riseOctaveUp(); - qa_2.note.riseOctaveUp(); - if (m_melody && m_srcMelody == e_srcThisUnit) { - if (m_melody->clef() == Tclef::Bass_F_8down) - m_melody->setClef(Tclef::Bass_F); - for (int n = 0; n < m_melody->length(); ++n) - m_melody->note(n)->p().riseOctaveUp(); - } +void TQAunit::riseOctaveUp() +{ + qa.note.riseOctaveUp(); + qa_2.note.riseOctaveUp(); + if (m_melody && m_srcMelody == e_srcThisUnit) { + if (m_melody->clef() == Tclef::Bass_F_8down) + m_melody->setClef(Tclef::Bass_F); + for (int n = 0; n < m_melody->length(); ++n) + m_melody->note(n)->p().riseOctaveUp(); + } } diff --git a/src/libs/core/exam/tqaunit.h b/src/libs/core/exam/tqaunit.h index 5d61af849b971c3c99db85d793b4fb90c635d15e..995e01f40adab7d66e30cd7dfe54b1a7b721e67f 100644 --- a/src/libs/core/exam/tqaunit.h +++ b/src/libs/core/exam/tqaunit.h @@ -16,26 +16,22 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TQAUNIT_H #define TQAUNIT_H -#include <nootkacoreglobal.h> #include "tqagroup.h" -#include <music/tkeysignature.h> #include <exam/tqatype.h> - +#include <music/tkeysignature.h> +#include <nootkacoreglobal.h> #define CORRECT_EFF (100.0) // effectiveness of correct answer #define NOTBAD_EFF (50.0) // effectiveness of 'not bad' answer #define ATTEMPT_FEE (0.96) // decrease effectiveness by 4% (100 - 4 = 96) for every additional attempt - class Texam; class Tattempt; class Tmelody; - /** * This class describes single question and given answer. * By default a melody element (pointer) is empty and attempts as well. @@ -45,297 +41,294 @@ class Tmelody; */ class NOOTKACORE_EXPORT TQAunit { - public: - - /** - * Copy constructor - */ - TQAunit(const TQAunit& otherUnit) { copy(otherUnit); } - - /** - * Pointer to exam that keeps this unit - */ - TQAunit(Texam* exam = nullptr); - - ~TQAunit(); - - enum Emistake { - e_correct = 0, - e_wrongAccid = 1, /**< occurs during enharmonic conversion */ - e_wrongKey = 2, - e_wrongOctave = 4, - e_wrongStyle = 8, /**< for further releases when typing of note name will be implemented */ - e_wrongPos = 16, /**< when wrong position */ - e_wrongString = 32, /**< when sound is proper but not on required string */ - e_wrongNote = 64, /**< the highest crime */ - e_wrongIntonation = 128, /**< when detected sound is out of range of intonation accuracy */ - e_littleNotes = 256, /**< when number of notes in answered melody is little */ - e_poorEffect = 512, /**< effectiveness of an answered melody is less than 70% (but greater than 50%) */ - e_veryPoor = 1024, /**< effectiveness less than 50% - CARDINAL MISTAKE */ - e_wrongRhythm = 2048, /**< more notes has wrong rhythm - CARDINAL MISTAKE */ - e_wrongTempo = 4096 /**< melody was played to slow or to fast */ - }; - - /** - * Copies given unit into this one. - * IT DOESN'T COPY A MELODY AND ATTEMPTS but only their pointers - */ - void copy(const TQAunit& otherUnit); - - /** - * Returns string with time divided by 10. - * Usually time is stored in value multiplied by 10. - * @param prec defines digit number after point. - */ - static QString timeToText(int time10, int prec = 1) { return QString("%1").arg(static_cast<qreal>(time10) / 10, 0, 'f', prec); } - - /** - * Gives ready to insert string with time value. - */ - QString timeText() { return timeToText(time); } - - /** - * Returns time value divided by 10 - */ - double getTime() { return static_cast<double>(time) / 10.0; } - - /** - * Set a given mistake. - * If actual effectiveness is necessary invoke updateEffectiveness() after. - */ - void setMistake(Emistake mis); - - /** - * Sets mistakes from value. It is the same: - * setMistake(e_wrongKey); setMistake(e_wrongOctave); - * and - * setMistake(e_wrongKey | e_wrongOctave); - * or - * setMistake(6); - * If actual effectiveness is necessary invoke updateEffectiveness() after. - */ - void setMistake(quint32 misVal) { p_valid = misVal; } - - /** - * set of mistakes as Boolean sum of @p Emistake - */ - quint32 mistake() const { return p_valid; } - - TQAgroup qa; - TQAtype::Etype questionAs; - TQAtype::Etype answerAs; - - Tnote::EnameStyle styleOfQuestion() const { return Tnote::EnameStyle(style / 16 - 1); } - Tnote::EnameStyle styleOfAnswer() const { return Tnote::EnameStyle(style % 16); } - void setStyle(Tnote::EnameStyle questionStyle, Tnote::EnameStyle answerStyle) { - style = (static_cast<quint8>(questionStyle) + 1) * 16 + static_cast<quint8>(answerStyle); - } - - TkeySignature key; - - /** - * time of answer multiple by 10 - */ - quint16 time; - - /** - * expected answers when question and answer types are the same - */ - TQAgroup qa_2; - - friend bool getTQAunitFromStream(QDataStream &in, TQAunit &qaUnit); - - bool isCorrect() const { return p_valid == e_correct; } - bool wrongAccid() const { return p_valid & e_wrongAccid; } - bool wrongKey() const { return p_valid & e_wrongKey; } - bool wrongOctave() const { return p_valid & e_wrongOctave; } - bool wrongStyle() const { return p_valid & e_wrongStyle; } - bool wrongPos() const { return p_valid & e_wrongPos; } - bool wrongString() const { return p_valid & e_wrongString; } - bool wrongNote() const {return p_valid & e_wrongNote; } - bool wrongIntonation() const {return p_valid & e_wrongIntonation; } - - /** - * when number of notes in answered melody is little - */ - bool littleNotes() const {return p_valid & e_littleNotes; } - - /** - * effectiveness is less than 70% (but greater than 50%) - */ - bool poorEffect() const {return p_valid & e_poorEffect; } - - /** - * effectiveness of an answered melody is less than 50% - */ - bool veryPoor() const {return p_valid & e_veryPoor; } - - /** - * more notes has wrong rhythm - CARDINAL MISTAKE - */ - bool wrongRhythm() const { return p_valid & e_wrongRhythm; } - - /** - * melody was played to slow or to fast - */ - bool wrongTempo() const { return p_valid & e_wrongTempo; } - - /** - * questionAs == TQAtype::e_onScore - */ - bool questionOnScore() const { return questionAs == TQAtype::e_onScore; } - - /** - * questionAs == TQAtype::e_asName - */ - bool questionAsName() const { return questionAs == TQAtype::e_asName; } - - /** - * questionAs == TQAtype::e_onInstr - */ - bool questionOnInstr() const { return questionAs == TQAtype::e_onInstr; } - - /** - * questionAs == TQAtype::e_asSound - */ - bool questionAsSound() const { return questionAs == TQAtype::e_asSound; } - - /** - * answerAs == TQAtype::e_onScore - */ - bool answerOnScore() const { return answerAs == TQAtype::e_onScore; } - - /** - * answerAs == TQAtype::e_asName - */ - bool answerAsName() const { return answerAs == TQAtype::e_asName; } - - /** - * answerAs == TQAtype::e_onInstr - */ - bool answerOnInstr() const { return answerAs == TQAtype::e_onInstr; } - - /** - * answerAs == TQAtype::e_asSound - */ - bool answerAsSound() const { return answerAs == TQAtype::e_asSound; } - - bool isWrong() const { return wrongNote() || wrongPos() || veryPoor(); } - bool isNotSoBad() const { return p_valid && !wrongNote() && !wrongPos(); } - - /** - * Parent exam or null. - */ - Texam* exam() const { return m_exam; } - - /** - * Creates and adds new @class Tattempt to the attempts list. - */ - void newAttempt(); - int attemptsCount() const { return attemptList ? attemptList->size() : 0; } - - /** - * Pointer to given attempt - */ - Tattempt* attempt(int nr) { return attemptList && nr < attemptsCount() ? attemptList->operator[](nr) : nullptr; } - - /** - * Pointer to the last attempt - */ - Tattempt* lastAttempt() { return attemptList->last(); } - - /** - * Returns number of melody playback in all attempts. - */ - int totalPlayBacks(); - - /** - * Returns effectiveness of this answer. - * For single note it is 100 for correct, 50 for 'not bad' and 0 if wrong, - * for melody it is average of all attempts effectiveness - */ - qreal effectiveness() const { return m_effectiveness; } - - /** - * Updates an effectiveness value to current answer state - */ - void updateEffectiveness(); - - /** - * Determines a source of melody. @p e_srcOtherUnit points melody in another unit - * @p e_srcLevelSet points melody in level - */ - enum EmelodySrc { - e_srcNoMelody, e_srcThisUnit, e_srcOtherUnit, e_srcLevelSet - }; - - /** - * Adds melody or replaces existing one. - */ - void addMelody(const QString& title); - - /** - * Adds melody existing in given source (other unit or a list) - * and with @p id (question number or index in the list) - */ - void addMelody(Tmelody* mel, EmelodySrc source, int id); - - /** - * Pointer to question melody or null if no melody. - */ - Tmelody* melody() const { return m_melody; } - EmelodySrc melodySource() const { return m_srcMelody; } - - /** - * Finishes answering process. Unit (a question) becomes answered. - */ - void setAnswered() { m_answered = true; } - - /** - * Sets @p answered() to @p FALSE - */ - void unsetAnswered() { m_answered = false; } - - /** - * @p TRUE when answering process was finished. - */ - bool answered() const { return m_answered; } - - void toXml(QXmlStreamWriter& xml); - bool fromXml(QXmlStreamReader& xml); - - /** - * This method simply rises all unit notes one octave up. - * The ONLY ONE PURPOSE of this method is to convert/fix - * bass dropped down clef of old Nootka to ordinary bass clef, - * so all notes has to transposed octave up - */ - void riseOctaveUp(); - - /** - * Number of another question (@p e_srcOtherUnit) - * or an item in a list (@p e_srcLevelSet) that contain a melody. - */ - int idOfMelody() const { return p_idOfMelody; } + /** + * Copy constructor + */ + TQAunit(const TQAunit &otherUnit) { copy(otherUnit); } + + /** + * Pointer to exam that keeps this unit + */ + TQAunit(Texam *exam = nullptr); + + ~TQAunit(); + + enum Emistake { + e_correct = 0, + e_wrongAccid = 1, /**< occurs during enharmonic conversion */ + e_wrongKey = 2, + e_wrongOctave = 4, + e_wrongStyle = 8, /**< for further releases when typing of note name will be implemented */ + e_wrongPos = 16, /**< when wrong position */ + e_wrongString = 32, /**< when sound is proper but not on required string */ + e_wrongNote = 64, /**< the highest crime */ + e_wrongIntonation = 128, /**< when detected sound is out of range of intonation accuracy */ + e_littleNotes = 256, /**< when number of notes in answered melody is little */ + e_poorEffect = 512, /**< effectiveness of an answered melody is less than 70% (but greater than 50%) */ + e_veryPoor = 1024, /**< effectiveness less than 50% - CARDINAL MISTAKE */ + e_wrongRhythm = 2048, /**< more notes has wrong rhythm - CARDINAL MISTAKE */ + e_wrongTempo = 4096 /**< melody was played to slow or to fast */ + }; + + /** + * Copies given unit into this one. + * IT DOESN'T COPY A MELODY AND ATTEMPTS but only their pointers + */ + void copy(const TQAunit &otherUnit); + + /** + * Returns string with time divided by 10. + * Usually time is stored in value multiplied by 10. + * @param prec defines digit number after point. + */ + static QString timeToText(int time10, int prec = 1) { return QString("%1").arg(static_cast<qreal>(time10) / 10, 0, 'f', prec); } + + /** + * Gives ready to insert string with time value. + */ + QString timeText() { return timeToText(time); } + + /** + * Returns time value divided by 10 + */ + double getTime() { return static_cast<double>(time) / 10.0; } + + /** + * Set a given mistake. + * If actual effectiveness is necessary invoke updateEffectiveness() after. + */ + void setMistake(Emistake mis); + + /** + * Sets mistakes from value. It is the same: + * setMistake(e_wrongKey); setMistake(e_wrongOctave); + * and + * setMistake(e_wrongKey | e_wrongOctave); + * or + * setMistake(6); + * If actual effectiveness is necessary invoke updateEffectiveness() after. + */ + void setMistake(quint32 misVal) { p_valid = misVal; } + + /** + * set of mistakes as Boolean sum of @p Emistake + */ + quint32 mistake() const { return p_valid; } + + TQAgroup qa; + TQAtype::Etype questionAs; + TQAtype::Etype answerAs; + + Tnote::EnameStyle styleOfQuestion() const { return Tnote::EnameStyle(style / 16 - 1); } + Tnote::EnameStyle styleOfAnswer() const { return Tnote::EnameStyle(style % 16); } + void setStyle(Tnote::EnameStyle questionStyle, Tnote::EnameStyle answerStyle) + { + style = (static_cast<quint8>(questionStyle) + 1) * 16 + static_cast<quint8>(answerStyle); + } + + TkeySignature key; + + /** + * time of answer multiple by 10 + */ + quint16 time; + + /** + * expected answers when question and answer types are the same + */ + TQAgroup qa_2; + + friend bool getTQAunitFromStream(QDataStream &in, TQAunit &qaUnit); + + bool isCorrect() const { return p_valid == e_correct; } + bool wrongAccid() const { return p_valid & e_wrongAccid; } + bool wrongKey() const { return p_valid & e_wrongKey; } + bool wrongOctave() const { return p_valid & e_wrongOctave; } + bool wrongStyle() const { return p_valid & e_wrongStyle; } + bool wrongPos() const { return p_valid & e_wrongPos; } + bool wrongString() const { return p_valid & e_wrongString; } + bool wrongNote() const { return p_valid & e_wrongNote; } + bool wrongIntonation() const { return p_valid & e_wrongIntonation; } + + /** + * when number of notes in answered melody is little + */ + bool littleNotes() const { return p_valid & e_littleNotes; } + + /** + * effectiveness is less than 70% (but greater than 50%) + */ + bool poorEffect() const { return p_valid & e_poorEffect; } + + /** + * effectiveness of an answered melody is less than 50% + */ + bool veryPoor() const { return p_valid & e_veryPoor; } + + /** + * more notes has wrong rhythm - CARDINAL MISTAKE + */ + bool wrongRhythm() const { return p_valid & e_wrongRhythm; } + + /** + * melody was played to slow or to fast + */ + bool wrongTempo() const { return p_valid & e_wrongTempo; } + + /** + * questionAs == TQAtype::e_onScore + */ + bool questionOnScore() const { return questionAs == TQAtype::e_onScore; } + + /** + * questionAs == TQAtype::e_asName + */ + bool questionAsName() const { return questionAs == TQAtype::e_asName; } + + /** + * questionAs == TQAtype::e_onInstr + */ + bool questionOnInstr() const { return questionAs == TQAtype::e_onInstr; } + + /** + * questionAs == TQAtype::e_asSound + */ + bool questionAsSound() const { return questionAs == TQAtype::e_asSound; } + + /** + * answerAs == TQAtype::e_onScore + */ + bool answerOnScore() const { return answerAs == TQAtype::e_onScore; } + + /** + * answerAs == TQAtype::e_asName + */ + bool answerAsName() const { return answerAs == TQAtype::e_asName; } + + /** + * answerAs == TQAtype::e_onInstr + */ + bool answerOnInstr() const { return answerAs == TQAtype::e_onInstr; } + + /** + * answerAs == TQAtype::e_asSound + */ + bool answerAsSound() const { return answerAs == TQAtype::e_asSound; } + + bool isWrong() const { return wrongNote() || wrongPos() || veryPoor(); } + bool isNotSoBad() const { return p_valid && !wrongNote() && !wrongPos(); } + + /** + * Parent exam or null. + */ + Texam *exam() const { return m_exam; } + + /** + * Creates and adds new @class Tattempt to the attempts list. + */ + void newAttempt(); + int attemptsCount() const { return attemptList ? attemptList->size() : 0; } + + /** + * Pointer to given attempt + */ + Tattempt *attempt(int nr) { return attemptList && nr < attemptsCount() ? attemptList->operator[](nr) : nullptr; } + + /** + * Pointer to the last attempt + */ + Tattempt *lastAttempt() { return attemptList->last(); } + + /** + * Returns number of melody playback in all attempts. + */ + int totalPlayBacks(); + + /** + * Returns effectiveness of this answer. + * For single note it is 100 for correct, 50 for 'not bad' and 0 if wrong, + * for melody it is average of all attempts effectiveness + */ + qreal effectiveness() const { return m_effectiveness; } + + /** + * Updates an effectiveness value to current answer state + */ + void updateEffectiveness(); + + /** + * Determines a source of melody. @p e_srcOtherUnit points melody in another unit + * @p e_srcLevelSet points melody in level + */ + enum EmelodySrc { e_srcNoMelody, e_srcThisUnit, e_srcOtherUnit, e_srcLevelSet }; + + /** + * Adds melody or replaces existing one. + */ + void addMelody(const QString &title); + + /** + * Adds melody existing in given source (other unit or a list) + * and with @p id (question number or index in the list) + */ + void addMelody(Tmelody *mel, EmelodySrc source, int id); + + /** + * Pointer to question melody or null if no melody. + */ + Tmelody *melody() const { return m_melody; } + EmelodySrc melodySource() const { return m_srcMelody; } + + /** + * Finishes answering process. Unit (a question) becomes answered. + */ + void setAnswered() { m_answered = true; } + + /** + * Sets @p answered() to @p FALSE + */ + void unsetAnswered() { m_answered = false; } + + /** + * @p TRUE when answering process was finished. + */ + bool answered() const { return m_answered; } + + void toXml(QXmlStreamWriter &xml); + bool fromXml(QXmlStreamReader &xml); + + /** + * This method simply rises all unit notes one octave up. + * The ONLY ONE PURPOSE of this method is to convert/fix + * bass dropped down clef of old Nootka to ordinary bass clef, + * so all notes has to transposed octave up + */ + void riseOctaveUp(); + + /** + * Number of another question (@p e_srcOtherUnit) + * or an item in a list (@p e_srcLevelSet) that contain a melody. + */ + int idOfMelody() const { return p_idOfMelody; } protected: - quint32 p_valid; - quint8 style; - int p_idOfMelody; - QList<Tattempt*> *attemptList; /**< Pointer to a list with attempts or @p nullptr if no attempts */ + quint32 p_valid; + quint8 style; + int p_idOfMelody; + QList<Tattempt *> *attemptList; /**< Pointer to a list with attempts or @p nullptr if no attempts */ - /** - * Deletes this melody if exists and belongs to this unit. - */ - void deleteMelody(); + /** + * Deletes this melody if exists and belongs to this unit. + */ + void deleteMelody(); private: - Tmelody *m_melody; - EmelodySrc m_srcMelody; - qreal m_effectiveness; - Texam *m_exam; - bool m_answered; + Tmelody *m_melody; + EmelodySrc m_srcMelody; + qreal m_effectiveness; + Texam *m_exam; + bool m_answered; }; NOOTKACORE_EXPORT bool getTQAunitFromStream(QDataStream &in, TQAunit &qaUnit); diff --git a/src/libs/core/exam/tresulttext.cpp b/src/libs/core/exam/tresulttext.cpp index 245f0beb78a99292d68dc433da62ff11009b00a0..80867bc28af16bb2c1170332e851bb7f70c9a004 100644 --- a/src/libs/core/exam/tresulttext.cpp +++ b/src/libs/core/exam/tresulttext.cpp @@ -17,85 +17,93 @@ ***************************************************************************/ #include "tresulttext.h" -#include "tqaunit.h" #include "tattempt.h" +#include "tqaunit.h" #include <QtGui/qguiapplication.h> - - /** - * Adds comma and space ', ' (',<br>' on Android) to not empty string or returns the same. - */ -void addSpaceToNotEmpty(QString& txt) { - if (!txt.isEmpty()) { -#if defined (Q_OS_ANDROID) - txt += QLatin1String(",<br>"); // new line for any mistake entry under Android (big letters) +/** + * Adds comma and space ', ' (',<br>' on Android) to not empty string or returns the same. + */ +void addSpaceToNotEmpty(QString &txt) +{ + if (!txt.isEmpty()) { +#if defined(Q_OS_ANDROID) + txt += QLatin1String(",<br>"); // new line for any mistake entry under Android (big letters) #else - txt += QLatin1String(", "); + txt += QLatin1String(", "); #endif - } + } } - /** Checks the length of string @p txt and adds new line tag if necessary */ -void newLineText(QString& txt, const QString& newText) { -#if !defined (Q_OS_ANDROID) // ignore it under mobile - if (txt.length() > 20 && !txt.contains(QLatin1String("<br>"))) - txt += QLatin1String("<br>"); +void newLineText(QString &txt, const QString &newText) +{ +#if !defined(Q_OS_ANDROID) // ignore it under mobile + if (txt.length() > 20 && !txt.contains(QLatin1String("<br>"))) + txt += QLatin1String("<br>"); #endif - txt += newText; + txt += newText; } - -QString wasAnswerOKtext(TQAunit* answer, int attempt) { - QString txt; - TQAunit curQ; - if (answer->melody() && attempt > 0 && attempt <= answer->attemptsCount()) - curQ.setMistake(answer->attempt(attempt - 1)->summary()); - else - curQ.setMistake(answer->mistake()); - if (curQ.isCorrect()) { - txt += QGuiApplication::translate("AnswerText", "Good answer!", "or 'Good!' or 'Correct!' would be somewhat more specific than merely 'It was good!' (previous version) 'It' in this case certainly does refer to a specific thing, which is in this case the answer, but it might be momentarily confused with some other specific thing, such as a shoe or a crocodile, or the wind on one's back. I know that's probably confusing, but the implied subject of 'Correct! is in a certain sense much more specific than a mere 'It' and is more certain to refer to the answer."); - } else - if (curQ.wrongNote() || curQ.wrongPos() || curQ.veryPoor()) - txt += QGuiApplication::translate("AnswerText", "Wrong answer!"); - else { - txt += QGuiApplication::translate("AnswerText", "Not bad, but:", "'Not so bad, but:' is perfectly clear, but a little less common in US English. To be a bit shorter, it might just as well be, 'Not bad, but:'") + QLatin1String("<br>"); - QString misMes; // Message with mistakes - if (curQ.wrongString()) +QString wasAnswerOKtext(TQAunit *answer, int attempt) +{ + QString txt; + TQAunit curQ; + if (answer->melody() && attempt > 0 && attempt <= answer->attemptsCount()) + curQ.setMistake(answer->attempt(attempt - 1)->summary()); + else + curQ.setMistake(answer->mistake()); + if (curQ.isCorrect()) { + txt += QGuiApplication::translate( + "AnswerText", + "Good answer!", + "or 'Good!' or 'Correct!' would be somewhat more specific than merely 'It was good!' (previous version) 'It' in this case certainly does refer to " + "a specific thing, which is in this case the answer, but it might be momentarily confused with some other specific thing, such as a shoe or a " + "crocodile, or the wind on one's back. I know that's probably confusing, but the implied subject of 'Correct! is in a certain sense much more " + "specific than a mere 'It' and is more certain to refer to the answer."); + } else if (curQ.wrongNote() || curQ.wrongPos() || curQ.veryPoor()) + txt += QGuiApplication::translate("AnswerText", "Wrong answer!"); + else { + txt += + QGuiApplication::translate( + "AnswerText", + "Not bad, but:", + "'Not so bad, but:' is perfectly clear, but a little less common in US English. To be a bit shorter, it might just as well be, 'Not bad, but:'") + + QLatin1String("<br>"); + QString misMes; // Message with mistakes + if (curQ.wrongString()) misMes = QGuiApplication::translate("AnswerText", "wrong string"); - if (answer->melody() && curQ.littleNotes()) + if (answer->melody() && curQ.littleNotes()) misMes = QGuiApplication::translate("AnswerText", "little valid notes", "the amount of correct notes in an answer is little"); - if (answer->melody() && curQ.wrongRhythm()) { + if (answer->melody() && curQ.wrongRhythm()) { addSpaceToNotEmpty(misMes); newLineText(misMes, QGuiApplication::translate("AnswerText", "incorrect rhythm")); - } - if (curQ.poorEffect()) { + } + if (curQ.poorEffect()) { addSpaceToNotEmpty(misMes); -#if !defined (Q_OS_ANDROID) // Under mobile - above method does it - if (!misMes.isEmpty()) +#if !defined(Q_OS_ANDROID) // Under mobile - above method does it + if (!misMes.isEmpty()) misMes += QLatin1String("<br>"); #endif - misMes += QGuiApplication::translate("AnswerText", "poor effectiveness"); - } - - if (curQ.wrongAccid()) { - misMes = QGuiApplication::translate("AnswerText", "wrong accidental"); - } - if (curQ.wrongKey()) { - addSpaceToNotEmpty(misMes); - newLineText(misMes, QGuiApplication::translate("AnswerText", "wrong key signature")); - } - if (curQ.wrongOctave()) { - addSpaceToNotEmpty(misMes); - newLineText(misMes, QGuiApplication::translate("AnswerText", "wrong octave")); - } - if (curQ.wrongIntonation()) { - addSpaceToNotEmpty(misMes); - newLineText(misMes, QGuiApplication::translate("AnswerText", "out of tune")); - } - txt += misMes; - } - return txt; + misMes += QGuiApplication::translate("AnswerText", "poor effectiveness"); + } + if (curQ.wrongAccid()) { + misMes = QGuiApplication::translate("AnswerText", "wrong accidental"); + } + if (curQ.wrongKey()) { + addSpaceToNotEmpty(misMes); + newLineText(misMes, QGuiApplication::translate("AnswerText", "wrong key signature")); + } + if (curQ.wrongOctave()) { + addSpaceToNotEmpty(misMes); + newLineText(misMes, QGuiApplication::translate("AnswerText", "wrong octave")); + } + if (curQ.wrongIntonation()) { + addSpaceToNotEmpty(misMes); + newLineText(misMes, QGuiApplication::translate("AnswerText", "out of tune")); + } + txt += misMes; + } + return txt; } - diff --git a/src/libs/core/exam/tresulttext.h b/src/libs/core/exam/tresulttext.h index 4c7b6ee0dfdb57d4cbdb4ad26cdce8baf9869a71..980ef42841c32c4e9e78d124135584910055679e 100644 --- a/src/libs/core/exam/tresulttext.h +++ b/src/libs/core/exam/tresulttext.h @@ -16,22 +16,21 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TRESULTTEXT_H #define TRESULTTEXT_H -#include <nootkacoreglobal.h> #include <QtCore/qstring.h> +#include <nootkacoreglobal.h> class TQAunit; class QColor; - /** - * Returns HTML formatted text with answer details. - * If @p fontSize remains default - default @p fontSize is taken. - * When @p attempt is bigger than 0 (and answer was a melody of course) - * The summary of that attempt is prepared. - */ -NOOTKACORE_EXPORT QString wasAnswerOKtext(TQAunit* answer, int attempt = 0); +/** + * Returns HTML formatted text with answer details. + * If @p fontSize remains default - default @p fontSize is taken. + * When @p attempt is bigger than 0 (and answer was a melody of course) + * The summary of that attempt is prepared. + */ +NOOTKACORE_EXPORT QString wasAnswerOKtext(TQAunit *answer, int attempt = 0); #endif // TRESULTTEXT_H diff --git a/src/libs/core/instruments/tbandoneonbg.cpp b/src/libs/core/instruments/tbandoneonbg.cpp index 1bef3afd1fb76ffb2497895334fee979fdbb5ae6..975d98083ee38669b5125e419309b72ea55175f2 100644 --- a/src/libs/core/instruments/tbandoneonbg.cpp +++ b/src/libs/core/instruments/tbandoneonbg.cpp @@ -20,99 +20,96 @@ #include "music/ttechnical.h" #include "tglobals.h" -#include <QtQml/qqmlengine.h> #include <QtCore/qtimer.h> +#include <QtQml/qqmlengine.h> #include <QtCore/qdebug.h> // #include "checktime.h" - struct BandoButt { - qreal x; - qreal y; - qint8 open; - qint8 close; + qreal x; + qreal y; + qint8 open; + qint8 close; }; - static BandoButt buttArray[71] = { -// left hand buttons - 33 pieces -// X , Y ,Open,Close - { 21.2244, 17.8522, -7, -9 }, // E-1 D-1 - { 8.77049, 63.5745, -9, -7 }, // D-1 E-1 - { 19.7463, 45.3289, 5, -2 }, // E0 A-1 E0 (5) x 3 - { 28.0139, 58.4429, 0, 5 }, // H-1 E0 - { 52.674, 51.8859, 20, 19 }, // G1 F#1 - { 43.9788, 38.9145, 9, 5 }, // G#0 E0 - { 35.5687, 25.3728, 3, -4 }, // D0 G-1 - { 45.5468, 13.2566, -2, 3 }, // A-1 D0 - { 57.2354, 2.56579, -3, -3 }, // G#-1 G#-1 - { 60.799, 20.5263, 10, 8 }, // A0 G0 - { 69.4941, 35.3509, 12, 10 }, // H0 A0 # 10 - { 77.9042, 49.1776, 22, 21 }, // A1 G#1 - { 73.0577, 8.69517, 8, 11 }, // G0 A#0 - { 86.4568, 19.5285, 13, 12 }, // C1 H0 - { 86.4568, 0, -1, -1 }, // A#-1 A#-1 - { 95.7222, 33.7829, 15, 14 }, // D1 C#1 - { 103.99, 49.1776, 16, 24 }, // D#1 H1 - { 99.5709, 9.26534, 4, 13 }, // D#0 C1 - { 113.968, 1.14036, 2, 4 }, // C#0 D#0 - { 111.83, 20.6688, 17, 15 }, // E1 D1 - { 119.67, 36.4912, 19, 17 }, // F#1 E1 # 20 - { 126.084, 11.6886, 18, 2 }, // F1 C#0 - { 128.65, 51.0307, 7, 6 }, // F#0 F0 - { 136.917, 24.0899, 1, 18 }, // C0 F1 - { 142.477, 5.13158, 6, 16 }, // F0 D#1 - { 144.33, 38.9145, 14, 9 }, // C#1 G#0 - { 153.452, 55.0219, -8, -10 }, // D#-1 C#-1 - { 152.74, 17.5329, 11, 1 }, // A#0 C0 - { 161.862, 31.2171, -4, 7 }, // G-1 F#0 - { 170.558, 46.7543, -5, 0 }, // F#-1 H-1 - { 179.11, 61.7215, -11, -6 }, // C-1 F-1 # 30 - { 178.825, 25.5153, -6, -5 }, // F-1 F#-1 - { 169.275, 11.9737, 21, 20 }, // G#1 G1 -// right hand buttons - 38 pieces - { 26.0417, 33.4609, 12, 12 }, // H0 H0 - { 18.9575, 70.2402, 11, 11 }, // A#0 A#0 - { 12.5596, 53.4871, 10, 10 }, // A0 A0 - { 39.8729, 63.0944, 16, 16 }, // D#1 D#1 - { 31.8882, 48.2275, 18, 18 }, // F1 F1 - { 34.7614, 19.897, 13, 15 }, // C1 D1 - { 46.5546, 29.8194, 17, 19 }, // E1 F#1 - { 44.2828, 8.27076, 14, 13 }, // C#1 C1 # 40 - { 55.1407, -3.22183, 48, 46 }, // H3 A3 A3 (46) x 3 - { 55.9759, 16.8902, 15, 14 }, // D1 C#1 - { 53.9714, 45.1873, 23, 17 }, // A#1 E1 - { 59.7177, 59.6533, 30, 30 }, // F2 F2 - { 66.8003, 27.4474, 26, 31 }, // C#2 F#2 - { 67.6355, 5.96555, 46, 45 }, // A3 G#3 - { 78.4934, 15.7543, 20, 21 }, // G1 G#1 - { 79.5958, -4.65841, 46, 45 }, // A3 G#3 - { 73.5155, 43.2831, 21, 22 }, // G#1 A1 - { 80.6649, 57.4818, 28, 29 }, // D#2 E2 # 50 E2 (29) x 3 - { 88.3489, 27.748, 19, 20 }, // F#1 G1 - { 94.4627, 43.116, 24, 26 }, // H1 C#2 - { 101.311, 58.0497, 31, 33 }, // F#2 G#2 - { 109.029, 29.1178, 22, 24 }, // A1 H1 - { 100.61, 16.8902, 35, 23 }, // A#2 A#1 - { 90.2198, 4.89646, 43, 35 }, // F#3 A#2 - { 103.75, -3.9234, 44, 43 }, // G3 F#3 - { 113.272, 6.60032, 41, 37 }, // E3 C3 - { 121.557, 20.1642, 37, 25 }, // C3 C2 - { 115.41, 44.2519, 27, 29 }, // D2 E2 # 60 - { 121.958, 59.9874, 34, 36 }, // A2 H2 - { 136.391, 48.1273, 33, 34 }, // G#2 A2 - { 128.84, 33.8284, 25, 27 }, // C2 D2 - { 142.237, 24.2735, 39, 39 }, // D3 D3 - { 134.82, 11.7787, 40, 40 }, // D#3 D#3 - { 126.702, -0.51574, 42, 42 }, // F3 F3 - { 149.487, 38.9733, 29, 32 }, // E2 G2 - { 156.169, 53.5395, 36, 38 }, // H2 C#3 - { 143.206, 62.5933, 38, 41 }, // C#3 E3 - { 161.681, 70.578, 32, 28 } // G2 D#2 # 70 + // left hand buttons - 33 pieces + // X , Y ,Open,Close + {21.2244, 17.8522, -7, -9}, // E-1 D-1 + {8.77049, 63.5745, -9, -7}, // D-1 E-1 + {19.7463, 45.3289, 5, -2}, // E0 A-1 E0 (5) x 3 + {28.0139, 58.4429, 0, 5}, // H-1 E0 + {52.674, 51.8859, 20, 19}, // G1 F#1 + {43.9788, 38.9145, 9, 5}, // G#0 E0 + {35.5687, 25.3728, 3, -4}, // D0 G-1 + {45.5468, 13.2566, -2, 3}, // A-1 D0 + {57.2354, 2.56579, -3, -3}, // G#-1 G#-1 + {60.799, 20.5263, 10, 8}, // A0 G0 + {69.4941, 35.3509, 12, 10}, // H0 A0 # 10 + {77.9042, 49.1776, 22, 21}, // A1 G#1 + {73.0577, 8.69517, 8, 11}, // G0 A#0 + {86.4568, 19.5285, 13, 12}, // C1 H0 + {86.4568, 0, -1, -1}, // A#-1 A#-1 + {95.7222, 33.7829, 15, 14}, // D1 C#1 + {103.99, 49.1776, 16, 24}, // D#1 H1 + {99.5709, 9.26534, 4, 13}, // D#0 C1 + {113.968, 1.14036, 2, 4}, // C#0 D#0 + {111.83, 20.6688, 17, 15}, // E1 D1 + {119.67, 36.4912, 19, 17}, // F#1 E1 # 20 + {126.084, 11.6886, 18, 2}, // F1 C#0 + {128.65, 51.0307, 7, 6}, // F#0 F0 + {136.917, 24.0899, 1, 18}, // C0 F1 + {142.477, 5.13158, 6, 16}, // F0 D#1 + {144.33, 38.9145, 14, 9}, // C#1 G#0 + {153.452, 55.0219, -8, -10}, // D#-1 C#-1 + {152.74, 17.5329, 11, 1}, // A#0 C0 + {161.862, 31.2171, -4, 7}, // G-1 F#0 + {170.558, 46.7543, -5, 0}, // F#-1 H-1 + {179.11, 61.7215, -11, -6}, // C-1 F-1 # 30 + {178.825, 25.5153, -6, -5}, // F-1 F#-1 + {169.275, 11.9737, 21, 20}, // G#1 G1 + // right hand buttons - 38 pieces + {26.0417, 33.4609, 12, 12}, // H0 H0 + {18.9575, 70.2402, 11, 11}, // A#0 A#0 + {12.5596, 53.4871, 10, 10}, // A0 A0 + {39.8729, 63.0944, 16, 16}, // D#1 D#1 + {31.8882, 48.2275, 18, 18}, // F1 F1 + {34.7614, 19.897, 13, 15}, // C1 D1 + {46.5546, 29.8194, 17, 19}, // E1 F#1 + {44.2828, 8.27076, 14, 13}, // C#1 C1 # 40 + {55.1407, -3.22183, 48, 46}, // H3 A3 A3 (46) x 3 + {55.9759, 16.8902, 15, 14}, // D1 C#1 + {53.9714, 45.1873, 23, 17}, // A#1 E1 + {59.7177, 59.6533, 30, 30}, // F2 F2 + {66.8003, 27.4474, 26, 31}, // C#2 F#2 + {67.6355, 5.96555, 46, 45}, // A3 G#3 + {78.4934, 15.7543, 20, 21}, // G1 G#1 + {79.5958, -4.65841, 46, 45}, // A3 G#3 + {73.5155, 43.2831, 21, 22}, // G#1 A1 + {80.6649, 57.4818, 28, 29}, // D#2 E2 # 50 E2 (29) x 3 + {88.3489, 27.748, 19, 20}, // F#1 G1 + {94.4627, 43.116, 24, 26}, // H1 C#2 + {101.311, 58.0497, 31, 33}, // F#2 G#2 + {109.029, 29.1178, 22, 24}, // A1 H1 + {100.61, 16.8902, 35, 23}, // A#2 A#1 + {90.2198, 4.89646, 43, 35}, // F#3 A#2 + {103.75, -3.9234, 44, 43}, // G3 F#3 + {113.272, 6.60032, 41, 37}, // E3 C3 + {121.557, 20.1642, 37, 25}, // C3 C2 + {115.41, 44.2519, 27, 29}, // D2 E2 # 60 + {121.958, 59.9874, 34, 36}, // A2 H2 + {136.391, 48.1273, 33, 34}, // G#2 A2 + {128.84, 33.8284, 25, 27}, // C2 D2 + {142.237, 24.2735, 39, 39}, // D3 D3 + {134.82, 11.7787, 40, 40}, // D#3 D#3 + {126.702, -0.51574, 42, 42}, // F3 F3 + {149.487, 38.9733, 29, 32}, // E2 G2 + {156.169, 53.5395, 36, 38}, // H2 C#3 + {143.206, 62.5933, 38, 41}, // C#3 E3 + {161.681, 70.578, 32, 28} // G2 D#2 # 70 }; - #define BIG_SCALE (1.2) #define SMALL_SCALE (0.8) @@ -128,411 +125,424 @@ static BandoButt buttArray[71] = { #define A3_BUTT_ID (47) #define A3_NOTE_ID (57) - -TbandoneonBg::TbandoneonBg(QQuickItem* parent) : - TcommonInstrument(parent), - m_currentIndex(-1) +TbandoneonBg::TbandoneonBg(QQuickItem *parent) + : TcommonInstrument(parent) + , m_currentIndex(-1) { - // HACK Default highlight color is quite the same as bellows closing color - // so change it here - it affects bandoneon only - if (QColor(255, 0, 127, 150) == GLOB->fingerColor()) - GLOB->setFingerColor(QColor(0, 128, 128, 250)); // teal without slight alpha - - for (int i = 0; i < 71; ++i) { - if (i < 33) { - m_notesArray[buttArray[i].open + 11].leftOpen = i + 1; - m_notesArray[buttArray[i].close + 11].leftClose = i + 1; - } else { - m_notesArray[buttArray[i].open + 11].rightOpen = i + 1; - m_notesArray[buttArray[i].close + 11].rightClose = i + 1; + // HACK Default highlight color is quite the same as bellows closing color + // so change it here - it affects bandoneon only + if (QColor(255, 0, 127, 150) == GLOB->fingerColor()) + GLOB->setFingerColor(QColor(0, 128, 128, 250)); // teal without slight alpha + + for (int i = 0; i < 71; ++i) { + if (i < 33) { + m_notesArray[buttArray[i].open + 11].leftOpen = i + 1; + m_notesArray[buttArray[i].close + 11].leftClose = i + 1; + } else { + m_notesArray[buttArray[i].open + 11].rightOpen = i + 1; + m_notesArray[buttArray[i].close + 11].rightClose = i + 1; + } } - } - - QQmlEngine engine; - QQmlComponent comp(&engine, this); - - comp.setData("import QtQuick 2.9; Rectangle { color: \"blue\"; scale: 1.2 }", QUrl()); - m_circleLeftOpen.item = createCircle(&comp); - m_circleRightOpen.item = createCircle(&comp); - comp.setData("import QtQuick 2.9; Rectangle { color: \"#FF00FF\"; scale: 1.2 }", QUrl()); - m_circleLeftClose.item = createCircle(&comp); - m_circleRightClose.item = createCircle(&comp); - m_circleCloseExtra.item = createCircle(&comp); -} + QQmlEngine engine; + QQmlComponent comp(&engine, this); + + comp.setData("import QtQuick 2.9; Rectangle { color: \"blue\"; scale: 1.2 }", QUrl()); + m_circleLeftOpen.item = createCircle(&comp); + m_circleRightOpen.item = createCircle(&comp); + comp.setData("import QtQuick 2.9; Rectangle { color: \"#FF00FF\"; scale: 1.2 }", QUrl()); + m_circleLeftClose.item = createCircle(&comp); + m_circleRightClose.item = createCircle(&comp); + m_circleCloseExtra.item = createCircle(&comp); +} TbandoneonBg::~TbandoneonBg() { } - -qreal TbandoneonBg::xAt(int b) { return buttArray[b].x; } - -qreal TbandoneonBg::yAt(int b) { return buttArray[b].y; } - -int TbandoneonBg::openAt(int b) { return buttArray[b].open; } - -int TbandoneonBg::closeAt(int b) { return buttArray[b].close; } - - -void TbandoneonBg::setCurrentIndex(int i) { -// CHECKTIME ( - m_currentIndex = i; - if (m_currentIndex > -1) { - Tnote n(m_closing ? buttArray[m_currentIndex].close : buttArray[m_currentIndex].open); - n.setOnUpperStaff(m_currentIndex > 32); - setNote(n, technical()); - emit noteChanged(); - } -// ) +qreal TbandoneonBg::xAt(int b) +{ + return buttArray[b].x; } +qreal TbandoneonBg::yAt(int b) +{ + return buttArray[b].y; +} -void TbandoneonBg::setOpening(bool o) { - if (o != m_opening) { - m_opening = o; - emit openingChanged(); - if (p_note.isValid()) { - if (m_circleLeftClose.buttonId) - m_circleLeftClose.item->setVisible(!m_opening); - if (m_circleRightClose.buttonId) - m_circleRightClose.item->setVisible(!m_opening); +int TbandoneonBg::openAt(int b) +{ + return buttArray[b].open; +} - fixScaleOfTheSame(); +int TbandoneonBg::closeAt(int b) +{ + return buttArray[b].close; +} - if (m_circleCloseExtra.buttonId == 4 || m_circleCloseExtra.buttonId == 51) - m_circleRightClose.item->setVisible(!m_opening); +void TbandoneonBg::setCurrentIndex(int i) +{ + // CHECKTIME ( + m_currentIndex = i; + if (m_currentIndex > -1) { + Tnote n(m_closing ? buttArray[m_currentIndex].close : buttArray[m_currentIndex].open); + n.setOnUpperStaff(m_currentIndex > 32); + setNote(n, technical()); + emit noteChanged(); } - } + // ) } - -void TbandoneonBg::setClosing(bool c) { - if (c != m_closing) { - m_closing = c; - emit closingChanged(); - if (m_circleLeftOpen.buttonId) - m_circleLeftOpen.item->setVisible(!m_closing); - if (m_circleRightOpen.buttonId) - m_circleRightOpen.item->setVisible(!m_closing); - - fixScaleOfTheSame(); - - if (m_circleCloseExtra.buttonId == 47) - m_circleCloseExtra.item->setVisible(!m_closing); - } +void TbandoneonBg::setOpening(bool o) +{ + if (o != m_opening) { + m_opening = o; + emit openingChanged(); + if (p_note.isValid()) { + if (m_circleLeftClose.buttonId) + m_circleLeftClose.item->setVisible(!m_opening); + if (m_circleRightClose.buttonId) + m_circleRightClose.item->setVisible(!m_opening); + + fixScaleOfTheSame(); + + if (m_circleCloseExtra.buttonId == 4 || m_circleCloseExtra.buttonId == 51) + m_circleRightClose.item->setVisible(!m_opening); + } + } } +void TbandoneonBg::setClosing(bool c) +{ + if (c != m_closing) { + m_closing = c; + emit closingChanged(); + if (m_circleLeftOpen.buttonId) + m_circleLeftOpen.item->setVisible(!m_closing); + if (m_circleRightOpen.buttonId) + m_circleRightOpen.item->setVisible(!m_closing); + + fixScaleOfTheSame(); + + if (m_circleCloseExtra.buttonId == 47) + m_circleCloseExtra.item->setVisible(!m_closing); + } +} /** * @p Tnote::onUpperStaff() determines on which pane button has to be selected * onUpperStaff() == TRUE - right pane, FALSE - left pane */ -void TbandoneonBg::setNote(const Tnote& n, quint32 noteDataValue) { - if (!n.isValid() && m_sideHighlight != HighlightNone) { - m_sideHighlight = HighlightNone; - emit sideHighlightChanged(); - } - if (!n.isValid() && !p_note.isValid()) - return; - if (!n.isValid()) { - hideCircles(); - setOpening(false); - setClosing(false); - m_currentIndex = -1; - return; - } - Ttechnical techn(noteDataValue); - setOpening(techn.bowing() == Ttechnical::BowDown); - setClosing(techn.bowing() == Ttechnical::BowUp); - int chromaticNew = n.chromatic(); - if (chromaticNew < -11 || chromaticNew > 48) { - setOutOfScale(true); - p_note.setNote(0); - hideCircles(); - emit outOfScaleChanged(); - return; - } - setOutOfScale(false); - int chromaticOld = p_note.isValid() ? p_note.chromatic() : 1000; - if (chromaticNew != chromaticOld || n.onUpperStaff() != p_note.onUpperStaff()) { - if (chromaticNew == chromaticOld && n.onUpperStaff() != p_note.onUpperStaff()) - hideCircles(); // reset marked buttons when note is the same but moved to upper/lower staff - // otherwise @p checkCircle condition doesn't occur and buttons remains on unappropriated side - p_note = n; - chromaticNew += 11; - - if (m_notesArray[chromaticNew].leftOpen != m_circleLeftOpen.buttonId) - checkCircle(m_notesArray[chromaticNew].leftOpen, m_circleLeftOpen, !m_closing && !n.onUpperStaff()); - if (m_notesArray[chromaticNew].leftClose != m_circleLeftClose.buttonId) - checkCircle(m_notesArray[chromaticNew].leftClose, m_circleLeftClose, !m_opening && !n.onUpperStaff()); - qreal scale = m_notesArray[chromaticNew].leftOpen && !m_opening && !m_closing - && m_notesArray[chromaticNew].leftOpen == m_notesArray[chromaticNew].leftClose ? SMALL_SCALE : BIG_SCALE; - m_circleLeftClose.item->setProperty("scale", scale); - if (m_notesArray[chromaticNew].rightOpen != m_circleRightOpen.buttonId) - checkCircle(m_notesArray[chromaticNew].rightOpen, m_circleRightOpen, !m_closing && n.onUpperStaff()); - if (m_notesArray[chromaticNew].rightClose != m_circleRightClose.buttonId) - checkCircle(m_notesArray[chromaticNew].rightClose, m_circleRightClose, !m_opening && n.onUpperStaff()); - scale = m_notesArray[chromaticNew].rightOpen && !m_opening && !m_closing - && m_notesArray[chromaticNew].rightOpen == m_notesArray[chromaticNew].rightClose ? SMALL_SCALE : BIG_SCALE; - m_circleRightClose.item->setProperty("scale", scale); - if (chromaticNew == E0_NOTE_ID || chromaticNew == E2_NOTE_ID) { // occur when closing - if (chromaticNew == E0_NOTE_ID) - m_circleCloseExtra.buttonId = E0_BUTT_ID; - else - m_circleCloseExtra.buttonId = E2_BUTT_ID; - if (chromaticNew == E0_NOTE_ID) - checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_opening && !n.onUpperStaff()); - else - checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_opening && n.onUpperStaff()); - m_circleCloseExtra.item->setProperty("color", QColor(255, 0, 255)); // #FF00FF - } else if (chromaticNew == A3_NOTE_ID) { // occur when opening - m_circleCloseExtra.buttonId = A3_BUTT_ID; - checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_closing && n.onUpperStaff()); - m_circleCloseExtra.item->setProperty("color", QColor(0, 0, 255)); // blue - } else - m_circleCloseExtra.item->setVisible(false); - } +void TbandoneonBg::setNote(const Tnote &n, quint32 noteDataValue) +{ + if (!n.isValid() && m_sideHighlight != HighlightNone) { + m_sideHighlight = HighlightNone; + emit sideHighlightChanged(); + } + if (!n.isValid() && !p_note.isValid()) + return; + if (!n.isValid()) { + hideCircles(); + setOpening(false); + setClosing(false); + m_currentIndex = -1; + return; + } + Ttechnical techn(noteDataValue); + setOpening(techn.bowing() == Ttechnical::BowDown); + setClosing(techn.bowing() == Ttechnical::BowUp); + int chromaticNew = n.chromatic(); + if (chromaticNew < -11 || chromaticNew > 48) { + setOutOfScale(true); + p_note.setNote(0); + hideCircles(); + emit outOfScaleChanged(); + return; + } + setOutOfScale(false); + int chromaticOld = p_note.isValid() ? p_note.chromatic() : 1000; + if (chromaticNew != chromaticOld || n.onUpperStaff() != p_note.onUpperStaff()) { + if (chromaticNew == chromaticOld && n.onUpperStaff() != p_note.onUpperStaff()) + hideCircles(); // reset marked buttons when note is the same but moved to upper/lower staff + // otherwise @p checkCircle condition doesn't occur and buttons remains on unappropriated side + p_note = n; + chromaticNew += 11; + + if (m_notesArray[chromaticNew].leftOpen != m_circleLeftOpen.buttonId) + checkCircle(m_notesArray[chromaticNew].leftOpen, m_circleLeftOpen, !m_closing && !n.onUpperStaff()); + if (m_notesArray[chromaticNew].leftClose != m_circleLeftClose.buttonId) + checkCircle(m_notesArray[chromaticNew].leftClose, m_circleLeftClose, !m_opening && !n.onUpperStaff()); + qreal scale = + m_notesArray[chromaticNew].leftOpen && !m_opening && !m_closing && m_notesArray[chromaticNew].leftOpen == m_notesArray[chromaticNew].leftClose + ? SMALL_SCALE + : BIG_SCALE; + m_circleLeftClose.item->setProperty("scale", scale); + if (m_notesArray[chromaticNew].rightOpen != m_circleRightOpen.buttonId) + checkCircle(m_notesArray[chromaticNew].rightOpen, m_circleRightOpen, !m_closing && n.onUpperStaff()); + if (m_notesArray[chromaticNew].rightClose != m_circleRightClose.buttonId) + checkCircle(m_notesArray[chromaticNew].rightClose, m_circleRightClose, !m_opening && n.onUpperStaff()); + scale = + m_notesArray[chromaticNew].rightOpen && !m_opening && !m_closing && m_notesArray[chromaticNew].rightOpen == m_notesArray[chromaticNew].rightClose + ? SMALL_SCALE + : BIG_SCALE; + m_circleRightClose.item->setProperty("scale", scale); + if (chromaticNew == E0_NOTE_ID || chromaticNew == E2_NOTE_ID) { // occur when closing + if (chromaticNew == E0_NOTE_ID) + m_circleCloseExtra.buttonId = E0_BUTT_ID; + else + m_circleCloseExtra.buttonId = E2_BUTT_ID; + if (chromaticNew == E0_NOTE_ID) + checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_opening && !n.onUpperStaff()); + else + checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_opening && n.onUpperStaff()); + m_circleCloseExtra.item->setProperty("color", QColor(255, 0, 255)); // #FF00FF + } else if (chromaticNew == A3_NOTE_ID) { // occur when opening + m_circleCloseExtra.buttonId = A3_BUTT_ID; + checkCircle(m_circleCloseExtra.buttonId, m_circleCloseExtra, !m_closing && n.onUpperStaff()); + m_circleCloseExtra.item->setProperty("color", QColor(0, 0, 255)); // blue + } else + m_circleCloseExtra.item->setVisible(false); + } } - -void TbandoneonBg::askQuestion(const Tnote& n, quint32 noteDataValue) { - setNote(n, noteDataValue); +void TbandoneonBg::askQuestion(const Tnote &n, quint32 noteDataValue) +{ + setNote(n, noteDataValue); } - -void TbandoneonBg::highlightAnswer(const Tnote& n, quint32 noteData) { - Ttechnical techn(noteData); - setOpening(techn.bowing() == Ttechnical::BowDown); - setClosing(techn.bowing() == Ttechnical::BowUp); - if (n.isValid()) { - m_sideHighlight = n.onUpperStaff() ? HighlightRight : HighlightLeft; - emit sideHighlightChanged(); - } +void TbandoneonBg::highlightAnswer(const Tnote &n, quint32 noteData) +{ + Ttechnical techn(noteData); + setOpening(techn.bowing() == Ttechnical::BowDown); + setClosing(techn.bowing() == Ttechnical::BowUp); + if (n.isValid()) { + m_sideHighlight = n.onUpperStaff() ? HighlightRight : HighlightLeft; + emit sideHighlightChanged(); + } } - - -int TbandoneonBg::technical() { - Ttechnical nd; - nd.setBowing(m_opening ? Ttechnical::BowDown : (m_closing ? Ttechnical::BowUp : Ttechnical::BowUndefined)); - return nd.data(); +int TbandoneonBg::technical() +{ + Ttechnical nd; + nd.setBowing(m_opening ? Ttechnical::BowDown : (m_closing ? Ttechnical::BowUp : Ttechnical::BowUndefined)); + return nd.data(); } - -void TbandoneonBg::setRightX(qreal rx) { - if (m_rightX != rx) { - m_rightX = rx; - updateCircesPos(); - emit rightXChanged(); - } +void TbandoneonBg::setRightX(qreal rx) +{ + if (m_rightX != rx) { + m_rightX = rx; + updateCircesPos(); + emit rightXChanged(); + } } - -void TbandoneonBg::setFactor(qreal f) { - if (f != m_factor) { - m_factor = f; - updateCircleSize(m_circleLeftOpen.item); - updateCircleSize(m_circleLeftClose.item); - updateCircleSize(m_circleRightOpen.item); - updateCircleSize(m_circleRightClose.item); - updateCircleSize(m_circleCloseExtra.item); - emit factorChanged(); - } +void TbandoneonBg::setFactor(qreal f) +{ + if (f != m_factor) { + m_factor = f; + updateCircleSize(m_circleLeftOpen.item); + updateCircleSize(m_circleLeftClose.item); + updateCircleSize(m_circleRightOpen.item); + updateCircleSize(m_circleRightClose.item); + updateCircleSize(m_circleCloseExtra.item); + emit factorChanged(); + } } - -void TbandoneonBg::setXOffset(qreal off) { - if (off != m_xOffset) { - m_xOffset = off; - updateCircesPos(); - emit xOffsetChanged(); - } +void TbandoneonBg::setXOffset(qreal off) +{ + if (off != m_xOffset) { + m_xOffset = off; + updateCircesPos(); + emit xOffsetChanged(); + } } - -void TbandoneonBg::markSelected(const QColor& markColor) { - int borderWidth = markColor.alpha() ? qRound(height() / 50.0) : 0; - markBorder(m_circleLeftOpen.item, borderWidth, markColor); - markBorder(m_circleRightOpen.item, borderWidth, markColor); - markBorder(m_circleLeftClose.item, borderWidth, markColor); - markBorder(m_circleRightClose.item, borderWidth, markColor); - markBorder(m_circleCloseExtra.item, borderWidth, markColor); +void TbandoneonBg::markSelected(const QColor &markColor) +{ + int borderWidth = markColor.alpha() ? qRound(height() / 50.0) : 0; + markBorder(m_circleLeftOpen.item, borderWidth, markColor); + markBorder(m_circleRightOpen.item, borderWidth, markColor); + markBorder(m_circleLeftClose.item, borderWidth, markColor); + markBorder(m_circleRightClose.item, borderWidth, markColor); + markBorder(m_circleCloseExtra.item, borderWidth, markColor); } +void TbandoneonBg::correct(const Tnote &n, quint32 noteData) +{ + if (m_circleLeftOpen.item->isVisible()) + p_wrongItem = m_circleLeftOpen.item; + else if (m_circleLeftClose.item->isVisible()) + p_wrongItem = m_circleLeftClose.item; + else if (m_circleRightOpen.item->isVisible()) + p_wrongItem = m_circleRightOpen.item; + else if (m_circleRightClose.item->isVisible()) + p_wrongItem = m_circleRightClose.item; + m_goodNote = n; + m_goodTechn = noteData; + + int goodChromatic = m_goodNote.chromatic() + 11; + Ttechnical goodTechn(m_goodTechn); + if (m_notesArray[goodChromatic].leftOpen && goodTechn.bowing() == Ttechnical::BowDown && !n.onUpperStaff()) { + p_goodItem = m_circleLeftOpen.item; + m_goodButton = m_notesArray[goodChromatic].leftOpen; + } else if (m_notesArray[goodChromatic].leftClose && goodTechn.bowing() == Ttechnical::BowUp && !n.onUpperStaff()) { + p_goodItem = m_circleLeftClose.item; + m_goodButton = m_notesArray[goodChromatic].leftClose; + } else if (m_notesArray[goodChromatic].rightOpen && goodTechn.bowing() == Ttechnical::BowDown && n.onUpperStaff()) { + p_goodItem = m_circleRightOpen.item; + m_goodButton = m_notesArray[goodChromatic].rightOpen; + } else if (m_notesArray[goodChromatic].rightClose && goodTechn.bowing() == Ttechnical::BowUp && n.onUpperStaff()) { + p_goodItem = m_circleRightClose.item; + m_goodButton = m_notesArray[goodChromatic].rightClose; + } else { + p_goodItem = nullptr; + } -void TbandoneonBg::correct(const Tnote& n, quint32 noteData) { - if (m_circleLeftOpen.item->isVisible()) - p_wrongItem = m_circleLeftOpen.item; - else if (m_circleLeftClose.item->isVisible()) - p_wrongItem = m_circleLeftClose.item; - else if (m_circleRightOpen.item->isVisible()) - p_wrongItem = m_circleRightOpen.item; - else if (m_circleRightClose.item->isVisible()) - p_wrongItem = m_circleRightClose.item; - m_goodNote = n; - m_goodTechn = noteData; - - int goodChromatic = m_goodNote.chromatic() + 11; - Ttechnical goodTechn(m_goodTechn); - if (m_notesArray[goodChromatic].leftOpen && goodTechn.bowing() == Ttechnical::BowDown && !n.onUpperStaff()) { - p_goodItem = m_circleLeftOpen.item; - m_goodButton = m_notesArray[goodChromatic].leftOpen; - } else if (m_notesArray[goodChromatic].leftClose && goodTechn.bowing() == Ttechnical::BowUp && !n.onUpperStaff()) { - p_goodItem = m_circleLeftClose.item; - m_goodButton = m_notesArray[goodChromatic].leftClose; - } else if (m_notesArray[goodChromatic].rightOpen && goodTechn.bowing() == Ttechnical::BowDown && n.onUpperStaff()) { - p_goodItem = m_circleRightOpen.item; - m_goodButton = m_notesArray[goodChromatic].rightOpen; - } else if (m_notesArray[goodChromatic].rightClose && goodTechn.bowing() == Ttechnical::BowUp && n.onUpperStaff()) { - p_goodItem = m_circleRightClose.item; - m_goodButton = m_notesArray[goodChromatic].rightClose; - } else { - p_goodItem = nullptr; - } - - emit correctInstrument(); + emit correctInstrument(); } +void TbandoneonBg::applyCorrect() +{ + if (p_goodItem && m_goodNote.isValid()) { + Ttechnical goodTechn(m_goodTechn); + p_note.setNote(0); // invalidate to stop setting button visible by following methods + setOpening(goodTechn.bowing() == Ttechnical::BowDown); + setClosing(goodTechn.bowing() == Ttechnical::BowUp); + hideCircles(); + if (p_goodItem == m_circleLeftClose.item) + checkCircle(m_goodButton, m_circleLeftClose); + else if (p_goodItem == m_circleLeftOpen.item) + checkCircle(m_goodButton, m_circleLeftOpen); + else if (p_goodItem == m_circleRightClose.item) + checkCircle(m_goodButton, m_circleRightClose); + else if (p_goodItem == m_circleRightOpen.item) + checkCircle(m_goodButton, m_circleRightOpen); + p_note = m_goodNote; + markSelected(GLOB->correctColor()); + } + if (p_wrongItem) { + p_wrongItem->setVisible(false); + p_wrongItem->setScale(BIG_SCALE); + p_wrongItem->setOpacity(1.0); + } -void TbandoneonBg::applyCorrect() { - if (p_goodItem && m_goodNote.isValid()) { - Ttechnical goodTechn(m_goodTechn); - p_note.setNote(0); // invalidate to stop setting button visible by following methods - setOpening(goodTechn.bowing() == Ttechnical::BowDown); - setClosing(goodTechn.bowing() == Ttechnical::BowUp); - hideCircles(); - if (p_goodItem == m_circleLeftClose.item) - checkCircle(m_goodButton, m_circleLeftClose); - else if (p_goodItem == m_circleLeftOpen.item) - checkCircle(m_goodButton, m_circleLeftOpen); - else if (p_goodItem == m_circleRightClose.item) - checkCircle(m_goodButton, m_circleRightClose); - else if (p_goodItem == m_circleRightOpen.item) - checkCircle(m_goodButton, m_circleRightOpen); - p_note = m_goodNote; - markSelected(GLOB->correctColor()); - } - if (p_wrongItem) { - p_wrongItem->setVisible(false); - p_wrongItem->setScale(BIG_SCALE); - p_wrongItem->setOpacity(1.0); - } - - if (!p_extraName.isEmpty()) { - p_extraName = QStringLiteral(" "); // hide name of wrong pointed note - // HACK: but do not clear extra name text - finish correction routines depend on it - emit wantNoteName(p_extraName, QVariant()); - } + if (!p_extraName.isEmpty()) { + p_extraName = QStringLiteral(" "); // hide name of wrong pointed note + // HACK: but do not clear extra name text - finish correction routines depend on it + emit wantNoteName(p_extraName, QVariant()); + } } - -void TbandoneonBg::showNoteName(Tnote::EnameStyle st, const Tnote& n, quint32 techn, const QColor& textColor) { - TcommonInstrument::showNoteName(st, n, techn, textColor); - QQuickItem* visIt = nullptr; - if (m_circleLeftOpen.item->isVisible()) - visIt = m_circleLeftOpen.item; - else if (m_circleLeftClose.item->isVisible()) - visIt = m_circleLeftClose.item; - else if (m_circleRightOpen.item->isVisible()) - visIt = m_circleRightOpen.item; - else if (m_circleRightClose.item->isVisible()) - visIt = m_circleRightClose.item; - emit wantNoteName(p_extraName, QVariant::fromValue(visIt)); +void TbandoneonBg::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) +{ + TcommonInstrument::showNoteName(st, n, techn, textColor); + QQuickItem *visIt = nullptr; + if (m_circleLeftOpen.item->isVisible()) + visIt = m_circleLeftOpen.item; + else if (m_circleLeftClose.item->isVisible()) + visIt = m_circleLeftClose.item; + else if (m_circleRightOpen.item->isVisible()) + visIt = m_circleRightOpen.item; + else if (m_circleRightClose.item->isVisible()) + visIt = m_circleRightClose.item; + emit wantNoteName(p_extraName, QVariant::fromValue(visIt)); } - -bool TbandoneonBg::canBeLeftOpen(short chromNoteNr) { - int nrInArr = chromNoteNr + 11; - return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].leftOpen; +bool TbandoneonBg::canBeLeftOpen(short chromNoteNr) +{ + int nrInArr = chromNoteNr + 11; + return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].leftOpen; } - -bool TbandoneonBg::canBeLeftClose(short chromNoteNr) { - int nrInArr = chromNoteNr + 11; - return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].leftClose; +bool TbandoneonBg::canBeLeftClose(short chromNoteNr) +{ + int nrInArr = chromNoteNr + 11; + return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].leftClose; } - -bool TbandoneonBg::canBeRightOpen(short chromNoteNr) { - int nrInArr = chromNoteNr + 11; - return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].rightOpen; +bool TbandoneonBg::canBeRightOpen(short chromNoteNr) +{ + int nrInArr = chromNoteNr + 11; + return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].rightOpen; } - -bool TbandoneonBg::canBeRightClose(short chromNoteNr) { - int nrInArr = chromNoteNr + 11; - return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].rightClose; +bool TbandoneonBg::canBeRightClose(short chromNoteNr) +{ + int nrInArr = chromNoteNr + 11; + return nrInArr >= 0 && nrInArr < 60 && m_notesArray[nrInArr].rightClose; } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -void TbandoneonBg::getNote() { - if (m_currentIndex < 0) - return; - p_note.setChromatic(m_closing ? buttArray[m_currentIndex].close : buttArray[m_currentIndex].open); - p_note.setOnUpperStaff(m_currentIndex > 32); +void TbandoneonBg::getNote() +{ + if (m_currentIndex < 0) + return; + p_note.setChromatic(m_closing ? buttArray[m_currentIndex].close : buttArray[m_currentIndex].open); + p_note.setOnUpperStaff(m_currentIndex > 32); } - -QQuickItem* TbandoneonBg::createCircle(QQmlComponent* comp) { - auto circle = qobject_cast<QQuickItem*>(comp->create()); - circle->setParentItem(this); - circle->setVisible(false); - circle->setZ(10); - return circle; +QQuickItem *TbandoneonBg::createCircle(QQmlComponent *comp) +{ + auto circle = qobject_cast<QQuickItem *>(comp->create()); + circle->setParentItem(this); + circle->setVisible(false); + circle->setZ(10); + return circle; } - -void TbandoneonBg::updateCircleSize(QQuickItem* it) { - it->setWidth(height() / 6.5); - it->setHeight(height() / 6.5); - it->setProperty("radius", height() / 13.0); +void TbandoneonBg::updateCircleSize(QQuickItem *it) +{ + it->setWidth(height() / 6.5); + it->setHeight(height() / 6.5); + it->setProperty("radius", height() / 13.0); } - -void TbandoneonBg::checkCircle(int butNr, TbandCircle& c, bool visible) { - c.buttonId = butNr; - if (c.buttonId) { - c.item->setX(m_xOffset + buttArray[c.buttonId - 1].x * m_factor * (butNr > 33 ? 1.3 : 1.1) + (butNr > 33 ? m_rightX - m_factor * 10.0 : 0.0) - m_factor * 0.5); - c.item->setY(buttArray[c.buttonId - 1].y * m_factor * (butNr > 33 ? 1.05 : 1.2) + (m_factor * 14.5) * ((butNr > 33 ? 0.49 : 0.29))); - c.item->setVisible(visible); - } else - c.item->setVisible(false); +void TbandoneonBg::checkCircle(int butNr, TbandCircle &c, bool visible) +{ + c.buttonId = butNr; + if (c.buttonId) { + c.item->setX(m_xOffset + buttArray[c.buttonId - 1].x * m_factor * (butNr > 33 ? 1.3 : 1.1) + (butNr > 33 ? m_rightX - m_factor * 10.0 : 0.0) + - m_factor * 0.5); + c.item->setY(buttArray[c.buttonId - 1].y * m_factor * (butNr > 33 ? 1.05 : 1.2) + (m_factor * 14.5) * ((butNr > 33 ? 0.49 : 0.29))); + c.item->setVisible(visible); + } else + c.item->setVisible(false); } - -void TbandoneonBg::updateCircesPos() { - if (p_note.isValid()) { - int chromaticNr = p_note.chromatic() + 11; - checkCircle(m_notesArray[chromaticNr].leftOpen, m_circleLeftOpen, m_circleLeftOpen.item->isVisible()); - checkCircle(m_notesArray[chromaticNr].leftClose, m_circleLeftClose, m_circleLeftClose.item->isVisible()); - checkCircle(m_notesArray[chromaticNr].rightOpen, m_circleRightOpen, m_circleRightOpen.item->isVisible()); - checkCircle(m_notesArray[chromaticNr].rightClose, m_circleRightClose, m_circleRightClose.item->isVisible()); - } +void TbandoneonBg::updateCircesPos() +{ + if (p_note.isValid()) { + int chromaticNr = p_note.chromatic() + 11; + checkCircle(m_notesArray[chromaticNr].leftOpen, m_circleLeftOpen, m_circleLeftOpen.item->isVisible()); + checkCircle(m_notesArray[chromaticNr].leftClose, m_circleLeftClose, m_circleLeftClose.item->isVisible()); + checkCircle(m_notesArray[chromaticNr].rightOpen, m_circleRightOpen, m_circleRightOpen.item->isVisible()); + checkCircle(m_notesArray[chromaticNr].rightClose, m_circleRightClose, m_circleRightClose.item->isVisible()); + } } - -void TbandoneonBg::hideCircles() { - m_circleLeftOpen.item->setVisible(false); - m_circleLeftOpen.buttonId = 0; - m_circleLeftClose.item->setVisible(false); - m_circleLeftClose.buttonId = 0; - m_circleRightOpen.item->setVisible(false); - m_circleRightOpen.buttonId = 0; - m_circleRightClose.item->setVisible(false); - m_circleRightClose.buttonId = 0; - m_circleCloseExtra.item->setVisible(false); - m_circleCloseExtra.buttonId = 0; +void TbandoneonBg::hideCircles() +{ + m_circleLeftOpen.item->setVisible(false); + m_circleLeftOpen.buttonId = 0; + m_circleLeftClose.item->setVisible(false); + m_circleLeftClose.buttonId = 0; + m_circleRightOpen.item->setVisible(false); + m_circleRightOpen.buttonId = 0; + m_circleRightClose.item->setVisible(false); + m_circleRightClose.buttonId = 0; + m_circleCloseExtra.item->setVisible(false); + m_circleCloseExtra.buttonId = 0; } - -void TbandoneonBg::fixScaleOfTheSame() { - if (m_circleLeftOpen.buttonId == m_circleLeftClose.buttonId) - m_circleLeftClose.item->setProperty("scale", m_closing ? BIG_SCALE : SMALL_SCALE); - if (m_circleRightOpen.buttonId == m_circleRightClose.buttonId) - m_circleRightClose.item->setProperty("scale", m_closing ? BIG_SCALE : SMALL_SCALE); +void TbandoneonBg::fixScaleOfTheSame() +{ + if (m_circleLeftOpen.buttonId == m_circleLeftClose.buttonId) + m_circleLeftClose.item->setProperty("scale", m_closing ? BIG_SCALE : SMALL_SCALE); + if (m_circleRightOpen.buttonId == m_circleRightClose.buttonId) + m_circleRightClose.item->setProperty("scale", m_closing ? BIG_SCALE : SMALL_SCALE); } diff --git a/src/libs/core/instruments/tbandoneonbg.h b/src/libs/core/instruments/tbandoneonbg.h index 2adeddbfa4208b140ddde96b0c06c9dae12033f3..bc14d7eb40836de8abff3b26368d2e60ccba32fd 100644 --- a/src/libs/core/instruments/tbandoneonbg.h +++ b/src/libs/core/instruments/tbandoneonbg.h @@ -19,16 +19,13 @@ #ifndef TBANDONEONBG_H #define TBANDONEONBG_H - #include "tcommoninstrument.h" -#include <QtQuick/qquickitem.h> #include <QtGui/qcolor.h> - +#include <QtQuick/qquickitem.h> class QQmlComponent; - /** * @class TbandoneonBg represents logic of bandoneon. * It has static array @p buttArray with X,Y button coordinates @@ -41,137 +38,136 @@ class QQmlComponent; */ class NOOTKACORE_EXPORT TbandoneonBg : public TcommonInstrument { - Q_OBJECT + Q_OBJECT - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex) - Q_PROPERTY(bool opening READ opening WRITE setOpening NOTIFY openingChanged) - Q_PROPERTY(bool closing READ closing WRITE setClosing NOTIFY closingChanged) - Q_PROPERTY(qreal rightX READ rightX WRITE setRightX NOTIFY rightXChanged) - Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) - Q_PROPERTY(EsideHighlight sideHighlight READ sideHighlight NOTIFY sideHighlightChanged) - Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY xOffsetChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex) + Q_PROPERTY(bool opening READ opening WRITE setOpening NOTIFY openingChanged) + Q_PROPERTY(bool closing READ closing WRITE setClosing NOTIFY closingChanged) + Q_PROPERTY(qreal rightX READ rightX WRITE setRightX NOTIFY rightXChanged) + Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) + Q_PROPERTY(EsideHighlight sideHighlight READ sideHighlight NOTIFY sideHighlightChanged) + Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY xOffsetChanged) public: - TbandoneonBg(QQuickItem* parent = nullptr); - ~TbandoneonBg(); + TbandoneonBg(QQuickItem *parent = nullptr); + ~TbandoneonBg(); - Q_INVOKABLE qreal xAt(int b); - Q_INVOKABLE qreal yAt(int b); - Q_INVOKABLE int openAt(int b); - Q_INVOKABLE int closeAt(int b); + Q_INVOKABLE qreal xAt(int b); + Q_INVOKABLE qreal yAt(int b); + Q_INVOKABLE int openAt(int b); + Q_INVOKABLE int closeAt(int b); - int currentIndex() const { return m_currentIndex; } - void setCurrentIndex(int i); + int currentIndex() const { return m_currentIndex; } + void setCurrentIndex(int i); - bool opening() const { return m_opening; } - void setOpening(bool o); + bool opening() const { return m_opening; } + void setOpening(bool o); - bool closing() const { return m_closing; } - void setClosing(bool c); + bool closing() const { return m_closing; } + void setClosing(bool c); - void setNote(const Tnote& n, quint32 noteDataValue = 255) override; + void setNote(const Tnote &n, quint32 noteDataValue = 255) override; - void askQuestion(const Tnote& n, quint32 noteDataValue) override; + void askQuestion(const Tnote &n, quint32 noteDataValue) override; - void highlightAnswer(const Tnote & n, quint32 noteData) override; + void highlightAnswer(const Tnote &n, quint32 noteData) override; - int technical() override; + int technical() override; - qreal rightX() const { return m_rightX; } - void setRightX(qreal rx); + qreal rightX() const { return m_rightX; } + void setRightX(qreal rx); - qreal factor() const { return m_factor; } - void setFactor(qreal f); + qreal factor() const { return m_factor; } + void setFactor(qreal f); - qreal xOffset() const { return m_xOffset; } - void setXOffset(qreal off); + qreal xOffset() const { return m_xOffset; } + void setXOffset(qreal off); - void paint(QPainter*) override {} + void paint(QPainter *) override { } - void markSelected(const QColor & markColor) override; + void markSelected(const QColor &markColor) override; - void correct(const Tnote & n, quint32 noteData) override; + void correct(const Tnote &n, quint32 noteData) override; - Q_INVOKABLE void applyCorrect() override; + Q_INVOKABLE void applyCorrect() override; - void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor& textColor) override; + void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) override; - /** - * Returns @p TRUE when given note (as chromatic number @p Tnote::chromatic()) - * is possible to play on bandoneon at given side/bellow direction - */ - bool canBeLeftOpen(short int chromNoteNr); - bool canBeLeftClose(short int chromNoteNr); - bool canBeRightOpen(short int chromNoteNr); - bool canBeRightClose(short int chromNoteNr); + /** + * Returns @p TRUE when given note (as chromatic number @p Tnote::chromatic()) + * is possible to play on bandoneon at given side/bellow direction + */ + bool canBeLeftOpen(short int chromNoteNr); + bool canBeLeftClose(short int chromNoteNr); + bool canBeRightOpen(short int chromNoteNr); + bool canBeRightClose(short int chromNoteNr); - /** - * Describes which side of bandoneon has to be highlighted for an answer. - * It depends on note layout (upper staff, lower staff) - */ - enum EsideHighlight { - HighlightNone = 0, HighlightLeft, HighlightRight - }; - Q_ENUM(EsideHighlight) + /** + * Describes which side of bandoneon has to be highlighted for an answer. + * It depends on note layout (upper staff, lower staff) + */ + enum EsideHighlight { HighlightNone = 0, HighlightLeft, HighlightRight }; + Q_ENUM(EsideHighlight) - EsideHighlight sideHighlight() const { return m_sideHighlight; } + EsideHighlight sideHighlight() const { return m_sideHighlight; } signals: - void closingChanged(); - void openingChanged(); - void rightXChanged(); - void factorChanged(); - void sideHighlightChanged(); - void xOffsetChanged(); + void closingChanged(); + void openingChanged(); + void rightXChanged(); + void factorChanged(); + void sideHighlightChanged(); + void xOffsetChanged(); private: - - /** - * @p QQuickItem circle and button number in @p buttArray, or 0 if no button. - */ - class TbandCircle { + /** + * @p QQuickItem circle and button number in @p buttArray, or 0 if no button. + */ + class TbandCircle + { public: - int buttonId = 0; - QQuickItem* item = nullptr; - }; - - void getNote(); - QQuickItem* createCircle(QQmlComponent* comp); - void updateCircleSize(QQuickItem* it); - void updateCircesPos(); - void checkCircle(int butNr, TbandCircle& c, bool visible = true); - void hideCircles(); - - /** - * fix closing button scale when it remains the same - */ - void fixScaleOfTheSame(); + int buttonId = 0; + QQuickItem *item = nullptr; + }; + + void getNote(); + QQuickItem *createCircle(QQmlComponent *comp); + void updateCircleSize(QQuickItem *it); + void updateCircesPos(); + void checkCircle(int butNr, TbandCircle &c, bool visible = true); + void hideCircles(); + + /** + * fix closing button scale when it remains the same + */ + void fixScaleOfTheSame(); private: - /** - * Contains button number in @p buttArray for left/right keys and opening/closing states. - * NOTICE: number is increased by 1, so @p 0 means: no button - */ - class TbandoNote { + /** + * Contains button number in @p buttArray for left/right keys and opening/closing states. + * NOTICE: number is increased by 1, so @p 0 means: no button + */ + class TbandoNote + { public: - quint8 leftOpen = 0; - quint8 leftClose = 0; - quint8 rightOpen = 0; - quint8 rightClose = 0; - }; - - int m_currentIndex; - bool m_closing = false; - bool m_opening = false; - TbandoNote m_notesArray[60]; - TbandCircle m_circleLeftOpen, m_circleLeftClose, m_circleRightOpen, m_circleRightClose, m_circleCloseExtra; - qreal m_factor = 1.0; - qreal m_rightX = 0.0; - qreal m_xOffset = 0.0; - EsideHighlight m_sideHighlight = HighlightNone; - quint32 m_goodTechn; - Tnote m_goodNote; - int m_goodButton; + quint8 leftOpen = 0; + quint8 leftClose = 0; + quint8 rightOpen = 0; + quint8 rightClose = 0; + }; + + int m_currentIndex; + bool m_closing = false; + bool m_opening = false; + TbandoNote m_notesArray[60]; + TbandCircle m_circleLeftOpen, m_circleLeftClose, m_circleRightOpen, m_circleRightClose, m_circleCloseExtra; + qreal m_factor = 1.0; + qreal m_rightX = 0.0; + qreal m_xOffset = 0.0; + EsideHighlight m_sideHighlight = HighlightNone; + quint32 m_goodTechn; + Tnote m_goodNote; + int m_goodButton; }; #endif // TBANDONEONBG_H diff --git a/src/libs/core/instruments/tcommoninstrument.cpp b/src/libs/core/instruments/tcommoninstrument.cpp index fbf580d3fdee6c92413293fdd5fc4d1e65718340..7ce1de6c229524fec156ede4587c2e6772e12a49 100644 --- a/src/libs/core/instruments/tcommoninstrument.cpp +++ b/src/libs/core/instruments/tcommoninstrument.cpp @@ -18,80 +18,79 @@ #include "tcommoninstrument.h" - -TcommonInstrument::TcommonInstrument(QQuickItem* parent) : - QQuickPaintedItem(parent) +TcommonInstrument::TcommonInstrument(QQuickItem *parent) + : QQuickPaintedItem(parent) { - setAcceptHoverEvents(true); - setRenderTarget(QQuickPaintedItem::FramebufferObject); - setAntialiasing(true); - setAcceptedMouseButtons(Qt::LeftButton); - - // Reset instrument zoom when goes disabled by setting scale to 1 - only mobile so far - connect(this, &QQuickItem::enabledChanged, this, [=]{ - if (!isEnabled()) - setScale(1.0); - }); + setAcceptHoverEvents(true); + setRenderTarget(QQuickPaintedItem::FramebufferObject); + setAntialiasing(true); + setAcceptedMouseButtons(Qt::LeftButton); + + // Reset instrument zoom when goes disabled by setting scale to 1 - only mobile so far + connect(this, &QQuickItem::enabledChanged, this, [=] { + if (!isEnabled()) + setScale(1.0); + }); } - -TcommonInstrument::~TcommonInstrument() { - restoreAfterExam(); +TcommonInstrument::~TcommonInstrument() +{ + restoreAfterExam(); } - -void TcommonInstrument::markBorder(QQuickItem* item, int borderWidth, const QColor& borderColor) { - auto border = qvariant_cast<QObject*>(item->property("border")); - if (border) { - border->setProperty("width", borderWidth); - if (borderWidth) - border->setProperty("color", borderColor); - } +void TcommonInstrument::markBorder(QQuickItem *item, int borderWidth, const QColor &borderColor) +{ + auto border = qvariant_cast<QObject *>(item->property("border")); + if (border) { + border->setProperty("width", borderWidth); + if (borderWidth) + border->setProperty("color", borderColor); + } } - -void TcommonInstrument::restoreAfterExam() { - p_goodItem = nullptr; - p_wrongItem = nullptr; +void TcommonInstrument::restoreAfterExam() +{ + p_goodItem = nullptr; + p_wrongItem = nullptr; } - -void TcommonInstrument::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) { - Q_UNUSED(textColor) - Q_UNUSED(techn) - if (n.isValid()) { - auto defStyle = Tnote::defaultStyle; - Tnote::defaultStyle = st; - p_extraName = QString("<font color=\"%1\">%2</font>").arg(textColor.name()).arg(n.styledName()); - Tnote::defaultStyle = defStyle; - } else - p_extraName.clear(); +void TcommonInstrument::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) +{ + Q_UNUSED(textColor) + Q_UNUSED(techn) + if (n.isValid()) { + auto defStyle = Tnote::defaultStyle; + Tnote::defaultStyle = st; + p_extraName = QString("<font color=\"%1\">%2</font>").arg(textColor.name()).arg(n.styledName()); + Tnote::defaultStyle = defStyle; + } else + p_extraName.clear(); } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# -void TcommonInstrument::hoverEnterEvent(QHoverEvent*) { - if (!m_active) { - m_active = true; - setOutOfScale(false); - emit activeChanged(); - } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# +void TcommonInstrument::hoverEnterEvent(QHoverEvent *) +{ + if (!m_active) { + m_active = true; + setOutOfScale(false); + emit activeChanged(); + } } - -void TcommonInstrument::hoverLeaveEvent(QHoverEvent*) { - if (m_active) { - m_active = false; - emit activeChanged(); - } +void TcommonInstrument::hoverLeaveEvent(QHoverEvent *) +{ + if (m_active) { + m_active = false; + emit activeChanged(); + } } - -void TcommonInstrument::setOutOfScale(bool out) { - if (out != m_outOfScale) { - m_outOfScale = out; - emit outOfScaleChanged(); - } +void TcommonInstrument::setOutOfScale(bool out) +{ + if (out != m_outOfScale) { + m_outOfScale = out; + emit outOfScaleChanged(); + } } - diff --git a/src/libs/core/instruments/tcommoninstrument.h b/src/libs/core/instruments/tcommoninstrument.h index 06293f5e149c215d1f2f04db2a47f08e86b327d8..e7c67b9fc2230d65c754758c505da6d5b42f1a63 100644 --- a/src/libs/core/instruments/tcommoninstrument.h +++ b/src/libs/core/instruments/tcommoninstrument.h @@ -19,145 +19,142 @@ #ifndef TCOMMONINSTRUMENT_H #define TCOMMONINSTRUMENT_H - -#include "nootkacoreglobal.h" #include "music/tnote.h" #include "music/ttechnical.h" +#include "nootkacoreglobal.h" #include <QtQuick/qquickpainteditem.h> - /** * Base class for all instruments */ class NOOTKACORE_EXPORT TcommonInstrument : public QQuickPaintedItem { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(bool active READ active NOTIFY activeChanged) - Q_PROPERTY(Tnote note READ note WRITE setNote NOTIFY noteChanged) - Q_PROPERTY(bool outOfScale READ outOfScale NOTIFY outOfScaleChanged) - Q_PROPERTY(QQuickItem* wrongItem READ wrongItem NOTIFY correctInstrument) - Q_PROPERTY(QQuickItem* goodItem READ goodItem NOTIFY correctInstrument) - Q_PROPERTY(QQuickItem* extraName READ extraName WRITE setExtraName NOTIFY extraNameChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(Tnote note READ note WRITE setNote NOTIFY noteChanged) + Q_PROPERTY(bool outOfScale READ outOfScale NOTIFY outOfScaleChanged) + Q_PROPERTY(QQuickItem *wrongItem READ wrongItem NOTIFY correctInstrument) + Q_PROPERTY(QQuickItem *goodItem READ goodItem NOTIFY correctInstrument) + Q_PROPERTY(QQuickItem *extraName READ extraName WRITE setExtraName NOTIFY extraNameChanged) public: - TcommonInstrument(QQuickItem* parent = nullptr); - ~TcommonInstrument() override; - - /** - * @p TRUE when mouse cursor is over - */ - bool active() { return m_active; } - - bool outOfScale() const { return m_outOfScale; } - - QQuickItem* wrongItem() { return p_wrongItem; } - QQuickItem* goodItem() { return p_goodItem; } - - QQuickItem* extraName() { return m_extraNameItem; } - void setExtraName(QQuickItem* en) { m_extraNameItem = en; } - - /** - * Returns text of extra note name displayed on instrument during exercises. - * Or empty string when nothing. - */ - QString extraNoteName() const { return p_extraName; } - - Tnote note() const { return p_note; } - virtual void setNote(const Tnote& n, quint32 noteDataValue = NO_TECHNICALS) = 0; - - /** - * @p noteData is extra information about note needed for some instruments (guitars, bandoneon). - * In case of guitar it is more important than note itself - */ - virtual void askQuestion(const Tnote& n, quint32 noteData) = 0; - - /** - * Highlight appropriate instrument part to point where to put an answer. - * For guitar it will be a string, for bandoneon bellow direction and side - */ - virtual void highlightAnswer(const Tnote& n, quint32 noteData) = 0; - - /** - * Returns additional note data like position on the guitar or left/right hand on bandoneon - */ - virtual int technical() = 0; - - /** - * Sets color of selected item (finger, button, key - depends on instrument type) - * to @p markColor if different than transparent, or to global color of selection - */ - virtual void markSelected(const QColor& markColor) = 0; - - /** - * Extra note name on instrument - * This method in common part which just sets @p p_extraName note name - * in given @p EnameStyle and @p textColor. - * Every subclassing instrument has to override it - * ant emit @p wantNoteName() with informations to precess by QML - */ - virtual void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor& textColor); - - /** - * Correcting answer logic: - * @p correct() method is called by exam executor. - * This method prepares correction and emits @p correctInstrument() signal. - * QML side (instrument implementation) starts animation then on @p p_wrongItem - * and when incorrect item is hidden by QML, it calls @p applyCorrect() - * to prepare correct answer and @p p_goodItem. - * After that QML continues animations and when finished, - * @p finishCorrectAnim() is invoked and @p correctionFinished() signal emitted. - */ - virtual void correct(const Tnote& n, quint32 noteData) = 0; - - virtual void applyCorrect() {} - - Q_INVOKABLE void finishCorrectAnim() { emit correctionFinished(); } - - /** - * Changes border of given @p item but only if the item has border property (QML Rectangle) - */ - void markBorder(QQuickItem* item, int borderWidth, const QColor& borderColor); - - /** - * Reset wrong/good item pointers to null to be sure that - * nothing spoils normal instrument behavior - */ - void restoreAfterExam(); + TcommonInstrument(QQuickItem *parent = nullptr); + ~TcommonInstrument() override; + + /** + * @p TRUE when mouse cursor is over + */ + bool active() { return m_active; } + + bool outOfScale() const { return m_outOfScale; } + + QQuickItem *wrongItem() { return p_wrongItem; } + QQuickItem *goodItem() { return p_goodItem; } + + QQuickItem *extraName() { return m_extraNameItem; } + void setExtraName(QQuickItem *en) { m_extraNameItem = en; } + + /** + * Returns text of extra note name displayed on instrument during exercises. + * Or empty string when nothing. + */ + QString extraNoteName() const { return p_extraName; } + + Tnote note() const { return p_note; } + virtual void setNote(const Tnote &n, quint32 noteDataValue = NO_TECHNICALS) = 0; + + /** + * @p noteData is extra information about note needed for some instruments (guitars, bandoneon). + * In case of guitar it is more important than note itself + */ + virtual void askQuestion(const Tnote &n, quint32 noteData) = 0; + + /** + * Highlight appropriate instrument part to point where to put an answer. + * For guitar it will be a string, for bandoneon bellow direction and side + */ + virtual void highlightAnswer(const Tnote &n, quint32 noteData) = 0; + + /** + * Returns additional note data like position on the guitar or left/right hand on bandoneon + */ + virtual int technical() = 0; + + /** + * Sets color of selected item (finger, button, key - depends on instrument type) + * to @p markColor if different than transparent, or to global color of selection + */ + virtual void markSelected(const QColor &markColor) = 0; + + /** + * Extra note name on instrument + * This method in common part which just sets @p p_extraName note name + * in given @p EnameStyle and @p textColor. + * Every subclassing instrument has to override it + * ant emit @p wantNoteName() with informations to precess by QML + */ + virtual void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor); + + /** + * Correcting answer logic: + * @p correct() method is called by exam executor. + * This method prepares correction and emits @p correctInstrument() signal. + * QML side (instrument implementation) starts animation then on @p p_wrongItem + * and when incorrect item is hidden by QML, it calls @p applyCorrect() + * to prepare correct answer and @p p_goodItem. + * After that QML continues animations and when finished, + * @p finishCorrectAnim() is invoked and @p correctionFinished() signal emitted. + */ + virtual void correct(const Tnote &n, quint32 noteData) = 0; + + virtual void applyCorrect() { } + + Q_INVOKABLE void finishCorrectAnim() { emit correctionFinished(); } + + /** + * Changes border of given @p item but only if the item has border property (QML Rectangle) + */ + void markBorder(QQuickItem *item, int borderWidth, const QColor &borderColor); + + /** + * Reset wrong/good item pointers to null to be sure that + * nothing spoils normal instrument behavior + */ + void restoreAfterExam(); signals: - void activeChanged(); - void noteChanged(); - void outOfScaleChanged(); - void correctInstrument(); - void correctionFinished(); - void extraNameChanged(); - - /** - * Emitted to display extra note name on the visual QML instrument. - * @p name is note name and @p origin could be everything: - * - position to display name text at - * - QML item to show name on it. - * Corresponding QML item has to understand what @p origin is. - */ - void wantNoteName(const QString& name, const QVariant& origin); + void activeChanged(); + void noteChanged(); + void outOfScaleChanged(); + void correctInstrument(); + void correctionFinished(); + void extraNameChanged(); + + /** + * Emitted to display extra note name on the visual QML instrument. + * @p name is note name and @p origin could be everything: + * - position to display name text at + * - QML item to show name on it. + * Corresponding QML item has to understand what @p origin is. + */ + void wantNoteName(const QString &name, const QVariant &origin); protected: - void hoverEnterEvent(QHoverEvent*) override; - void hoverLeaveEvent(QHoverEvent*) override; + void hoverEnterEvent(QHoverEvent *) override; + void hoverLeaveEvent(QHoverEvent *) override; - void setOutOfScale(bool out); + void setOutOfScale(bool out); - Tnote p_note; - QQuickItem *p_wrongItem = nullptr; - QQuickItem *p_goodItem = nullptr; - QString p_extraName; + Tnote p_note; + QQuickItem *p_wrongItem = nullptr; + QQuickItem *p_goodItem = nullptr; + QString p_extraName; private: - bool m_active = false; - bool m_outOfScale = false; - QQuickItem *m_extraNameItem = nullptr; + bool m_active = false; + bool m_outOfScale = false; + QQuickItem *m_extraNameItem = nullptr; }; #endif // TCOMMONINSTRUMENT_H diff --git a/src/libs/core/instruments/tguitarbg.cpp b/src/libs/core/instruments/tguitarbg.cpp index fef9b73717438dfab68856bcc06bda79ca63ceba..0cc6f84765597c0760ef4154708c4a64109d474c 100644 --- a/src/libs/core/instruments/tguitarbg.cpp +++ b/src/libs/core/instruments/tguitarbg.cpp @@ -17,586 +17,594 @@ ***************************************************************************/ #include "tguitarbg.h" -#include "tglobals.h" #include "music/ttune.h" +#include "tglobals.h" #include "tpath.h" -#include <QtQml/qqmlengine.h> +#include <QtCore/qtimer.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpainter.h> #include <QtGui/qpalette.h> -#include <QtCore/qtimer.h> +#include <QtQml/qqmlengine.h> -#include <QtCore/qdebug.h> #include "checktime.h" +#include <QtCore/qdebug.h> - -TguitarBg::TguitarBg(QQuickItem* parent) : - TcommonInstrument(parent), - m_curStr(7), m_curFret(99) // none +TguitarBg::TguitarBg(QQuickItem *parent) + : TcommonInstrument(parent) + , m_curStr(7) + , m_curFret(99) // none { - setTune(); - - QQmlEngine engine; - QQmlComponent comp(&engine, this); - - comp.setData("import QtQuick 2.9; Rectangle { z: 5 }", QUrl()); - for (int s = 0; s < 6; ++s) { - m_stringItems[s] = qobject_cast<QQuickItem*>(comp.create()); - m_stringItems[s]->setParentItem(this); - m_stringItems[s]->setVisible(false); - m_fingerItems[s] = qobject_cast<QQuickItem*>(comp.create()); - m_fingerItems[s]->setParentItem(this); - m_fingerItems[s]->setVisible(false); - } - p_wrongItem = m_fingerItems[0]; - - connect(GLOB, &Tglobals::guitarParamsChanged, this, &TguitarBg::updateGuitar); - - // Android has QML mouse area instead -#if defined (Q_OS_ANDROID) - setAcceptHoverEvents(false); - setAcceptedMouseButtons(Qt::NoButton); -#endif -} + setTune(); + + QQmlEngine engine; + QQmlComponent comp(&engine, this); + comp.setData("import QtQuick 2.9; Rectangle { z: 5 }", QUrl()); + for (int s = 0; s < 6; ++s) { + m_stringItems[s] = qobject_cast<QQuickItem *>(comp.create()); + m_stringItems[s]->setParentItem(this); + m_stringItems[s]->setVisible(false); + m_fingerItems[s] = qobject_cast<QQuickItem *>(comp.create()); + m_fingerItems[s]->setParentItem(this); + m_fingerItems[s]->setVisible(false); + } + p_wrongItem = m_fingerItems[0]; -QPointF TguitarBg::fretToPos(const TfingerPos& pos) const { - qreal xPos = m_fbRect.x(); - if (pos.fret()) - xPos = m_fretsPos[pos.fret() - 1] - qRound(m_fretWidth / 1.5); - return QPointF(xPos, m_fbRect.y() + m_strGap * (pos.str() - 1) + m_strGap / 2.0 - (pos.str() < 7 ? strWidth(pos.str() - 1) / 2.0 : 0.0)); + connect(GLOB, &Tglobals::guitarParamsChanged, this, &TguitarBg::updateGuitar); + + // Android has QML mouse area instead +#if defined(Q_OS_ANDROID) + setAcceptHoverEvents(false); + setAcceptedMouseButtons(Qt::NoButton); +#endif } +QPointF TguitarBg::fretToPos(const TfingerPos &pos) const +{ + qreal xPos = m_fbRect.x(); + if (pos.fret()) + xPos = m_fretsPos[pos.fret() - 1] - qRound(m_fretWidth / 1.5); + return QPointF(xPos, m_fbRect.y() + m_strGap * (pos.str() - 1) + m_strGap / 2.0 - (pos.str() < 7 ? strWidth(pos.str() - 1) / 2.0 : 0.0)); +} -void TguitarBg::setNote(const Tnote& n, quint32 noteDataValue) { - Q_UNUSED(noteDataValue) - if (!p_note.compareNotes(n)) { - short noteNr = n.chromatic(); - bool foundPos = false; - bool doShow = n.isValid(); - for (int s = 0; s < 6; ++ s) { - auto strNr = GLOB->strOrder(s); - int fret = noteNr - GLOB->Gtune()->strChromatic(strNr + 1); - if (doShow && fret >= 0 && fret <= static_cast<int>(GLOB->GfretsNumber)) { // found - if (fret == 0) { // open string +void TguitarBg::setNote(const Tnote &n, quint32 noteDataValue) +{ + Q_UNUSED(noteDataValue) + if (!p_note.compareNotes(n)) { + short noteNr = n.chromatic(); + bool foundPos = false; + bool doShow = n.isValid(); + for (int s = 0; s < 6; ++s) { + auto strNr = GLOB->strOrder(s); + int fret = noteNr - GLOB->Gtune()->strChromatic(strNr + 1); + if (doShow && fret >= 0 && fret <= static_cast<int>(GLOB->GfretsNumber)) { // found + if (fret == 0) { // open string + m_fingerItems[strNr]->setVisible(false); + m_stringItems[strNr]->setVisible(true); + } else { // some fret + m_fingerItems[strNr]->setVisible(true); + auto p = fretToPos(TfingerPos(static_cast<unsigned char>(strNr + 1), static_cast<unsigned char>(fret))); + m_fingerItems[strNr]->setX(p.x()); + m_fingerItems[strNr]->setY(m_stringItems[strNr]->y() - m_fingerItems[strNr]->height() / 2.0); + m_stringItems[strNr]->setVisible(false); + } + foundPos = true; + if (!GLOB->GshowOtherPos) + doShow = false; + } else { // not found on this string or no need to show - hide then m_fingerItems[strNr]->setVisible(false); - m_stringItems[strNr]->setVisible(true); - } else { // some fret - m_fingerItems[strNr]->setVisible(true); - auto p = fretToPos(TfingerPos(static_cast<unsigned char>(strNr + 1), static_cast<unsigned char>(fret))); - m_fingerItems[strNr]->setX(p.x()); - m_fingerItems[strNr]->setY(m_stringItems[strNr]->y() - m_fingerItems[strNr]->height() / 2.0); m_stringItems[strNr]->setVisible(false); } - foundPos = true; - if (!GLOB->GshowOtherPos) - doShow = false; - } else { // not found on this string or no need to show - hide then - m_fingerItems[strNr]->setVisible(false); - m_stringItems[strNr]->setVisible(false); } - } - setOutOfScale(!foundPos && n.isValid()); - if (outOfScale()) - p_note.setNote(0); // invalidate it - else - p_note = n; -#if defined (Q_OS_ANDROID) - emit noteWasSet(); + setOutOfScale(!foundPos && n.isValid()); + if (outOfScale()) + p_note.setNote(0); // invalidate it + else + p_note = n; +#if defined(Q_OS_ANDROID) + emit noteWasSet(); #endif - } else { - if (p_wrongItem) - p_wrongItem->setVisible(false); - if (p_goodItem) - p_goodItem->setVisible(false); - } - if (m_highlightedString && !n.isValid()) { - m_highlightedString->setVisible(false); - m_latestHighlightedData = 255; - } + } else { + if (p_wrongItem) + p_wrongItem->setVisible(false); + if (p_goodItem) + p_goodItem->setVisible(false); + } + if (m_highlightedString && !n.isValid()) { + m_highlightedString->setVisible(false); + m_latestHighlightedData = 255; + } } - -void TguitarBg::setFingerPos(const TfingerPos& fp) { - QPoint p = fretToPos(fp).toPoint(); - for (int s = 0; s < 6; ++ s) { - if (fp.fret() == 0) { // open string - m_fingerItems[s]->setVisible(false); - m_stringItems[s]->setVisible(fp.str() == s + 1); - } else { // some fret - if (fp.isValid() && fp.str() == s + 1) { - m_fingerItems[s]->setVisible(true); - m_fingerItems[s]->setX(p.x()); - m_fingerItems[s]->setY(m_stringItems[s]->y() - m_fingerItems[s]->height() / 2.0); - } else +void TguitarBg::setFingerPos(const TfingerPos &fp) +{ + QPoint p = fretToPos(fp).toPoint(); + for (int s = 0; s < 6; ++s) { + if (fp.fret() == 0) { // open string m_fingerItems[s]->setVisible(false); - m_stringItems[s]->setVisible(false); + m_stringItems[s]->setVisible(fp.str() == s + 1); + } else { // some fret + if (fp.isValid() && fp.str() == s + 1) { + m_fingerItems[s]->setVisible(true); + m_fingerItems[s]->setX(p.x()); + m_fingerItems[s]->setY(m_stringItems[s]->y() - m_fingerItems[s]->height() / 2.0); + } else + m_fingerItems[s]->setVisible(false); + m_stringItems[s]->setVisible(false); + } } - } } - -qreal TguitarBg::xiiFret() const { - return static_cast<qreal>(m_fretsPos[GLOB->instrument().ukulele() ? 14 : 11]); +qreal TguitarBg::xiiFret() const +{ + return static_cast<qreal>(m_fretsPos[GLOB->instrument().ukulele() ? 14 : 11]); } - #include <QtCore/qelapsedtimer.h> QElapsedTimer paintTimer; -void TguitarBg::paint(QPainter* painter) { - paintTimer.restart(); -// FINGERBOARD - painter->setPen(Qt::NoPen); - if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) - painter->setBrush(QBrush(Qt::black, Qt::SolidPattern)); - else { - QColor fbEdgeColor(175, 160, 114, 220); - painter->setBrush(QBrush(fbEdgeColor, Qt::SolidPattern)); - } - QPolygon a; - int fbThick = ((m_strGap * GLOB->Gtune()->stringNr()) / 6) / 3; // thickness of fretboard - painter->drawRect(m_fbRect.x() + m_strGap / 3 - 8, m_fbRect.y() - fbThick, m_fbRect.width() + fbThick, m_fbRect.height() + fbThick); - if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) - painter->setBrush(QBrush(QPixmap(Tpath::img("fingbg")))); - else - painter->setBrush(QBrush(QPixmap(Tpath::img("fingbg-el")))); - painter->drawRect(m_fbRect); -// FRETS - // zero fret (upper bridge or HUESO) - int huesoW = qRound(m_fbRect.width() * 0.01); - if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) { - painter->setPen(Qt::NoPen); - painter->setBrush(QBrush(QColor(255, 251, 240), Qt::SolidPattern)); // #FFFBF0 cream color for hueso - painter->drawRect(m_fbRect.x() - 8, m_fbRect.y() + 4, huesoW, m_fbRect.height()); - a.setPoints(4, m_fbRect.x() - 8, m_fbRect.y() + 4, - m_fbRect.x() - 8 + huesoW, m_fbRect.y() + 4, - m_fbRect.x() + m_strGap / 3 - 8 + huesoW, m_fbRect.y() - m_strGap / 3, - m_fbRect.x() + m_strGap / 3 - 8, m_fbRect.y() - m_strGap / 3); - painter->setBrush(QBrush(QColor(255, 238, 188), Qt::SolidPattern)); // a bit darker for its rant - painter->drawPolygon(a); - } else { - QLinearGradient fretGrad(m_fbRect.x() - 8, 10.0, m_fbRect.x() - 8 + huesoW, 10.0); - fretGrad.setColorAt(0.0, QColor(218, 228, 228)); - fretGrad.setColorAt(0.4, QColor(127, 128, 110)); - fretGrad.setColorAt(0.7, QColor(59, 56, 43)); - fretGrad.setColorAt(0.9, QColor(0, 0, 0)); // black - painter->setBrush(fretGrad); - painter->drawRoundedRect(m_fbRect.x() - 8, m_fbRect.y() + 2, huesoW, m_fbRect.height() - 4, 2, 2); - } - // others frets - qint8 fretMarks[GLOB->GfretsNumber]; // array keeps whether fret is marked with dots (1) or two (2) - for (unsigned int i = 0; i < GLOB->GfretsNumber; ++i) - fretMarks[i] = 0; - for (int fr = 0; fr < GLOB->GmarkedFrets.size(); ++fr) { - QString exMark, frTxt = GLOB->GmarkedFrets[fr].toString(); - if (frTxt.contains(QLatin1String("!"))) { - exMark = QStringLiteral("!"); - frTxt.replace(QLatin1String("!"), QString()); +void TguitarBg::paint(QPainter *painter) +{ + paintTimer.restart(); + // FINGERBOARD + painter->setPen(Qt::NoPen); + if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) + painter->setBrush(QBrush(Qt::black, Qt::SolidPattern)); + else { + QColor fbEdgeColor(175, 160, 114, 220); + painter->setBrush(QBrush(fbEdgeColor, Qt::SolidPattern)); } - bool ok; - int frNr = frTxt.toInt(&ok); - if (ok && frNr > 0 && frNr <= static_cast<int>(GLOB->GfretsNumber)) { - if (exMark.isEmpty()) - fretMarks[frNr - 1] = 1; - else - fretMarks[frNr - 1] = 2; + QPolygon a; + int fbThick = ((m_strGap * GLOB->Gtune()->stringNr()) / 6) / 3; // thickness of fretboard + painter->drawRect(m_fbRect.x() + m_strGap / 3 - 8, m_fbRect.y() - fbThick, m_fbRect.width() + fbThick, m_fbRect.height() + fbThick); + if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) + painter->setBrush(QBrush(QPixmap(Tpath::img("fingbg")))); + else + painter->setBrush(QBrush(QPixmap(Tpath::img("fingbg-el")))); + painter->drawRect(m_fbRect); + // FRETS + // zero fret (upper bridge or HUESO) + int huesoW = qRound(m_fbRect.width() * 0.01); + if (GLOB->instrument().classicGuitar() || GLOB->instrument().ukulele()) { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(QColor(255, 251, 240), Qt::SolidPattern)); // #FFFBF0 cream color for hueso + painter->drawRect(m_fbRect.x() - 8, m_fbRect.y() + 4, huesoW, m_fbRect.height()); + a.setPoints(4, + m_fbRect.x() - 8, + m_fbRect.y() + 4, + m_fbRect.x() - 8 + huesoW, + m_fbRect.y() + 4, + m_fbRect.x() + m_strGap / 3 - 8 + huesoW, + m_fbRect.y() - m_strGap / 3, + m_fbRect.x() + m_strGap / 3 - 8, + m_fbRect.y() - m_strGap / 3); + painter->setBrush(QBrush(QColor(255, 238, 188), Qt::SolidPattern)); // a bit darker for its rant + painter->drawPolygon(a); + } else { + QLinearGradient fretGrad(m_fbRect.x() - 8, 10.0, m_fbRect.x() - 8 + huesoW, 10.0); + fretGrad.setColorAt(0.0, QColor(218, 228, 228)); + fretGrad.setColorAt(0.4, QColor(127, 128, 110)); + fretGrad.setColorAt(0.7, QColor(59, 56, 43)); + fretGrad.setColorAt(0.9, QColor(0, 0, 0)); // black + painter->setBrush(fretGrad); + painter->drawRoundedRect(m_fbRect.x() - 8, m_fbRect.y() + 2, huesoW, m_fbRect.height() - 4, 2, 2); } - } - for (unsigned int i = 0; i < GLOB->GfretsNumber; i++) { - QLinearGradient fretGrad(0.0, 0.0, 1.0, 0.0); - fretGrad.setCoordinateMode(QGradient::ObjectBoundingMode); - fretGrad.setColorAt(0.0, QColor(218, 228, 228)); - fretGrad.setColorAt(0.4, QColor(127, 128, 110)); - fretGrad.setColorAt(0.7, QColor(59, 56, 43)); - fretGrad.setColorAt(0.9, QColor(0, 0, 0)); - painter->setBrush(fretGrad); - painter->drawRoundedRect(m_fretsPos[i], m_fbRect.y() + 2, m_fbRect.width() * 0.0085, m_fbRect.height() - 4, 2, 2); - - if (fretMarks[i]) { - int markW = m_strGap / 3; - painter->setBrush(QBrush(Qt::white, Qt::SolidPattern)); //white color for circles marking 5, 7, 9... frets - if (fretMarks[i] == 1) { - painter->drawEllipse(m_fretsPos[i] - markW / 2 - (m_fretsPos[i] - m_fretsPos[i - 1]) / 2, - m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, markW, markW); - } else { - painter->drawEllipse(m_fretsPos[i - 1] + 2 + ((m_fretsPos[i] - m_fretsPos[i - 1]) - 2 * markW) / 3, - m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, markW, markW); - painter->drawEllipse(m_fretsPos[i - 1] + 2 + (((m_fretsPos[i] - m_fretsPos[i - 1]) - 2 * markW) / 3) * 2 + markW, - m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, markW, markW); - } + // others frets + qint8 fretMarks[GLOB->GfretsNumber]; // array keeps whether fret is marked with dots (1) or two (2) + for (unsigned int i = 0; i < GLOB->GfretsNumber; ++i) + fretMarks[i] = 0; + for (int fr = 0; fr < GLOB->GmarkedFrets.size(); ++fr) { + QString exMark, frTxt = GLOB->GmarkedFrets[fr].toString(); + if (frTxt.contains(QLatin1String("!"))) { + exMark = QStringLiteral("!"); + frTxt.replace(QLatin1String("!"), QString()); + } + bool ok; + int frNr = frTxt.toInt(&ok); + if (ok && frNr > 0 && frNr <= static_cast<int>(GLOB->GfretsNumber)) { + if (exMark.isEmpty()) + fretMarks[frNr - 1] = 1; + else + fretMarks[frNr - 1] = 2; + } } - } -// STRINGS - QFont strFont = qApp->font(); - strFont.setPixelSize((int)qRound(0.75 * m_strGap));//setting font for digits - painter->setFont(strFont); - painter->setBrush(QBrush(Qt::NoBrush)); - for (int i = 0; i < 6; i++) { -// drawing main strings - qreal lineYpos = m_fbRect.y() + m_strGap / 2 + i * m_strGap; - QLinearGradient strGrad(1.0, lineYpos - m_strWidth[i] / 2, 1.0, lineYpos + m_strWidth[i] / 2); - strGrad.setColorAt(0.0, m_strColors[i]); - strGrad.setColorAt(0.3, m_strColors[i]); - strGrad.setColorAt(0.7, m_strColors[i].darker()); - painter->setPen(QPen(QBrush(strGrad), m_strWidth[i], Qt::SolidLine)); - painter->drawLine(1, lineYpos, width() - 1 - m_strGap, lineYpos); - if (i < GLOB->Gtune()->stringNr()) { -// drawing digits of strings in circles - painter->setPen(QPen(m_strColors[i], 1, Qt::SolidLine)); - painter->setBrush(QBrush(QColor(0, 0, 0, 180))); - int circleX; - if (!GLOB->GisRightHanded) { - painter->scale (-1, 1); - painter->translate(-width(), 0); - circleX = 1; - } else - circleX = width() - 1 - m_strGap; - QRect elRect(circleX, m_fbRect.y() + i * m_strGap, m_strGap - 1, m_strGap - 1); - painter->drawEllipse(elRect); - painter->setPen(QPen(Qt::green,1,Qt::SolidLine));// in green color - painter->drawText(elRect, Qt::AlignCenter, QString::number(i + 1)); - if (!GLOB->GisRightHanded) { - painter->translate(width(), 0); - painter->scale (-1, 1); + for (unsigned int i = 0; i < GLOB->GfretsNumber; i++) { + QLinearGradient fretGrad(0.0, 0.0, 1.0, 0.0); + fretGrad.setCoordinateMode(QGradient::ObjectBoundingMode); + fretGrad.setColorAt(0.0, QColor(218, 228, 228)); + fretGrad.setColorAt(0.4, QColor(127, 128, 110)); + fretGrad.setColorAt(0.7, QColor(59, 56, 43)); + fretGrad.setColorAt(0.9, QColor(0, 0, 0)); + painter->setBrush(fretGrad); + painter->drawRoundedRect(m_fretsPos[i], m_fbRect.y() + 2, m_fbRect.width() * 0.0085, m_fbRect.height() - 4, 2, 2); + + if (fretMarks[i]) { + int markW = m_strGap / 3; + painter->setBrush(QBrush(Qt::white, Qt::SolidPattern)); // white color for circles marking 5, 7, 9... frets + if (fretMarks[i] == 1) { + painter->drawEllipse(m_fretsPos[i] - markW / 2 - (m_fretsPos[i] - m_fretsPos[i - 1]) / 2, + m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, + markW, + markW); + } else { + painter->drawEllipse(m_fretsPos[i - 1] + 2 + ((m_fretsPos[i] - m_fretsPos[i - 1]) - 2 * markW) / 3, + m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, + markW, + markW); + painter->drawEllipse(m_fretsPos[i - 1] + 2 + (((m_fretsPos[i] - m_fretsPos[i - 1]) - 2 * markW) / 3) * 2 + markW, + m_fbRect.y() + m_strGap * int((GLOB->Gtune()->stringNr() / 2)) - markW / 2, + markW, + markW); + } } - // shadow of the strings - painter->setPen(QPen(QColor(0, 0, 0, 150), m_strWidth[i], Qt::SolidLine)); // on the fingerboard - int yy = lineYpos + m_strWidth[i] * 1.5;// m_strGap * 0.2; - painter->drawLine(m_fbRect.x() - 6 + huesoW, yy, m_fbRect.x() + m_fbRect.width() - 1, yy); - painter->setPen(QPen(Qt::black, 1, Qt::SolidLine)); //on upper bridge - painter->drawLine(m_fbRect.x() - 8, lineYpos - 2, m_fbRect.x() - 8 + huesoW , lineYpos - 2); - painter->drawLine(m_fbRect.x() - 8, lineYpos + m_strWidth[i] - 1, m_fbRect.x() - 8 + huesoW, lineYpos + m_strWidth[i] - 1); - if (GLOB->instrument().electricGuitar() || GLOB->instrument().bassGuitar()) { // shadow on the pickup if exist (bass or electric guitar) - int pickX = width() * 0.88; -// if (!GLOB->GisRightHanded) -// pickX = width() - (BG_PIX->pickUpRect().x() + BG_PIX->pickUpRect().width()); - painter->setPen(QPen(QColor(28, 28, 28, 30), m_strWidth[i], Qt::SolidLine)); - yy += m_strGap * 0.1; - qreal pickupWidth = height() * 0.6217948717948718 * 1.3; - int subW = qRound(pickupWidth * 0.15); - QPoint ps[5] = { - QPoint(m_fbRect.x() + m_fbRect.width() + fbThick, yy + m_strGap * 0.12), - QPoint(pickX, yy + m_strGap * 0.12), - QPoint(pickX + subW, yy), - QPoint(pickX + pickupWidth - subW, yy), - QPoint(pickX + pickupWidth - subW * 0.3, yy + m_strGap * 0.15) - }; - painter->drawPolyline(ps, 5); + } + // STRINGS + QFont strFont = qApp->font(); + strFont.setPixelSize((int)qRound(0.75 * m_strGap)); // setting font for digits + painter->setFont(strFont); + painter->setBrush(QBrush(Qt::NoBrush)); + for (int i = 0; i < 6; i++) { + // drawing main strings + qreal lineYpos = m_fbRect.y() + m_strGap / 2 + i * m_strGap; + QLinearGradient strGrad(1.0, lineYpos - m_strWidth[i] / 2, 1.0, lineYpos + m_strWidth[i] / 2); + strGrad.setColorAt(0.0, m_strColors[i]); + strGrad.setColorAt(0.3, m_strColors[i]); + strGrad.setColorAt(0.7, m_strColors[i].darker()); + painter->setPen(QPen(QBrush(strGrad), m_strWidth[i], Qt::SolidLine)); + painter->drawLine(1, lineYpos, width() - 1 - m_strGap, lineYpos); + if (i < GLOB->Gtune()->stringNr()) { + // drawing digits of strings in circles + painter->setPen(QPen(m_strColors[i], 1, Qt::SolidLine)); + painter->setBrush(QBrush(QColor(0, 0, 0, 180))); + int circleX; + if (!GLOB->GisRightHanded) { + painter->scale(-1, 1); + painter->translate(-width(), 0); + circleX = 1; + } else + circleX = width() - 1 - m_strGap; + QRect elRect(circleX, m_fbRect.y() + i * m_strGap, m_strGap - 1, m_strGap - 1); + painter->drawEllipse(elRect); + painter->setPen(QPen(Qt::green, 1, Qt::SolidLine)); // in green color + painter->drawText(elRect, Qt::AlignCenter, QString::number(i + 1)); + if (!GLOB->GisRightHanded) { + painter->translate(width(), 0); + painter->scale(-1, 1); + } + // shadow of the strings + painter->setPen(QPen(QColor(0, 0, 0, 150), m_strWidth[i], Qt::SolidLine)); // on the fingerboard + int yy = lineYpos + m_strWidth[i] * 1.5; // m_strGap * 0.2; + painter->drawLine(m_fbRect.x() - 6 + huesoW, yy, m_fbRect.x() + m_fbRect.width() - 1, yy); + painter->setPen(QPen(Qt::black, 1, Qt::SolidLine)); // on upper bridge + painter->drawLine(m_fbRect.x() - 8, lineYpos - 2, m_fbRect.x() - 8 + huesoW, lineYpos - 2); + painter->drawLine(m_fbRect.x() - 8, lineYpos + m_strWidth[i] - 1, m_fbRect.x() - 8 + huesoW, lineYpos + m_strWidth[i] - 1); + if (GLOB->instrument().electricGuitar() || GLOB->instrument().bassGuitar()) { // shadow on the pickup if exist (bass or electric guitar) + int pickX = width() * 0.88; + // if (!GLOB->GisRightHanded) + // pickX = width() - (BG_PIX->pickUpRect().x() + BG_PIX->pickUpRect().width()); + painter->setPen(QPen(QColor(28, 28, 28, 30), m_strWidth[i], Qt::SolidLine)); + yy += m_strGap * 0.1; + qreal pickupWidth = height() * 0.6217948717948718 * 1.3; + int subW = qRound(pickupWidth * 0.15); + QPoint ps[5] = {QPoint(m_fbRect.x() + m_fbRect.width() + fbThick, yy + m_strGap * 0.12), + QPoint(pickX, yy + m_strGap * 0.12), + QPoint(pickX + subW, yy), + QPoint(pickX + pickupWidth - subW, yy), + QPoint(pickX + pickupWidth - subW * 0.3, yy + m_strGap * 0.15)}; + painter->drawPolyline(ps, 5); + } } - } - } } - -void TguitarBg::askQuestion(const Tnote& n, quint32 noteDataValue) { - p_note = n; - TfingerPos fp(static_cast<quint8>(noteDataValue)); - setFingerPos(fp); +void TguitarBg::askQuestion(const Tnote &n, quint32 noteDataValue) +{ + p_note = n; + TfingerPos fp(static_cast<quint8>(noteDataValue)); + setFingerPos(fp); } - -void TguitarBg::highlightAnswer(const Tnote& n, quint32 noteData) { - Q_UNUSED(n) - if (!m_highlightedString) { - QQmlEngine engine; - QQmlComponent comp(&engine, this); - comp.setData("import QtQuick 2.9; Rectangle {}", QUrl()); - m_highlightedString = qobject_cast<QQuickItem*>(comp.create()); - m_highlightedString->setParentItem(this); - m_highlightedString->setZ(1); - } - TfingerPos f(static_cast<quint8>(noteData)); - if (f.str() > 0 && f.str() < 7) { - int strNr = f.str() - 1; - m_highlightedString->setX(m_stringItems[strNr]->x()); - m_highlightedString->setY(m_stringItems[strNr]->y()); - m_highlightedString->setWidth(m_stringItems[strNr]->width()); - m_highlightedString->setHeight(m_stringItems[strNr]->height()); - m_highlightedString->setProperty("color", GLOB->EanswerColor); - m_highlightedString->setVisible(true); - m_latestHighlightedData = noteData; - } +void TguitarBg::highlightAnswer(const Tnote &n, quint32 noteData) +{ + Q_UNUSED(n) + if (!m_highlightedString) { + QQmlEngine engine; + QQmlComponent comp(&engine, this); + comp.setData("import QtQuick 2.9; Rectangle {}", QUrl()); + m_highlightedString = qobject_cast<QQuickItem *>(comp.create()); + m_highlightedString->setParentItem(this); + m_highlightedString->setZ(1); + } + TfingerPos f(static_cast<quint8>(noteData)); + if (f.str() > 0 && f.str() < 7) { + int strNr = f.str() - 1; + m_highlightedString->setX(m_stringItems[strNr]->x()); + m_highlightedString->setY(m_stringItems[strNr]->y()); + m_highlightedString->setWidth(m_stringItems[strNr]->width()); + m_highlightedString->setHeight(m_stringItems[strNr]->height()); + m_highlightedString->setProperty("color", GLOB->EanswerColor); + m_highlightedString->setVisible(true); + m_latestHighlightedData = noteData; + } } +void TguitarBg::updateGuitar() +{ + if (!GLOB->instrument().isGuitar()) + return; // HACK: Avoid crash when instrument is changed form guitar to another kind -void TguitarBg::updateGuitar() { - if (!GLOB->instrument().isGuitar()) - return; // HACK: Avoid crash when instrument is changed form guitar to another kind - - setTune(); - geometryChanged(QRectF(x(), y(), width(), height()), QRectF()); + setTune(); + geometryChanged(QRectF(x(), y(), width(), height()), QRectF()); } - -void TguitarBg::markSelected(const QColor& markColor) { - int borderWidth = markColor.alpha() ? qRound(height() / 40.0) : 0; - for (int s = 0; s < 6; ++s) { - markBorder(m_stringItems[s], borderWidth, markColor); - markBorder(m_fingerItems[s], borderWidth, markColor); - } -#if defined (Q_OS_ANDROID) - emit clearGuitar(); +void TguitarBg::markSelected(const QColor &markColor) +{ + int borderWidth = markColor.alpha() ? qRound(height() / 40.0) : 0; + for (int s = 0; s < 6; ++s) { + markBorder(m_stringItems[s], borderWidth, markColor); + markBorder(m_fingerItems[s], borderWidth, markColor); + } +#if defined(Q_OS_ANDROID) + emit clearGuitar(); #endif } - -void TguitarBg::showNoteName(Tnote::EnameStyle st, const Tnote& n, quint32 techn, const QColor& textColor) { - TcommonInstrument::showNoteName(st, n, techn, textColor); - QQuickItem* visIt = nullptr; - for (int s = 0; s < 6; ++ s) { - if (m_fingerItems[s]->isVisible()) - visIt = m_fingerItems[s]; - else if (m_stringItems[s]->isVisible()) - visIt = m_stringItems[s]; - if (visIt) - break; - } - emit wantNoteName(p_extraName, QVariant::fromValue(visIt)); +void TguitarBg::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) +{ + TcommonInstrument::showNoteName(st, n, techn, textColor); + QQuickItem *visIt = nullptr; + for (int s = 0; s < 6; ++s) { + if (m_fingerItems[s]->isVisible()) + visIt = m_fingerItems[s]; + else if (m_stringItems[s]->isVisible()) + visIt = m_stringItems[s]; + if (visIt) + break; + } + emit wantNoteName(p_extraName, QVariant::fromValue(visIt)); } - -void TguitarBg::correct(const Tnote& n, quint32 noteData) { - if (m_selectedPos.isValid()) { - if (m_selectedPos.fret() > 0) - p_wrongItem = m_fingerItems[m_selectedPos.str() - 1]; - else - p_wrongItem = m_stringItems[m_selectedPos.str() - 1]; - } else - p_wrongItem = nullptr; - p_note = n; - m_goodPos = TfingerPos(static_cast<quint8>(noteData)); - if (m_goodPos.isValid()) { - if (m_goodPos.fret() > 0) - p_goodItem = m_fingerItems[m_goodPos.str() - 1]; - else - p_goodItem = m_stringItems[m_goodPos.str() - 1]; - } else - p_goodItem = nullptr; - emit correctInstrument(); +void TguitarBg::correct(const Tnote &n, quint32 noteData) +{ + if (m_selectedPos.isValid()) { + if (m_selectedPos.fret() > 0) + p_wrongItem = m_fingerItems[m_selectedPos.str() - 1]; + else + p_wrongItem = m_stringItems[m_selectedPos.str() - 1]; + } else + p_wrongItem = nullptr; + p_note = n; + m_goodPos = TfingerPos(static_cast<quint8>(noteData)); + if (m_goodPos.isValid()) { + if (m_goodPos.fret() > 0) + p_goodItem = m_fingerItems[m_goodPos.str() - 1]; + else + p_goodItem = m_stringItems[m_goodPos.str() - 1]; + } else + p_goodItem = nullptr; + emit correctInstrument(); } - -void TguitarBg::applyCorrect() { - if (p_wrongItem) - p_wrongItem->setVisible(false); - if (p_goodItem) { - if (m_goodPos.fret() > 0) { - auto p = fretToPos(m_goodPos); - p_goodItem->setX(p.x()); - p_goodItem->setY(m_stringItems[m_goodPos.str() - 1]->y() - m_fingerItems[m_goodPos.str() - 1]->height() / 2.0); - } - p_goodItem->setVisible(true); - markSelected(GLOB->correctColor()); - if (m_highlightedString) - m_highlightedString->setVisible(false); - } - if (!p_extraName.isEmpty()) { - p_extraName = QStringLiteral(" "); // hide name of wrong pointed note - // HACK: but do not clear extra name text - finish correction routines depend on it - emit wantNoteName(p_extraName, QVariant()); - } +void TguitarBg::applyCorrect() +{ + if (p_wrongItem) + p_wrongItem->setVisible(false); + if (p_goodItem) { + if (m_goodPos.fret() > 0) { + auto p = fretToPos(m_goodPos); + p_goodItem->setX(p.x()); + p_goodItem->setY(m_stringItems[m_goodPos.str() - 1]->y() - m_fingerItems[m_goodPos.str() - 1]->height() / 2.0); + } + p_goodItem->setVisible(true); + markSelected(GLOB->correctColor()); + if (m_highlightedString) + m_highlightedString->setVisible(false); + } + if (!p_extraName.isEmpty()) { + p_extraName = QStringLiteral(" "); // hide name of wrong pointed note + // HACK: but do not clear extra name text - finish correction routines depend on it + emit wantNoteName(p_extraName, QVariant()); + } } - -void TguitarBg::finishCorrectAnim() { - if (p_wrongItem) { - p_wrongItem->setScale(1.0); - p_wrongItem->setOpacity(1.0); - } - p_note.setNote(0); - m_selectedPos.setData(255); - TcommonInstrument::finishCorrectAnim(); +void TguitarBg::finishCorrectAnim() +{ + if (p_wrongItem) { + p_wrongItem->setScale(1.0); + p_wrongItem->setOpacity(1.0); + } + p_note.setNote(0); + m_selectedPos.setData(255); + TcommonInstrument::finishCorrectAnim(); } - -void TguitarBg::pressedAt(qreal px, qreal py) { - paintFingerAtPoint(QPointF(px, py).toPoint()); - // skip press point set - not used in that event handler - auto me = new QMouseEvent(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - mouseReleaseEvent(me); +void TguitarBg::pressedAt(qreal px, qreal py) +{ + paintFingerAtPoint(QPointF(px, py).toPoint()); + // skip press point set - not used in that event handler + auto me = new QMouseEvent(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + mouseReleaseEvent(me); } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -void TguitarBg::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) { - if (oldGeometry.width() != newGeometry.width() || oldGeometry.height() != newGeometry.height()) { - if (!GLOB->instrument().isGuitar()) - return; - - QSize newSize = newGeometry.size().toSize(); - m_fbRect = QRect(10, newSize.height() / 18, - GLOB->instrument().ukulele() ? newSize.width() * 0.8 : (6 * newSize.width()) / 7, - newSize.height() - newSize.height() / 18); - m_fretWidth = ((m_fbRect.width() + ((GLOB->GfretsNumber / 2) * (GLOB->GfretsNumber / 2 + 1)) - + GLOB->GfretsNumber / 4) / (GLOB->GfretsNumber + 1)) + 1; - m_strGap = m_fbRect.height() / GLOB->Gtune()->stringNr(); - m_fretsPos[0] = m_fbRect.x() + m_fretWidth; - for (unsigned int i = 2; i < GLOB->GfretsNumber + 1; i++) - m_fretsPos[i - 1] = m_fretsPos[i - 2] + (m_fretWidth - (i / 2)); - m_lastFret = m_fretsPos[GLOB->GfretsNumber - 1]; - if (m_lastFret > (m_fbRect.width() + 10)) - m_fbRect.setWidth(m_lastFret - 8); - qreal wFactor = newSize.height() / 150.0; - m_strWidth[0] = m_widthFromPitch[0] * wFactor; - m_strWidth[1] = m_widthFromPitch[1] * wFactor; - m_strWidth[2] = m_widthFromPitch[2] * wFactor; - m_strWidth[3] = m_widthFromPitch[3] * wFactor; - m_strWidth[4] = m_widthFromPitch[4] * wFactor; - m_strWidth[5] = m_widthFromPitch[5] * wFactor; +void TguitarBg::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (oldGeometry.width() != newGeometry.width() || oldGeometry.height() != newGeometry.height()) { + if (!GLOB->instrument().isGuitar()) + return; + + QSize newSize = newGeometry.size().toSize(); + m_fbRect = QRect(10, + newSize.height() / 18, + GLOB->instrument().ukulele() ? newSize.width() * 0.8 : (6 * newSize.width()) / 7, + newSize.height() - newSize.height() / 18); + m_fretWidth = ((m_fbRect.width() + ((GLOB->GfretsNumber / 2) * (GLOB->GfretsNumber / 2 + 1)) + GLOB->GfretsNumber / 4) / (GLOB->GfretsNumber + 1)) + 1; + m_strGap = m_fbRect.height() / GLOB->Gtune()->stringNr(); + m_fretsPos[0] = m_fbRect.x() + m_fretWidth; + for (unsigned int i = 2; i < GLOB->GfretsNumber + 1; i++) + m_fretsPos[i - 1] = m_fretsPos[i - 2] + (m_fretWidth - (i / 2)); + m_lastFret = m_fretsPos[GLOB->GfretsNumber - 1]; + if (m_lastFret > (m_fbRect.width() + 10)) + m_fbRect.setWidth(m_lastFret - 8); + qreal wFactor = newSize.height() / 150.0; + m_strWidth[0] = m_widthFromPitch[0] * wFactor; + m_strWidth[1] = m_widthFromPitch[1] * wFactor; + m_strWidth[2] = m_widthFromPitch[2] * wFactor; + m_strWidth[3] = m_widthFromPitch[3] * wFactor; + m_strWidth[4] = m_widthFromPitch[4] * wFactor; + m_strWidth[5] = m_widthFromPitch[5] * wFactor; + + for (int s = 0; s < 6; ++s) { + m_stringItems[s]->setProperty("color", GLOB->GselectedColor); + m_stringItems[s]->setWidth(newGeometry.width() - m_strGap - 2.0); + m_stringItems[s]->setHeight(m_strWidth[s] * 1.5); + m_stringItems[s]->setX(1.0); + m_stringItems[s]->setY(m_fbRect.y() + static_cast<qreal>(m_strGap * s) + m_strGap / 2.0 - strWidth(s) / 2.0); + + m_fingerItems[s]->setProperty("color", GLOB->GselectedColor); + m_fingerItems[s]->setWidth(m_fretWidth / 1.6); + m_fingerItems[s]->setHeight(m_fretWidth / 2.46); + m_fingerItems[s]->setProperty("radius", m_fretWidth / 3.2); + } + if (m_highlightedString && m_latestHighlightedData != 255) + highlightAnswer(Tnote(), m_latestHighlightedData); + + emit fretWidthChanged(); + emit stringsGapChanged(); + + if (oldGeometry.height() != newGeometry.height()) + emit heightChanged(); + if (oldGeometry.width() != newGeometry.width()) + emit widthChanged(); + if (p_note.isValid()) { // update position(s) of fingers by resetting note and setting it again + Tnote tmpNote = p_note; + p_note.setNote(0); + setNote(tmpNote); + } - for (int s = 0; s < 6; ++s) { - m_stringItems[s]->setProperty("color", GLOB->GselectedColor); - m_stringItems[s]->setWidth(newGeometry.width() - m_strGap - 2.0); - m_stringItems[s]->setHeight(m_strWidth[s] * 1.5); - m_stringItems[s]->setX(1.0); - m_stringItems[s]->setY(m_fbRect.y() + static_cast<qreal>(m_strGap * s) + m_strGap / 2.0 - strWidth(s) / 2.0); - - m_fingerItems[s]->setProperty("color", GLOB->GselectedColor); - m_fingerItems[s]->setWidth(m_fretWidth / 1.6); - m_fingerItems[s]->setHeight(m_fretWidth / 2.46); - m_fingerItems[s]->setProperty("radius", m_fretWidth / 3.2); + update(); } - if (m_highlightedString && m_latestHighlightedData != 255) - highlightAnswer(Tnote(), m_latestHighlightedData); - - emit fretWidthChanged(); - emit stringsGapChanged(); - - if (oldGeometry.height() != newGeometry.height()) - emit heightChanged(); - if (oldGeometry.width() != newGeometry.width()) - emit widthChanged(); - if (p_note.isValid()) { // update position(s) of fingers by resetting note and setting it again - Tnote tmpNote = p_note; - p_note.setNote(0); - setNote(tmpNote); - } - - update(); - } } +void TguitarBg::hoverMoveEvent(QHoverEvent *event) +{ + if (!active()) + hoverEnterEvent(nullptr); -void TguitarBg::hoverMoveEvent(QHoverEvent* event) { - if (!active()) - hoverEnterEvent(nullptr); - - paintFingerAtPoint(event->pos()); + paintFingerAtPoint(event->pos()); } - -void TguitarBg::mousePressEvent(QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton) { - setPressed(true); - m_mouseStartPos = event->pos(); - } +void TguitarBg::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + setPressed(true); + m_mouseStartPos = event->pos(); + } } - -void TguitarBg::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { - if ((event->pos() - m_mouseStartPos).manhattanLength() < m_fretWidth / 2) { - if (m_curStr < 7) { - m_selectedPos.setPos(m_curStr + 1, m_curFret); - Tnote n(GLOB->Gtune()->strChromatic(m_curStr + 1) + m_curFret); - if (GLOB->showOtherPos()) - setNote(n); // selects all possible positions on the fingerboard - else { // it selects only just clicked position - p_note = n; - setFingerPos(m_selectedPos); - } - emit fingerPosChanged(); - emit noteChanged(); - } +void TguitarBg::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + if ((event->pos() - m_mouseStartPos).manhattanLength() < m_fretWidth / 2) { + if (m_curStr < 7) { + m_selectedPos.setPos(m_curStr + 1, m_curFret); + Tnote n(GLOB->Gtune()->strChromatic(m_curStr + 1) + m_curFret); + if (GLOB->showOtherPos()) + setNote(n); // selects all possible positions on the fingerboard + else { // it selects only just clicked position + p_note = n; + setFingerPos(m_selectedPos); + } + emit fingerPosChanged(); + emit noteChanged(); + } + } + setPressed(false); } - setPressed(false); - } } - -void TguitarBg::paintFingerAtPoint(QPoint p) { - int strNr = 7, fretNr = 99; - if ( (p.y() >= m_fbRect.y()) && (p.y() <= (height() - m_fbRect.y() - 4)) ) { - int tx = p.x(), ty = p.y(); -// tx = mapToScene(p.x(), p.y()).x(); TODO lefthand guitars - strNr = (ty - m_fbRect.y()) / m_strGap; - if (tx < m_fbRect.x() || tx > m_lastFret) - fretNr = 0; - else { - for (unsigned int i = 0; i < GLOB->GfretsNumber; i++) { - if (tx <= m_fretsPos[i]) { - fretNr = i + 1; - break; +void TguitarBg::paintFingerAtPoint(QPoint p) +{ + int strNr = 7, fretNr = 99; + if ((p.y() >= m_fbRect.y()) && (p.y() <= (height() - m_fbRect.y() - 4))) { + int tx = p.x(), ty = p.y(); + // tx = mapToScene(p.x(), p.y()).x(); TODO lefthand guitars + strNr = (ty - m_fbRect.y()) / m_strGap; + if (tx < m_fbRect.x() || tx > m_lastFret) + fretNr = 0; + else { + for (unsigned int i = 0; i < GLOB->GfretsNumber; i++) { + if (tx <= m_fretsPos[i]) { + fretNr = i + 1; + break; + } + } } - } } - } - - if (m_curStr != strNr || m_curFret != fretNr) { - if (fretNr > 0 && fretNr < 99) { // show finger - m_fingerPos = fretToPos(TfingerPos(static_cast<unsigned char>(strNr + 1), static_cast<unsigned char>(fretNr))); - if (strNr < 6) - m_fingerPos.setY(m_stringItems[strNr]->y() - m_fingerItems[0]->height() / 2.0); -// if (m_curStr != 7) m_workStrings[m_curStr]->hide(); - } else { // show string line - m_fingerPos.setX(0.0); + + if (m_curStr != strNr || m_curFret != fretNr) { + if (fretNr > 0 && fretNr < 99) { // show finger + m_fingerPos = fretToPos(TfingerPos(static_cast<unsigned char>(strNr + 1), static_cast<unsigned char>(fretNr))); + if (strNr < 6) + m_fingerPos.setY(m_stringItems[strNr]->y() - m_fingerItems[0]->height() / 2.0); + // if (m_curStr != 7) m_workStrings[m_curStr]->hide(); + } else { // show string line + m_fingerPos.setX(0.0); + } + m_curStr = strNr; + m_curFret = fretNr; + emit fingerPosChanged(); + emit stringChanged(); } - m_curStr = strNr; - m_curFret = fretNr; - emit fingerPosChanged(); - emit stringChanged(); - } } - -void TguitarBg::setTune() { - for (int i = 0; i < GLOB->Gtune()->stringNr(); i++) { - int stringChromatic = GLOB->Gtune()->strChromatic(i + 1) + GLOB->transposition(); - if (stringChromatic > 14) { // highest than cis1 - m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 200 : 125)); // are nylon - m_widthFromPitch[i] = 2; // and thinner - } else if (stringChromatic > 10) { // highest than gis - m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 200 : 125)); // are nylon - m_widthFromPitch[i] = 2.5; // and more thick - } else if (stringChromatic > 4) { // highest than dis - m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 215 : 150)); // are nylon - m_widthFromPitch[i] = 3; // and more thick - } else if (stringChromatic > 0) { // highest than b-1(contra) - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 3; // and more thick - } else if (stringChromatic > -5) { // highest than g-1(contra) 1-st string of bass - m_strColors[i] = QColor(194, 148, 50); // #C29432" // are gold-plated - m_widthFromPitch[i] = 3.5; // and more thick - } else if (stringChromatic > -10) { // highest than d-1(contra) - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 4; // and more thick - } else if (stringChromatic > -15) { // highest than gis-2(subcontra) - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 4.5; // and more thick - } else if (stringChromatic > -20) { // highest than dis-1(contra) - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 5; // and more thick - } else if (stringChromatic > -25) { // highest than - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 6; // and more thick - } else if (stringChromatic > -30) { // highest than - m_strColors[i] = QColor(194, 148, 50); // are gold-plated - m_widthFromPitch[i] = 7; // and more thick +void TguitarBg::setTune() +{ + for (int i = 0; i < GLOB->Gtune()->stringNr(); i++) { + int stringChromatic = GLOB->Gtune()->strChromatic(i + 1) + GLOB->transposition(); + if (stringChromatic > 14) { // highest than cis1 + m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 200 : 125)); // are nylon + m_widthFromPitch[i] = 2; // and thinner + } else if (stringChromatic > 10) { // highest than gis + m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 200 : 125)); // are nylon + m_widthFromPitch[i] = 2.5; // and more thick + } else if (stringChromatic > 4) { // highest than dis + m_strColors[i] = QColor(255, 255, 255, (GLOB->instrument().ukulele() ? 215 : 150)); // are nylon + m_widthFromPitch[i] = 3; // and more thick + } else if (stringChromatic > 0) { // highest than b-1(contra) + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 3; // and more thick + } else if (stringChromatic > -5) { // highest than g-1(contra) 1-st string of bass + m_strColors[i] = QColor(194, 148, 50); // #C29432" // are gold-plated + m_widthFromPitch[i] = 3.5; // and more thick + } else if (stringChromatic > -10) { // highest than d-1(contra) + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 4; // and more thick + } else if (stringChromatic > -15) { // highest than gis-2(subcontra) + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 4.5; // and more thick + } else if (stringChromatic > -20) { // highest than dis-1(contra) + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 5; // and more thick + } else if (stringChromatic > -25) { // highest than + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 6; // and more thick + } else if (stringChromatic > -30) { // highest than + m_strColors[i] = QColor(194, 148, 50); // are gold-plated + m_widthFromPitch[i] = 7; // and more thick + } + if (GLOB->instrument().ukulele()) + m_widthFromPitch[i] *= 1.75; } - if (GLOB->instrument().ukulele()) - m_widthFromPitch[i] *= 1.75; - } } - -void TguitarBg::setPressed(bool pr) { - if (pr != m_pressed) { - m_pressed = pr; - emit pressedChanged(); - } +void TguitarBg::setPressed(bool pr) +{ + if (pr != m_pressed) { + m_pressed = pr; + emit pressedChanged(); + } } - diff --git a/src/libs/core/instruments/tguitarbg.h b/src/libs/core/instruments/tguitarbg.h index 04069b033e1bb7fd18dc658bd734d14f2459168d..05ec673de757b727ca4a5641aa1234e0f95ab3a5 100644 --- a/src/libs/core/instruments/tguitarbg.h +++ b/src/libs/core/instruments/tguitarbg.h @@ -19,12 +19,10 @@ #ifndef TGUITARBG_H #define TGUITARBG_H - #include "nootkacoreglobal.h" #include "tcommoninstrument.h" #include "tfingerpos.h" - /** * This is static background of guitar QML component. * It re-paints its content only when size changes (user scales main window) @@ -34,130 +32,127 @@ */ class NOOTKACORE_EXPORT TguitarBg : public TcommonInstrument { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(int fretWidth READ fretWidth NOTIFY fretWidthChanged) - Q_PROPERTY(int stringsGap READ stringsGap NOTIFY stringsGapChanged) - Q_PROPERTY(QPointF fingerPos READ fingerPos NOTIFY fingerPosChanged) - Q_PROPERTY(int string READ currentString NOTIFY stringChanged) - Q_PROPERTY(qreal xiiFret READ xiiFret NOTIFY stringsGapChanged) - Q_PROPERTY(QRect fbRect READ fbRect NOTIFY stringsGapChanged) - Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) - + Q_PROPERTY(int fretWidth READ fretWidth NOTIFY fretWidthChanged) + Q_PROPERTY(int stringsGap READ stringsGap NOTIFY stringsGapChanged) + Q_PROPERTY(QPointF fingerPos READ fingerPos NOTIFY fingerPosChanged) + Q_PROPERTY(int string READ currentString NOTIFY stringChanged) + Q_PROPERTY(qreal xiiFret READ xiiFret NOTIFY stringsGapChanged) + Q_PROPERTY(QRect fbRect READ fbRect NOTIFY stringsGapChanged) + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) public: - TguitarBg(QQuickItem* parent = nullptr); + TguitarBg(QQuickItem *parent = nullptr); - short currentString() const { return m_curStr; } - short currentFret() const { return m_curFret; } + short currentString() const { return m_curStr; } + short currentFret() const { return m_curFret; } - /** - * Average width of fret - */ - int fretWidth() const { return m_fretWidth; } + /** + * Average width of fret + */ + int fretWidth() const { return m_fretWidth; } - /** - * Distance between strings - */ - int stringsGap() const { return m_strGap; } + /** + * Distance between strings + */ + int stringsGap() const { return m_strGap; } - QPointF fingerPos() const { return m_fingerPos; } + QPointF fingerPos() const { return m_fingerPos; } - void setNote(const Tnote& n, quint32 noteDataValue = 255) override; + void setNote(const Tnote &n, quint32 noteDataValue = 255) override; - void setFingerPos(const TfingerPos& fp); + void setFingerPos(const TfingerPos &fp); - void paint(QPainter* painter) override; + void paint(QPainter *painter) override; - void askQuestion(const Tnote& n, quint32 noteDataValue) override; + void askQuestion(const Tnote &n, quint32 noteDataValue) override; - void highlightAnswer(const Tnote & n, quint32 noteData) override; + void highlightAnswer(const Tnote &n, quint32 noteData) override; - int technical() override { return static_cast<int>(m_selectedPos.data()); } + int technical() override { return static_cast<int>(m_selectedPos.data()); } - /** - * Guitar fingerboard rectangle - */ - QRect fbRect() const { return m_fbRect; } + /** + * Guitar fingerboard rectangle + */ + QRect fbRect() const { return m_fbRect; } - bool pressed() const { return m_pressed; } + bool pressed() const { return m_pressed; } - /** - * Returns scene coordinates of given guitar position (between bars) - */ - QPointF fretToPos(const TfingerPos &pos) const; + /** + * Returns scene coordinates of given guitar position (between bars) + */ + QPointF fretToPos(const TfingerPos &pos) const; - qreal xiiFret() const; + qreal xiiFret() const; - Q_INVOKABLE qreal strWidth(int str) const { return m_strWidth[str]; } + Q_INVOKABLE qreal strWidth(int str) const { return m_strWidth[str]; } - /** - * Updates fingerboard to actual settings (guitar type string/fret number, marks) - */ - void updateGuitar(); + /** + * Updates fingerboard to actual settings (guitar type string/fret number, marks) + */ + void updateGuitar(); - void markSelected(const QColor & markColor) override; + void markSelected(const QColor &markColor) override; - void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor& textColor) override; + void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) override; - void correct(const Tnote & n, quint32 noteData) override; + void correct(const Tnote &n, quint32 noteData) override; - Q_INVOKABLE void finishCorrectAnim(); + Q_INVOKABLE void finishCorrectAnim(); - Q_INVOKABLE void applyCorrect() override; + Q_INVOKABLE void applyCorrect() override; - /** - * Simulates mouse press/touch at given position. - * Simply calculates what note is at that place on the fretboard - * and sets it - */ - Q_INVOKABLE void pressedAt(qreal px, qreal py); + /** + * Simulates mouse press/touch at given position. + * Simply calculates what note is at that place on the fretboard + * and sets it + */ + Q_INVOKABLE void pressedAt(qreal px, qreal py); signals: - void fretWidthChanged(); - void stringsGapChanged(); - void fingerPosChanged(); - void stringChanged(); - void pressedChanged(); - void clearGuitar(); - void noteWasSet(); + void fretWidthChanged(); + void stringsGapChanged(); + void fingerPosChanged(); + void stringChanged(); + void pressedChanged(); + void clearGuitar(); + void noteWasSet(); protected: - void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; - void hoverMoveEvent(QHoverEvent* event) override; - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent (QMouseEvent* event) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void hoverMoveEvent(QHoverEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; - void paintFingerAtPoint(QPoint p); + void paintFingerAtPoint(QPoint p); - /** - * Determines string width by its note pitch. Sets @p loNote & @p hiNote - */ - void setTune(); + /** + * Determines string width by its note pitch. Sets @p loNote & @p hiNote + */ + void setTune(); - void setPressed(bool pr); + void setPressed(bool pr); private: - QRect m_fbRect; /**< Represents top left positions and size of a fingerboard */ - int m_strGap; /**< Distance between strings */ - int m_fretWidth; /**< Average width of fret */ - int m_lastFret; /**< Position of the last fret (in whole widget coordinates) */ - int m_fretsPos[24]; /**< @p fretsPos stores X positions of frets in global widget coordinates */ - short m_curStr, m_curFret; /**< Actual position of cursor over the guitar in strings/frets coordinates */ - qreal m_strWidth[6]; /**< Array of each string width. The width depends on fretboard height. */ - qreal m_widthFromPitch[6]; /**< Base values from which @p m_strWidth is calculated determined from tune. */ - QColor m_strColors[6]; - - bool m_pressed = false; - QPointF m_fingerPos; - QQuickItem *m_fingerItems[6]; - QQuickItem *m_stringItems[6]; - QQuickItem *m_highlightedString = nullptr; - quint32 m_latestHighlightedData = 255; /**< Store technical data to properly resize highlighted string */ - TfingerPos m_selectedPos, m_goodPos; - QPoint m_mouseStartPos; - + QRect m_fbRect; /**< Represents top left positions and size of a fingerboard */ + int m_strGap; /**< Distance between strings */ + int m_fretWidth; /**< Average width of fret */ + int m_lastFret; /**< Position of the last fret (in whole widget coordinates) */ + int m_fretsPos[24]; /**< @p fretsPos stores X positions of frets in global widget coordinates */ + short m_curStr, m_curFret; /**< Actual position of cursor over the guitar in strings/frets coordinates */ + qreal m_strWidth[6]; /**< Array of each string width. The width depends on fretboard height. */ + qreal m_widthFromPitch[6]; /**< Base values from which @p m_strWidth is calculated determined from tune. */ + QColor m_strColors[6]; + + bool m_pressed = false; + QPointF m_fingerPos; + QQuickItem *m_fingerItems[6]; + QQuickItem *m_stringItems[6]; + QQuickItem *m_highlightedString = nullptr; + quint32 m_latestHighlightedData = 255; /**< Store technical data to properly resize highlighted string */ + TfingerPos m_selectedPos, m_goodPos; + QPoint m_mouseStartPos; }; #endif // TGUITARBG_H diff --git a/src/libs/core/instruments/tpianobg.cpp b/src/libs/core/instruments/tpianobg.cpp index ab19a34572b4c321e2810db4b1d9507e0e90e354..9213d2fab2937601bfdbd10794e860075db741ab 100644 --- a/src/libs/core/instruments/tpianobg.cpp +++ b/src/libs/core/instruments/tpianobg.cpp @@ -17,178 +17,178 @@ ***************************************************************************/ #include "tpianobg.h" -#include "tpath.h" #include "tglobals.h" +#include "tpath.h" -#include <QtGui/qpainter.h> -#include <QtGui/qguiapplication.h> -#include <QtGui/qscreen.h> #include <QtCore/qmath.h> #include <QtCore/qtimer.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qpainter.h> +#include <QtGui/qscreen.h> -#include <QtCore/qdebug.h> #include "checktime.h" +#include <QtCore/qdebug.h> - -TpianoBg::TpianoBg(QQuickItem* parent) : - TcommonInstrument(parent), - m_keyWidth(32.0), - m_firstOctave(-3) +TpianoBg::TpianoBg(QQuickItem *parent) + : TcommonInstrument(parent) + , m_keyWidth(32.0) + , m_firstOctave(-3) { } - -TpianoBg::~TpianoBg() {} - - -void TpianoBg::setKeyWidth(qreal kw) { - if (m_keyWidth != kw) { - m_keyWidth = kw; - calculateMetrics(width()); - } +TpianoBg::~TpianoBg() +{ } - -void TpianoBg::setNote(const Tnote& n, quint32 noteDataValue) { - Q_UNUSED(noteDataValue) - if (!p_note.compareNotes(n)) { - int keyNr = -1; - bool isWhite = true; - if (n.isValid()) { - Tnote sharpNote = Tnote(n.chromatic()); - int o = sharpNote.octave() - m_firstOctave; - isWhite = sharpNote.alter() == 0; - keyNr = o * 7 + sharpNote.note() - (isWhite ? 1 : 0); +void TpianoBg::setKeyWidth(qreal kw) +{ + if (m_keyWidth != kw) { + m_keyWidth = kw; + calculateMetrics(width()); } - p_note = n; - emit wantKeyToSelect(keyNr, isWhite); - } } - -void TpianoBg::askQuestion(const Tnote& n, quint32 noteDataValue) { - Q_UNUSED(noteDataValue) - setNote(n); +void TpianoBg::setNote(const Tnote &n, quint32 noteDataValue) +{ + Q_UNUSED(noteDataValue) + if (!p_note.compareNotes(n)) { + int keyNr = -1; + bool isWhite = true; + if (n.isValid()) { + Tnote sharpNote = Tnote(n.chromatic()); + int o = sharpNote.octave() - m_firstOctave; + isWhite = sharpNote.alter() == 0; + keyNr = o * 7 + sharpNote.note() - (isWhite ? 1 : 0); + } + p_note = n; + emit wantKeyToSelect(keyNr, isWhite); + } } - -void TpianoBg::setFirstOctave(int firstO) { - auto fo = static_cast<char>(firstO); - if (fo != m_firstOctave) { - m_firstOctave = fo; - emit firstOctaveChanged(); - } +void TpianoBg::askQuestion(const Tnote &n, quint32 noteDataValue) +{ + Q_UNUSED(noteDataValue) + setNote(n); } - -void TpianoBg::markSelected(const QColor& markColor) { - int borderWidth = qRound(m_keyWidth / (markColor.alpha() ? 8.0 : 16.0)); - markBorder(m_keyHighlight, borderWidth, markColor); +void TpianoBg::setFirstOctave(int firstO) +{ + auto fo = static_cast<char>(firstO); + if (fo != m_firstOctave) { + m_firstOctave = fo; + emit firstOctaveChanged(); + } } - -void TpianoBg::applyCorrect() { - int keyNr = -1; - bool isWhite = true; - Tnote sharpNote = Tnote(p_note.chromatic()); - int o = sharpNote.octave() - m_firstOctave; - isWhite = sharpNote.alter() == 0; - keyNr = o * 7 + sharpNote.note() - (isWhite ? 1 : 0); - emit wantKeyToSelect(keyNr, isWhite); - markSelected(GLOB->correctColor()); - if (!p_extraName.isEmpty()) { - p_extraName = QStringLiteral(" "); // hide name of wrong pointed note - // HACK: but do not clear extra name text - finish correction routines depend on it - emit wantNoteName(p_extraName, QVariant()); - } +void TpianoBg::markSelected(const QColor &markColor) +{ + int borderWidth = qRound(m_keyWidth / (markColor.alpha() ? 8.0 : 16.0)); + markBorder(m_keyHighlight, borderWidth, markColor); } - -void TpianoBg::correct(const Tnote& n, quint32 noteData) { - Q_UNUSED(noteData) - p_wrongItem = m_keyHighlight; - p_note = n; - p_goodItem = m_keyHighlight; - emit correctInstrument(); +void TpianoBg::applyCorrect() +{ + int keyNr = -1; + bool isWhite = true; + Tnote sharpNote = Tnote(p_note.chromatic()); + int o = sharpNote.octave() - m_firstOctave; + isWhite = sharpNote.alter() == 0; + keyNr = o * 7 + sharpNote.note() - (isWhite ? 1 : 0); + emit wantKeyToSelect(keyNr, isWhite); + markSelected(GLOB->correctColor()); + if (!p_extraName.isEmpty()) { + p_extraName = QStringLiteral(" "); // hide name of wrong pointed note + // HACK: but do not clear extra name text - finish correction routines depend on it + emit wantNoteName(p_extraName, QVariant()); + } } - -QString TpianoBg::octaveName(int oNr) const { - return Tnote::shortOctaveName(oNr); +void TpianoBg::correct(const Tnote &n, quint32 noteData) +{ + Q_UNUSED(noteData) + p_wrongItem = m_keyHighlight; + p_note = n; + p_goodItem = m_keyHighlight; + emit correctInstrument(); } - -int TpianoBg::zoomViewX(qreal xPos, qreal zoomKeyW) { - int k = qBound(1, qFloor((xPos - m_margin) / m_keyWidth), m_keysNumber); // key number - qreal o = static_cast<qreal>(qBound(0, k / 7, m_keysNumber / 7)) + 0.15; // octave - return qBound(0.0, (o * (width() - zoomKeyW * 7.0)) / static_cast<qreal>(m_keysNumber / 7), width() - zoomKeyW * 7.0); +QString TpianoBg::octaveName(int oNr) const +{ + return Tnote::shortOctaveName(oNr); } - -void TpianoBg::selectKey(QQuickItem* keyItem) { - if (keyItem != m_selectedKey) { - m_selectedKey = keyItem; - emit selectedKeyChanged(); - } +int TpianoBg::zoomViewX(qreal xPos, qreal zoomKeyW) +{ + int k = qBound(1, qFloor((xPos - m_margin) / m_keyWidth), m_keysNumber); // key number + qreal o = static_cast<qreal>(qBound(0, k / 7, m_keysNumber / 7)) + 0.15; // octave + return qBound(0.0, (o * (width() - zoomKeyW * 7.0)) / static_cast<qreal>(m_keysNumber / 7), width() - zoomKeyW * 7.0); } - -void TpianoBg::setSelectedKey(QQuickItem* it) { - selectKey(it); - if (!m_readOnly && m_selectedKey) { - int keyNr = m_selectedKey->property("nr").toInt(); - Tnote newNote(static_cast<char>(keyNr % 7 + 1), m_firstOctave + static_cast<char>(keyNr / 7), 0); - if (m_selectedKey->z() > 0) { // black key - newNote.setNote(newNote.note() - 1); - newNote.setAlter(Tnote::e_Sharp); +void TpianoBg::selectKey(QQuickItem *keyItem) +{ + if (keyItem != m_selectedKey) { + m_selectedKey = keyItem; + emit selectedKeyChanged(); } - p_note = newNote; - emit noteChanged(); - } } - -void TpianoBg::setKeyHighlight(QQuickItem* hi) { - m_keyHighlight = hi; +void TpianoBg::setSelectedKey(QQuickItem *it) +{ + selectKey(it); + if (!m_readOnly && m_selectedKey) { + int keyNr = m_selectedKey->property("nr").toInt(); + Tnote newNote(static_cast<char>(keyNr % 7 + 1), m_firstOctave + static_cast<char>(keyNr / 7), 0); + if (m_selectedKey->z() > 0) { // black key + newNote.setNote(newNote.note() - 1); + newNote.setAlter(Tnote::e_Sharp); + } + p_note = newNote; + emit noteChanged(); + } } - -void TpianoBg::setAmbitus(const Tnote& loNote, const Tnote& hiNote) { - m_loNote = loNote; - m_hiNote = hiNote; - setFirstOctave(m_loNote.octave()); - calculateMetrics(width()); +void TpianoBg::setKeyHighlight(QQuickItem *hi) +{ + m_keyHighlight = hi; } - -void TpianoBg::showNoteName(Tnote::EnameStyle st, const Tnote& n, quint32 techn, const QColor& textColor) { - TcommonInstrument::showNoteName(st, n, techn, textColor); - emit wantNoteName(p_extraName, QVariant::fromValue(m_selectedKey)); +void TpianoBg::setAmbitus(const Tnote &loNote, const Tnote &hiNote) +{ + m_loNote = loNote; + m_hiNote = hiNote; + setFirstOctave(m_loNote.octave()); + calculateMetrics(width()); } +void TpianoBg::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) +{ + TcommonInstrument::showNoteName(st, n, techn, textColor); + emit wantNoteName(p_extraName, QVariant::fromValue(m_selectedKey)); +} -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -void TpianoBg::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) { - if (oldGeometry.width() != newGeometry.width() || oldGeometry.height() != newGeometry.height()) { - calculateMetrics(qFloor(newGeometry.width())); - emit widthChanged(); - emit heightChanged(); - } +void TpianoBg::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (oldGeometry.width() != newGeometry.width() || oldGeometry.height() != newGeometry.height()) { + calculateMetrics(qFloor(newGeometry.width())); + emit widthChanged(); + emit heightChanged(); + } } - -void TpianoBg::calculateMetrics(qreal newWidth) { - int oldKeysNr = m_keysNumber; - qreal oldMargin = m_margin; - qreal oldKeyW = m_keyWidth; - m_keysNumber = static_cast<int>(m_hiNote.octave() - m_loNote.octave() + 1) * 7; - m_keyWidth = newWidth / static_cast<qreal>(m_keysNumber + 1); - m_margin = (newWidth - m_keysNumber * m_keyWidth) / 2.0; - if (oldKeysNr != m_keysNumber) - emit keysNumberChanged(); - if (m_keyWidth != oldKeyW || oldMargin != m_margin) - emit keyWidthChanged(); +void TpianoBg::calculateMetrics(qreal newWidth) +{ + int oldKeysNr = m_keysNumber; + qreal oldMargin = m_margin; + qreal oldKeyW = m_keyWidth; + m_keysNumber = static_cast<int>(m_hiNote.octave() - m_loNote.octave() + 1) * 7; + m_keyWidth = newWidth / static_cast<qreal>(m_keysNumber + 1); + m_margin = (newWidth - m_keysNumber * m_keyWidth) / 2.0; + if (oldKeysNr != m_keysNumber) + emit keysNumberChanged(); + if (m_keyWidth != oldKeyW || oldMargin != m_margin) + emit keyWidthChanged(); } diff --git a/src/libs/core/instruments/tpianobg.h b/src/libs/core/instruments/tpianobg.h index cc11d3d39cce5dca929f83b2d20fa91cbbb23c50..ed4bfca7cd7fa10f0dba12210839bc01ee0c5f65 100644 --- a/src/libs/core/instruments/tpianobg.h +++ b/src/libs/core/instruments/tpianobg.h @@ -19,96 +19,93 @@ #ifndef TPIANOBG_H #define TPIANOBG_H - #include "tcommoninstrument.h" - /** * The C++ logic of piano component and its painted background */ class NOOTKACORE_EXPORT TpianoBg : public TcommonInstrument { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(qreal keyWidth READ keyWidth WRITE setKeyWidth NOTIFY keyWidthChanged) - Q_PROPERTY(int firstOctave READ firstOctave WRITE setFirstOctave NOTIFY firstOctaveChanged) - Q_PROPERTY(int keysNumber READ keysNumber NOTIFY keysNumberChanged) - Q_PROPERTY(QQuickItem* selectedKey READ selectedKey WRITE setSelectedKey NOTIFY selectedKeyChanged) - Q_PROPERTY(QQuickItem* keyHighlight READ keyHighlight WRITE setKeyHighlight) - Q_PROPERTY(qreal margin READ margin NOTIFY keyWidthChanged) + Q_PROPERTY(qreal keyWidth READ keyWidth WRITE setKeyWidth NOTIFY keyWidthChanged) + Q_PROPERTY(int firstOctave READ firstOctave WRITE setFirstOctave NOTIFY firstOctaveChanged) + Q_PROPERTY(int keysNumber READ keysNumber NOTIFY keysNumberChanged) + Q_PROPERTY(QQuickItem *selectedKey READ selectedKey WRITE setSelectedKey NOTIFY selectedKeyChanged) + Q_PROPERTY(QQuickItem *keyHighlight READ keyHighlight WRITE setKeyHighlight) + Q_PROPERTY(qreal margin READ margin NOTIFY keyWidthChanged) public: - explicit TpianoBg(QQuickItem* parent = nullptr); - ~TpianoBg() override; + explicit TpianoBg(QQuickItem *parent = nullptr); + ~TpianoBg() override; - qreal keyWidth() const { return m_keyWidth; } - void setKeyWidth(qreal kw); + qreal keyWidth() const { return m_keyWidth; } + void setKeyWidth(qreal kw); - void setNote(const Tnote& n, quint32 noteDataValue = NO_TECHNICALS) override; + void setNote(const Tnote &n, quint32 noteDataValue = NO_TECHNICALS) override; - void askQuestion(const Tnote& n, quint32 noteDataValue) override; + void askQuestion(const Tnote &n, quint32 noteDataValue) override; - void highlightAnswer(const Tnote&, quint32) override {} + void highlightAnswer(const Tnote &, quint32) override { } - int technical() override { return NO_TECHNICALS; } // Fake - piano has no extra note data + int technical() override { return NO_TECHNICALS; } // Fake - piano has no extra note data - int firstOctave() const { return static_cast<int>(m_firstOctave); } - void setFirstOctave(int firstO); + int firstOctave() const { return static_cast<int>(m_firstOctave); } + void setFirstOctave(int firstO); - void paint(QPainter*) override {} + void paint(QPainter *) override { } - void markSelected(const QColor & markColor) override; + void markSelected(const QColor &markColor) override; - Q_INVOKABLE void applyCorrect() override; + Q_INVOKABLE void applyCorrect() override; - void correct(const Tnote& n, quint32 noteData) override; + void correct(const Tnote &n, quint32 noteData) override; - int keysNumber() const { return m_keysNumber; } - qreal margin() const { return m_margin; } + int keysNumber() const { return m_keysNumber; } + qreal margin() const { return m_margin; } - Q_INVOKABLE QString octaveName(int oNr) const; + Q_INVOKABLE QString octaveName(int oNr) const; - /** - * Calculates zoom view X coordinate from clicked @p xPos of piano widget. - * @p zoomKeyW is the width of piano key in zoomed preview - */ - Q_INVOKABLE int zoomViewX(qreal xPos, qreal zoomKeyW); + /** + * Calculates zoom view X coordinate from clicked @p xPos of piano widget. + * @p zoomKeyW is the width of piano key in zoomed preview + */ + Q_INVOKABLE int zoomViewX(qreal xPos, qreal zoomKeyW); - QQuickItem* selectedKey() { return m_selectedKey; } - void setSelectedKey(QQuickItem* it); + QQuickItem *selectedKey() { return m_selectedKey; } + void setSelectedKey(QQuickItem *it); - QQuickItem* keyHighlight() { return m_keyHighlight; } - void setKeyHighlight(QQuickItem* hi); + QQuickItem *keyHighlight() { return m_keyHighlight; } + void setKeyHighlight(QQuickItem *hi); - Q_INVOKABLE void selectKey(QQuickItem* keyItem); + Q_INVOKABLE void selectKey(QQuickItem *keyItem); - Q_INVOKABLE void setAmbitus(const Tnote& loNote, const Tnote& hiNote); + Q_INVOKABLE void setAmbitus(const Tnote &loNote, const Tnote &hiNote); - void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor& textColor) override; + void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) override; signals: - void keyWidthChanged(); - void keysNumberChanged(); - void firstOctaveChanged(); - void selectedKeyChanged(); - void wantKeyToSelect(int k, bool isWhite); + void keyWidthChanged(); + void keysNumberChanged(); + void firstOctaveChanged(); + void selectedKeyChanged(); + void wantKeyToSelect(int k, bool isWhite); protected: - void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; private: - void calculateMetrics(qreal newWidth); + void calculateMetrics(qreal newWidth); private: - int m_keysNumber = 0; - qreal m_keyWidth; - qreal m_margin; - char m_firstOctave; - bool m_readOnly = false; - QQuickItem *m_selectedKey = nullptr; - QQuickItem *m_keyHighlight = nullptr; /**< Key highlighting item (Rectangle) */ - Tnote m_loNote, m_hiNote; + int m_keysNumber = 0; + qreal m_keyWidth; + qreal m_margin; + char m_firstOctave; + bool m_readOnly = false; + QQuickItem *m_selectedKey = nullptr; + QQuickItem *m_keyHighlight = nullptr; /**< Key highlighting item (Rectangle) */ + Tnote m_loNote, m_hiNote; }; #endif // TPIANOBG_H diff --git a/src/libs/core/instruments/tsaxbg.cpp b/src/libs/core/instruments/tsaxbg.cpp index fe1aeb3c397b7e96d2b35c349d9bda0417df8f2e..03821692f83368cc3f44e922cecb87a47024c1d1 100644 --- a/src/libs/core/instruments/tsaxbg.cpp +++ b/src/libs/core/instruments/tsaxbg.cpp @@ -23,140 +23,137 @@ #include <QtCore/qdebug.h> - -#define LOWEST_NOTE (11) // lowest note for saxophone notation is a#/bb in small octave +#define LOWEST_NOTE (11) // lowest note for saxophone notation is a#/bb in small octave #define HIGHEST_NOTE (49) // highest note for saxophone notation is f3 in three-lines octave #define SUPPORTED_NOTES (39) // number of notes supported by fingering chart - -TsaxBg::TsaxBg(QQuickItem* parent) : - TcommonInstrument(parent) +TsaxBg::TsaxBg(QQuickItem *parent) + : TcommonInstrument(parent) { - m_notesArray = new quint32[SUPPORTED_NOTES]; - m_notesArray[0] = 4227322; // a#/bb - m_notesArray[1] = 4202746; // b - m_notesArray[2] = 4194554; // c1 - m_notesArray[3] = 4210938; // c#1/db1 - m_notesArray[4] = 250; // d1 - m_notesArray[5] = 2097402; // d#1/eb1 - m_notesArray[6] = 122; // e1 - m_notesArray[7] = 58; // f1 - m_notesArray[8] = 90; // f#1/gb1 or 524346 - m_notesArray[9] = 26; // g1 - m_notesArray[10] = 4122; // g#1/ab1 - m_notesArray[11] = 10; // a1 or 4106 - m_notesArray[12] = 262154; // a#1/bb1 or 6 or 34 - m_notesArray[13] = 2; // b1 or 262146 - m_notesArray[14] = 8; // c2 or 131074 - m_notesArray[15] = 0; // c#2 or 2064 - m_notesArray[16] = 2298; // d2 or 10490 - m_notesArray[17] = 2099450; // d#2/eb2 - m_notesArray[18] = 2170; // e2 - m_notesArray[19] = 2106; // f2 - m_notesArray[20] = 2138; // f#2/gb2 or 526394 - m_notesArray[21] = 2074; // g2 - m_notesArray[22] = 6170; // g#2/ab2 - m_notesArray[23] = 2058; // a2 - m_notesArray[24] = 264202; // a#2/bb2 or 2054 or 2082 - m_notesArray[25] = 2050; // b2 - m_notesArray[26] = 2056; // c3 or 133122 - m_notesArray[27] = 2048; // c#3/db3 or 2144 - m_notesArray[28] = 3072; // d3 - m_notesArray[29] = 3584; // d#3/eb3 - m_notesArray[30] = 69120; // e3 or 2073 - m_notesArray[31] = 69376; // f3 or 2057 - m_notesArray[32] = 264225; // f#3/g#3 or 593664 - m_notesArray[33] = 264193; // g3 or 2049 - m_notesArray[34] = 133170; // g#3/ab3 or 788488 - m_notesArray[35] = 2296; // a3 or 133144 - m_notesArray[36] = 2160; // a#3/bb3 or 2552 - m_notesArray[37] = 2320; // b3 or 133656 - m_notesArray[38] = 2832; // c3 or 2099378 + m_notesArray = new quint32[SUPPORTED_NOTES]; + m_notesArray[0] = 4227322; // a#/bb + m_notesArray[1] = 4202746; // b + m_notesArray[2] = 4194554; // c1 + m_notesArray[3] = 4210938; // c#1/db1 + m_notesArray[4] = 250; // d1 + m_notesArray[5] = 2097402; // d#1/eb1 + m_notesArray[6] = 122; // e1 + m_notesArray[7] = 58; // f1 + m_notesArray[8] = 90; // f#1/gb1 or 524346 + m_notesArray[9] = 26; // g1 + m_notesArray[10] = 4122; // g#1/ab1 + m_notesArray[11] = 10; // a1 or 4106 + m_notesArray[12] = 262154; // a#1/bb1 or 6 or 34 + m_notesArray[13] = 2; // b1 or 262146 + m_notesArray[14] = 8; // c2 or 131074 + m_notesArray[15] = 0; // c#2 or 2064 + m_notesArray[16] = 2298; // d2 or 10490 + m_notesArray[17] = 2099450; // d#2/eb2 + m_notesArray[18] = 2170; // e2 + m_notesArray[19] = 2106; // f2 + m_notesArray[20] = 2138; // f#2/gb2 or 526394 + m_notesArray[21] = 2074; // g2 + m_notesArray[22] = 6170; // g#2/ab2 + m_notesArray[23] = 2058; // a2 + m_notesArray[24] = 264202; // a#2/bb2 or 2054 or 2082 + m_notesArray[25] = 2050; // b2 + m_notesArray[26] = 2056; // c3 or 133122 + m_notesArray[27] = 2048; // c#3/db3 or 2144 + m_notesArray[28] = 3072; // d3 + m_notesArray[29] = 3584; // d#3/eb3 + m_notesArray[30] = 69120; // e3 or 2073 + m_notesArray[31] = 69376; // f3 or 2057 + m_notesArray[32] = 264225; // f#3/g#3 or 593664 + m_notesArray[33] = 264193; // g3 or 2049 + m_notesArray[34] = 133170; // g#3/ab3 or 788488 + m_notesArray[35] = 2296; // a3 or 133144 + m_notesArray[36] = 2160; // a#3/bb3 or 2552 + m_notesArray[37] = 2320; // b3 or 133656 + m_notesArray[38] = 2832; // c3 or 2099378 } - TsaxBg::~TsaxBg() { - delete[] m_notesArray; + delete[] m_notesArray; } - -void TsaxBg::setNote(const Tnote& n, quint32 noteDataValue) { - Q_UNUSED(noteDataValue) - bool out = false; - if (n.isValid()) { - int ch = n.chromatic(); - out = ch < LOWEST_NOTE || ch > HIGHEST_NOTE; -// qDebug() << "setNote" << n.toText() << ch; - if (!p_note.isValid() || ch != p_note.chromatic()) { - p_note.setChromatic(ch); - if (ch >= LOWEST_NOTE && ch <= HIGHEST_NOTE) { - ch -= LOWEST_NOTE; - m_fingeringId = m_notesArray[ch]; - } else +void TsaxBg::setNote(const Tnote &n, quint32 noteDataValue) +{ + Q_UNUSED(noteDataValue) + bool out = false; + if (n.isValid()) { + int ch = n.chromatic(); + out = ch < LOWEST_NOTE || ch > HIGHEST_NOTE; + // qDebug() << "setNote" << n.toText() << ch; + if (!p_note.isValid() || ch != p_note.chromatic()) { + p_note.setChromatic(ch); + if (ch >= LOWEST_NOTE && ch <= HIGHEST_NOTE) { + ch -= LOWEST_NOTE; + m_fingeringId = m_notesArray[ch]; + } else + m_fingeringId = 0; + emit fingeringIdChanged(); + } + } else { + if (m_fingeringId) { m_fingeringId = 0; - emit fingeringIdChanged(); - } - } else { - if (m_fingeringId) { - m_fingeringId = 0; - p_note.setNote(0); - emit fingeringIdChanged(); - } - } - setOutOfScale(out); + p_note.setNote(0); + emit fingeringIdChanged(); + } + } + setOutOfScale(out); } - -void TsaxBg::askQuestion(const Tnote& n, quint32 noteDataValue) { - Q_UNUSED(noteDataValue) - setNote(n); +void TsaxBg::askQuestion(const Tnote &n, quint32 noteDataValue) +{ + Q_UNUSED(noteDataValue) + setNote(n); } - -void TsaxBg::setFlapNumber(int fNr) { - quint32 flatPower = qFloor(qPow(2.0, fNr)); - bool flapOn = m_fingeringId & flatPower; - if (flapOn) - m_fingeringId &= ~flatPower; - else - m_fingeringId |= flatPower; -// qDebug() << "setFlapNumber" << fNr << m_fingeringId; - emit fingeringIdChanged(); - for (int f = 0; f < SUPPORTED_NOTES; ++f) { - if (m_notesArray[f] == m_fingeringId) { - p_note.setChromatic(LOWEST_NOTE + f); - emit noteChanged(); - break; +void TsaxBg::setFlapNumber(int fNr) +{ + quint32 flatPower = qFloor(qPow(2.0, fNr)); + bool flapOn = m_fingeringId & flatPower; + if (flapOn) + m_fingeringId &= ~flatPower; + else + m_fingeringId |= flatPower; + // qDebug() << "setFlapNumber" << fNr << m_fingeringId; + emit fingeringIdChanged(); + for (int f = 0; f < SUPPORTED_NOTES; ++f) { + if (m_notesArray[f] == m_fingeringId) { + p_note.setChromatic(LOWEST_NOTE + f); + emit noteChanged(); + break; + } } - } } - -void TsaxBg::markSelected(const QColor& mColor) { - m_markColor = mColor; - emit markColorChanged(); +void TsaxBg::markSelected(const QColor &mColor) +{ + m_markColor = mColor; + emit markColorChanged(); } - -void TsaxBg::correct(const Tnote& n, quint32 noteData) { - Q_UNUSED(noteData) - m_goodNote = n; - emit correctInstrument(); +void TsaxBg::correct(const Tnote &n, quint32 noteData) +{ + Q_UNUSED(noteData) + m_goodNote = n; + emit correctInstrument(); } - -void TsaxBg::applyCorrect() { - setNote(m_goodNote); - if (!p_extraName.isEmpty()) { - p_extraName = QStringLiteral(" "); // hide name of wrong pointed note - // HACK: but do not clear extra name text - finish correction routines depend on it - emit wantNoteName(p_extraName, QVariant()); - } +void TsaxBg::applyCorrect() +{ + setNote(m_goodNote); + if (!p_extraName.isEmpty()) { + p_extraName = QStringLiteral(" "); // hide name of wrong pointed note + // HACK: but do not clear extra name text - finish correction routines depend on it + emit wantNoteName(p_extraName, QVariant()); + } } - -void TsaxBg::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) { - TcommonInstrument::showNoteName(st, n, techn, textColor); - emit wantNoteName(p_extraName, QVariant()); +void TsaxBg::showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) +{ + TcommonInstrument::showNoteName(st, n, techn, textColor); + emit wantNoteName(p_extraName, QVariant()); } diff --git a/src/libs/core/instruments/tsaxbg.h b/src/libs/core/instruments/tsaxbg.h index ff06c1d2248f442d4039f948b920289c42ee439a..07e05c11694498e6d432f7eee0f35c2294594584 100644 --- a/src/libs/core/instruments/tsaxbg.h +++ b/src/libs/core/instruments/tsaxbg.h @@ -19,12 +19,10 @@ #ifndef TSAXBG_H #define TSAXBG_H - -#include "tcommoninstrument.h" #include "music/tnote.h" +#include "tcommoninstrument.h" #include <QtQuick/qquickitem.h> - /** * @class TsaxBg defines fingering chart shapes of notes from a# (11) till c3 (49). * Every shape is represented by unique number obtained as a bit-mask of closed (1) or opened (0) saxophone flips. @@ -35,57 +33,55 @@ */ class NOOTKACORE_EXPORT TsaxBg : public TcommonInstrument { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(int flapNumber READ flapNumber WRITE setFlapNumber NOTIFY flapNumberChanged) - Q_PROPERTY(int fingeringId READ fingeringId NOTIFY fingeringIdChanged) - Q_PROPERTY(QColor markColor READ markColor NOTIFY markColorChanged) + Q_PROPERTY(int flapNumber READ flapNumber WRITE setFlapNumber NOTIFY flapNumberChanged) + Q_PROPERTY(int fingeringId READ fingeringId NOTIFY fingeringIdChanged) + Q_PROPERTY(QColor markColor READ markColor NOTIFY markColorChanged) public: - TsaxBg(QQuickItem* parent = nullptr); - ~TsaxBg(); + TsaxBg(QQuickItem *parent = nullptr); + ~TsaxBg(); - void setNote(const Tnote& n, quint32 noteDataValue = NO_TECHNICALS) override; + void setNote(const Tnote &n, quint32 noteDataValue = NO_TECHNICALS) override; - void askQuestion(const Tnote& n, quint32 noteDataValue) override; + void askQuestion(const Tnote &n, quint32 noteDataValue) override; - void highlightAnswer(const Tnote&, quint32) override {} + void highlightAnswer(const Tnote &, quint32) override { } - int technical() override { return NO_TECHNICALS; } // Fake - saxophone has no extra note data + int technical() override { return NO_TECHNICALS; } // Fake - saxophone has no extra note data - int flapNumber() const { return m_flapNumber; } + int flapNumber() const { return m_flapNumber; } - QColor markColor() const { return m_markColor; } + QColor markColor() const { return m_markColor; } - /** - * Every saxophone flaps when clicked sets this value to its number - */ - void setFlapNumber(int fNr); + /** + * Every saxophone flaps when clicked sets this value to its number + */ + void setFlapNumber(int fNr); - quint32 fingeringId() const { return m_fingeringId; } + quint32 fingeringId() const { return m_fingeringId; } - void paint(QPainter*) override {} + void paint(QPainter *) override { } - void markSelected(const QColor & mColor) override; + void markSelected(const QColor &mColor) override; - void correct(const Tnote & n, quint32 noteData) override; - Q_INVOKABLE void applyCorrect() override; + void correct(const Tnote &n, quint32 noteData) override; + Q_INVOKABLE void applyCorrect() override; - void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor& textColor) override; + void showNoteName(Tnote::EnameStyle st, const Tnote &n, quint32 techn, const QColor &textColor) override; signals: - void flapNumberChanged(); - void fingeringIdChanged(); - void markColorChanged(); - + void flapNumberChanged(); + void fingeringIdChanged(); + void markColorChanged(); private: - int m_flapNumber; - quint32 m_fingeringId = 0; - quint32 *m_notesArray; - QColor m_markColor = Qt::transparent; - Tnote m_goodNote; + int m_flapNumber; + quint32 m_fingeringId = 0; + quint32 *m_notesArray; + QColor m_markColor = Qt::transparent; + Tnote m_goodNote; }; #endif // TSAXBG_H diff --git a/src/libs/core/music/tchunk.cpp b/src/libs/core/music/tchunk.cpp index ac8791fa526d804f7bd0a1ced81c84f5c3805d5e..bfbf7026b64ca969a1c454c4b28b980ce1e7079f 100644 --- a/src/libs/core/music/tchunk.cpp +++ b/src/libs/core/music/tchunk.cpp @@ -16,27 +16,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tchunk.h" -#include <QtCore/qxmlstream.h> #include <QtCore/qdebug.h> +#include <QtCore/qxmlstream.h> - -Tchunk::Tchunk(const Tnote& pitch, const TfingerPos& fretPos) : - m_pitch(pitch) +Tchunk::Tchunk(const Tnote &pitch, const TfingerPos &fretPos) + : m_pitch(pitch) { - m_technical.setFingerPos(fretPos); + m_technical.setFingerPos(fretPos); } - -Tchunk::Tchunk(const Tnote& pitch, const Ttechnical& technical) : - m_pitch(pitch), - m_technical(technical) +Tchunk::Tchunk(const Tnote &pitch, const Ttechnical &technical) + : m_pitch(pitch) + , m_technical(technical) { } - /** * @p MusicXML <note> elements required order (possible used in Nootka) * <pitch> @@ -57,54 +53,54 @@ Tchunk::Tchunk(const Tnote& pitch, const Ttechnical& technical) : * <beam> (0 to 8 times) * <notations> (Zero or more times) */ -void Tchunk::toXml(QXmlStreamWriter& xml, int* staffNr) { - xml.writeStartElement(QLatin1String("note")); +void Tchunk::toXml(QXmlStreamWriter &xml, int *staffNr) +{ + xml.writeStartElement(QLatin1String("note")); if (m_pitch.isRest() || !m_pitch.isValid()) - xml.writeEmptyElement(QLatin1String("rest")); + xml.writeEmptyElement(QLatin1String("rest")); else - m_pitch.toXml(xml); // or <pitch> + m_pitch.toXml(xml); // or <pitch> xml.writeTextElement(QLatin1String("duration"), QString::number(m_pitch.rtm.isValid() ? m_pitch.duration() : Trhythm(Trhythm::Quarter).duration())); if (m_pitch.rtm.tie()) - tieToXml(xml, m_pitch.rtm.tie(), e_tie); + tieToXml(xml, m_pitch.rtm.tie(), e_tie); xml.writeTextElement(QLatin1String("type"), m_pitch.rtm.xmlType()); if (m_pitch.hasDot()) - xml.writeEmptyElement(QLatin1String("dot")); -// if (m_pitch.alter && !m_pitch.isRest() && m_pitch.isValid()) { -// QString accidentalTag; -// switch (m_pitch.alter) { -// case -2: accidentalTag = QStringLiteral("flat-flat"); break; -// case -1: accidentalTag = QStringLiteral("flat"); break; -// case 1: accidentalTag = QStringLiteral("sharp"); break; -// case 2: accidentalTag = QStringLiteral("double-sharp"); break; -// default: break; -// } -// if (!accidentalTag.isEmpty()) -// xml.writeTextElement(QLatin1String("accidental"), accidentalTag); -// } + xml.writeEmptyElement(QLatin1String("dot")); + // if (m_pitch.alter && !m_pitch.isRest() && m_pitch.isValid()) { + // QString accidentalTag; + // switch (m_pitch.alter) { + // case -2: accidentalTag = QStringLiteral("flat-flat"); break; + // case -1: accidentalTag = QStringLiteral("flat"); break; + // case 1: accidentalTag = QStringLiteral("sharp"); break; + // case 2: accidentalTag = QStringLiteral("double-sharp"); break; + // default: break; + // } + // if (!accidentalTag.isEmpty()) + // xml.writeTextElement(QLatin1String("accidental"), accidentalTag); + // } if (m_pitch.rhythm() == Trhythm::NoRhythm) { if (!m_pitch.isRest() && m_pitch.isValid()) - xml.writeTextElement(QLatin1String("stem"), QLatin1String("none")); + xml.writeTextElement(QLatin1String("stem"), QLatin1String("none")); } if (staffNr) - xml.writeTextElement(QLatin1String("staff"), QString::number(*staffNr)); + xml.writeTextElement(QLatin1String("staff"), QString::number(*staffNr)); if (m_pitch.rtm.beam()) { - xml.writeStartElement(QStringLiteral("beam")); + xml.writeStartElement(QStringLiteral("beam")); xml.writeAttribute(QStringLiteral("number"), QStringLiteral("1")); xml.writeCharacters(beamToString(m_pitch.rtm.beam())); - xml.writeEndElement(); // beam + xml.writeEndElement(); // beam } if (m_pitch.rtm.tie() || !m_technical.isEmpty()) { - xml.writeStartElement(QLatin1String("notations")); + xml.writeStartElement(QLatin1String("notations")); if (!m_technical.isEmpty()) - m_technical.toXml(xml); + m_technical.toXml(xml); if (m_pitch.rtm.tie()) - tieToXml(xml, m_pitch.rtm.tie(), e_tied); - xml.writeEndElement(); + tieToXml(xml, m_pitch.rtm.tie(), e_tied); + xml.writeEndElement(); } - xml.writeEndElement(); // note + xml.writeEndElement(); // note } - static bool tupletWarn = true, rtmWarn = true, moreDotsWarn = true, dot16Warn = true; /** @@ -113,123 +109,124 @@ static bool tupletWarn = true, rtmWarn = true, moreDotsWarn = true, dot16Warn = * Tie is recognized by <notations><tied type="" ></tied></notations> tag, <tie /> itself is ignored, * but during save, both tags are writing. */ -quint16 Tchunk::fromXml(QXmlStreamReader& xml, int* staffNr, int* voiceNr) { - quint16 ok = e_xmlOK; - int stNr = 1; - m_pitch.setRhythm(Trhythm(Trhythm::NoRhythm)); - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("grace")) { - ok |= e_xmlUnsupported | e_xmlIsGrace; - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("chord")) { - ok |= e_xmlUnsupported | e_xmlIsChord; - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("pitch")) - m_pitch.fromXml(xml); - else if (xml.name() == QLatin1String("type")) { - auto rtmType = xml.readElementText(); - m_pitch.setRhythmValue(rtmType.toStdString()); - if (!rtmType.isEmpty() && m_pitch.rhythm() == Trhythm::NoRhythm) { - if (rtmWarn) { - qDebug() << "[Tchunk] Unsupported rhythmic value:" << rtmType; - rtmWarn = false; +quint16 Tchunk::fromXml(QXmlStreamReader &xml, int *staffNr, int *voiceNr) +{ + quint16 ok = e_xmlOK; + int stNr = 1; + m_pitch.setRhythm(Trhythm(Trhythm::NoRhythm)); + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("grace")) { + ok |= e_xmlUnsupported | e_xmlIsGrace; + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("chord")) { + ok |= e_xmlUnsupported | e_xmlIsChord; + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("pitch")) + m_pitch.fromXml(xml); + else if (xml.name() == QLatin1String("type")) { + auto rtmType = xml.readElementText(); + m_pitch.setRhythmValue(rtmType.toStdString()); + if (!rtmType.isEmpty() && m_pitch.rhythm() == Trhythm::NoRhythm) { + if (rtmWarn) { + qDebug() << "[Tchunk] Unsupported rhythmic value:" << rtmType; + rtmWarn = false; + } + ok |= e_xmlUnsupported | e_xmlIsStrangeRtm; + } + } else if (xml.name() == QLatin1String("time-modification")) { + ok |= e_xmlUnsupported | e_xmlIsTupletStart; // no matter start/stop - unsupported anyway + if (tupletWarn) { + qDebug() << "[Tchunk] Tuplets are not supported."; + tupletWarn = false; + } + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("rest")) { + m_pitch.setRest(true); + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("dot")) { + if (m_pitch.rhythm() < Trhythm::Sixteenth) { + if (m_pitch.hasDot()) { + if (ok & e_xmlHasTwoDots) { + if (moreDotsWarn) { + qDebug() << "[Tchunk] More than two augmented dots are not supported!"; + moreDotsWarn = false; + } + } else if (m_pitch.rhythm() < Trhythm::Eighth) + ok |= e_xmlHasTwoDots; + } else + m_pitch.setDot(true); + } else { + if (dot16Warn) { + qDebug() << "[Tchunk] Sixteenth with dots are not supported!"; + dot16Warn = false; + } + ok |= e_xmlUnsupported | e_xmlIsStrangeRtm; + } + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("notations")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("technical")) + m_technical.fromXml(xml); + else if (xml.name() == QLatin1String("tied")) { + auto type = xml.attributes().value(QStringLiteral("type")); + Trhythm::Etie tie = Trhythm::e_noTie; + if (type == QLatin1String("start")) + tie = m_pitch.rtm.tie() ? Trhythm::e_tieCont : Trhythm::e_tieStart; // tie was set already - means tie starts and stops on this note + else if (type == QLatin1String("stop")) + tie = m_pitch.rtm.tie() ? Trhythm::e_tieCont : Trhythm::e_tieEnd; // tie was set already - means tie starts and stops on this note + m_pitch.rtm.setTie(tie); + xml.skipCurrentElement(); + // TODO: we are detecting tuplets in <time-modification> + // below tag is only for visualization, but may be useful to obtain tuplet start/stop + // } else if (xml.name() == QLatin1String("tuplet")) { + // auto type = xml.attributes().value(QStringLiteral("type")); + // if (type == QLatin1String("start")) + // ok |= e_xmlUnsupported | e_xmlIsTupletStart; + // else if (type == QLatin1String("stop")) + // ok |= e_xmlUnsupported | e_xmlIsTupletStop; + // else + // qDebug() << "[Tchunk] Error in XML tuplet type! Ignored!"; + // qDebug() << "[Tchunk] Tuplets are not supported."; + // xml.skipCurrentElement(); + } else + xml.skipCurrentElement(); } - ok |= e_xmlUnsupported | e_xmlIsStrangeRtm; - } - } else if (xml.name() == QLatin1String("time-modification")) { - ok |= e_xmlUnsupported | e_xmlIsTupletStart; // no matter start/stop - unsupported anyway - if (tupletWarn) { - qDebug() << "[Tchunk] Tuplets are not supported."; - tupletWarn = false; - } - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("rest")) { - m_pitch.setRest(true); - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("dot")) { - if (m_pitch.rhythm() < Trhythm::Sixteenth) { - if (m_pitch.hasDot()) { - if (ok & e_xmlHasTwoDots) { - if (moreDotsWarn) { - qDebug() << "[Tchunk] More than two augmented dots are not supported!"; - moreDotsWarn = false; - } - } else if (m_pitch.rhythm() < Trhythm::Eighth) - ok |= e_xmlHasTwoDots; - } else - m_pitch.setDot(true); - } else { - if (dot16Warn) { - qDebug() << "[Tchunk] Sixteenth with dots are not supported!"; - dot16Warn = false; - } - ok |= e_xmlUnsupported | e_xmlIsStrangeRtm; - } - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("notations")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("technical")) - m_technical.fromXml(xml); - else if (xml.name() == QLatin1String("tied")) { - auto type = xml.attributes().value(QStringLiteral("type")); - Trhythm::Etie tie = Trhythm::e_noTie; - if (type == QLatin1String("start")) - tie = m_pitch.rtm.tie() ? Trhythm::e_tieCont : Trhythm::e_tieStart; // tie was set already - means tie starts and stops on this note - else if (type == QLatin1String("stop")) - tie = m_pitch.rtm.tie() ? Trhythm::e_tieCont : Trhythm::e_tieEnd; // tie was set already - means tie starts and stops on this note - m_pitch.rtm.setTie(tie); - xml.skipCurrentElement(); - // TODO: we are detecting tuplets in <time-modification> - // below tag is only for visualization, but may be useful to obtain tuplet start/stop -// } else if (xml.name() == QLatin1String("tuplet")) { -// auto type = xml.attributes().value(QStringLiteral("type")); -// if (type == QLatin1String("start")) -// ok |= e_xmlUnsupported | e_xmlIsTupletStart; -// else if (type == QLatin1String("stop")) -// ok |= e_xmlUnsupported | e_xmlIsTupletStop; -// else -// qDebug() << "[Tchunk] Error in XML tuplet type! Ignored!"; -// qDebug() << "[Tchunk] Tuplets are not supported."; -// xml.skipCurrentElement(); - } else - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("voice")) { - int vNr = xml.readElementText().toInt(); - if (voiceNr) - *voiceNr = vNr; - else if (vNr != 1) - ok |= e_xmlUnsupported; - } else if (xml.name() == QLatin1String("staff")) - stNr = xml.readElementText().toInt(); - else - xml.skipCurrentElement(); - } - if (staffNr) - *staffNr = stNr; - return ok; + } else if (xml.name() == QLatin1String("voice")) { + int vNr = xml.readElementText().toInt(); + if (voiceNr) + *voiceNr = vNr; + else if (vNr != 1) + ok |= e_xmlUnsupported; + } else if (xml.name() == QLatin1String("staff")) + stNr = xml.readElementText().toInt(); + else + xml.skipCurrentElement(); + } + if (staffNr) + *staffNr = stNr; + return ok; } - -QString Tchunk::beamToString(Trhythm::Ebeam b) { - switch (b) { - case Trhythm::e_beamStart: return QStringLiteral("begin"); - case Trhythm::e_beamCont: return QStringLiteral("continue"); - case Trhythm::e_beamEnd: return QStringLiteral("end"); - default: return QString(); - } +QString Tchunk::beamToString(Trhythm::Ebeam b) +{ + switch (b) { + case Trhythm::e_beamStart: + return QStringLiteral("begin"); + case Trhythm::e_beamCont: + return QStringLiteral("continue"); + case Trhythm::e_beamEnd: + return QStringLiteral("end"); + default: + return QString(); + } } - -void Tchunk::tieToXml(QXmlStreamWriter& xml, Trhythm::Etie t, Tchunk::EtieTag tag) { - QString tieTag = tag == e_tie ? QLatin1String("tie") : QLatin1String("tied"); - xml.writeStartElement(tieTag); +void Tchunk::tieToXml(QXmlStreamWriter &xml, Trhythm::Etie t, Tchunk::EtieTag tag) +{ + QString tieTag = tag == e_tie ? QLatin1String("tie") : QLatin1String("tied"); + xml.writeStartElement(tieTag); xml.writeAttribute(QLatin1String("type"), t == Trhythm::e_tieStart ? QLatin1String("start") : QLatin1String("stop")); - xml.writeEndElement(); - if (t == Trhythm::e_tieCont) - tieToXml(xml, Trhythm::e_tieStart, tag); + xml.writeEndElement(); + if (t == Trhythm::e_tieCont) + tieToXml(xml, Trhythm::e_tieStart, tag); } - - - - diff --git a/src/libs/core/music/tchunk.h b/src/libs/core/music/tchunk.h old mode 100755 new mode 100644 index a8bd4b0ce68c00b141563e68f769cbfcd3bdc8c3..933c728de4108293e57f43b052585f22aad59203 --- a/src/libs/core/music/tchunk.h +++ b/src/libs/core/music/tchunk.h @@ -19,16 +19,13 @@ #ifndef TCHUNK_H #define TCHUNK_H - -#include <nootkacoreglobal.h> #include "tnote.h" #include "ttechnical.h" - +#include <nootkacoreglobal.h> class QXmlStreamReader; class QXmlStreamWriter; - /** * This class represent a note: * a pitch and rhythm described by @p p() (@p Tnote) @@ -38,101 +35,100 @@ class QXmlStreamWriter; */ class NOOTKACORE_EXPORT Tchunk { - public: - Tchunk(const Tnote& pitch, const TfingerPos& fretPos = TfingerPos()); - - Tchunk(const Tnote& pitch, const Ttechnical& technical); - - /** - * Default constructor - creates 'empty' note and position. - */ - Tchunk() {} - ~Tchunk() {} - - /** - * The note - */ - Tnote& p() { return m_pitch; } - - int duration() const { return m_pitch.duration(); } - - /** - * Position a note on a guitar (if any) - by default it is null - invalid - */ - TfingerPos& g() { return m_technical.fingerPos(); } - - /** - * Extra note data like guitar position, bow direction, staff position (upper/lower) and fingering - */ - Ttechnical& t() { return m_technical; } - - bool onUpperStaff() const { return m_technical.onUpperStaff(); } - void setOnUpperStaff(bool onUpper) { m_technical.setOnUpperStaff(onUpper); } - - Ttechnical::EbowDirection bowing() const { return m_technical.bowing(); } - void setBowing(Ttechnical::EbowDirection b) { m_technical.setBowing(b); } - - /** - * Finger number [0 - 5]. - * -1 is returned when undefined - */ - int finger() const { return m_technical.finger(); } - void setFinger(int fi) { m_technical.setFinger(fi); } - - /** - * Numeric value representing all extra note parameters - */ - quint32 technical() const { return m_technical.data(); } - void setTechnical(quint32 nd) { m_technical.setData(nd); } - - /** - * Returns @p TRUE when position on the guitar is valid. - */ - bool validPos() { return g().str() != 7; } - - /** - * If @p staffNr is set appropriate <staff>staffNr</staff> is added - */ - void toXml(QXmlStreamWriter& xml, int* staffNr = nullptr); - - enum EimportResult { - e_xmlOK = 0, - e_xmlUnsupported = 1, - e_xmlHasTwoDots = 2, - e_xmlIsChord = 4, - e_xmlIsGrace = 8, - e_xmlIsStrangeRtm = 16, - e_xmlIsTupletStart = 32, - e_xmlIsTupletStop = 64 - }; - - /** - * Trough @p staffNr (if set) is returned staff number the note belongs to. - */ - quint16 fromXml(QXmlStreamReader& xml, int* staffNr = nullptr, int* voiceNr = nullptr); - - /** - * Converts beam type into music XML compatible string - */ - static QString beamToString(Trhythm::Ebeam b); - - /** - * In music XML structure ties are written in two places: - * below 'pitch' with <tie type /> - * and inside <notations/> with <tied type /> - * but we have single method to generate them both and this enumerator is to distinguish them - */ - enum EtieTag { e_tie, e_tied }; - - /** - * @p t has to be kind of tie but not empty - */ - void tieToXml(QXmlStreamWriter& xml, Trhythm::Etie t, EtieTag tag); + Tchunk(const Tnote &pitch, const TfingerPos &fretPos = TfingerPos()); + + Tchunk(const Tnote &pitch, const Ttechnical &technical); + + /** + * Default constructor - creates 'empty' note and position. + */ + Tchunk() { } + ~Tchunk() { } + + /** + * The note + */ + Tnote &p() { return m_pitch; } + + int duration() const { return m_pitch.duration(); } + + /** + * Position a note on a guitar (if any) - by default it is null - invalid + */ + TfingerPos &g() { return m_technical.fingerPos(); } + + /** + * Extra note data like guitar position, bow direction, staff position (upper/lower) and fingering + */ + Ttechnical &t() { return m_technical; } + + bool onUpperStaff() const { return m_technical.onUpperStaff(); } + void setOnUpperStaff(bool onUpper) { m_technical.setOnUpperStaff(onUpper); } + + Ttechnical::EbowDirection bowing() const { return m_technical.bowing(); } + void setBowing(Ttechnical::EbowDirection b) { m_technical.setBowing(b); } + + /** + * Finger number [0 - 5]. + * -1 is returned when undefined + */ + int finger() const { return m_technical.finger(); } + void setFinger(int fi) { m_technical.setFinger(fi); } + + /** + * Numeric value representing all extra note parameters + */ + quint32 technical() const { return m_technical.data(); } + void setTechnical(quint32 nd) { m_technical.setData(nd); } + + /** + * Returns @p TRUE when position on the guitar is valid. + */ + bool validPos() { return g().str() != 7; } + + /** + * If @p staffNr is set appropriate <staff>staffNr</staff> is added + */ + void toXml(QXmlStreamWriter &xml, int *staffNr = nullptr); + + enum EimportResult { + e_xmlOK = 0, + e_xmlUnsupported = 1, + e_xmlHasTwoDots = 2, + e_xmlIsChord = 4, + e_xmlIsGrace = 8, + e_xmlIsStrangeRtm = 16, + e_xmlIsTupletStart = 32, + e_xmlIsTupletStop = 64 + }; + + /** + * Trough @p staffNr (if set) is returned staff number the note belongs to. + */ + quint16 fromXml(QXmlStreamReader &xml, int *staffNr = nullptr, int *voiceNr = nullptr); + + /** + * Converts beam type into music XML compatible string + */ + static QString beamToString(Trhythm::Ebeam b); + + /** + * In music XML structure ties are written in two places: + * below 'pitch' with <tie type /> + * and inside <notations/> with <tied type /> + * but we have single method to generate them both and this enumerator is to distinguish them + */ + enum EtieTag { e_tie, e_tied }; + + /** + * @p t has to be kind of tie but not empty + */ + void tieToXml(QXmlStreamWriter &xml, Trhythm::Etie t, EtieTag tag); private: - Tnote m_pitch; - Ttechnical m_technical; + Tnote m_pitch; + Ttechnical m_technical; }; #endif // TCHUNK_H diff --git a/src/libs/core/music/tclef.cpp b/src/libs/core/music/tclef.cpp index 8f2beb6c27f0e5d09f28054524ed0249d6d79e1e..d39e663fc9596d1ea561cbb46c0b1122ff56d900 100644 --- a/src/libs/core/music/tclef.cpp +++ b/src/libs/core/music/tclef.cpp @@ -17,189 +17,182 @@ ***************************************************************************/ #include "tclef.h" +#include <QtCore/qdebug.h> #include <QtCore/qmath.h> -#include <QtWidgets/qapplication.h> #include <QtCore/qxmlstream.h> -#include <QtCore/qdebug.h> - +#include <QtWidgets/qapplication.h> Tclef::EclefType Tclef::defaultType = Tclef::Treble_G; - -QString Tclef::name() const { - switch(m_type) { +QString Tclef::name() const +{ + switch (m_type) { case Treble_G: - return QApplication::translate("Tclef", "treble"); + return QApplication::translate("Tclef", "treble"); case Treble_G_8down: - return QApplication::translate("Tclef", "treble dropped"); + return QApplication::translate("Tclef", "treble dropped"); case Bass_F: - return QApplication::translate("Tclef", "bass"); + return QApplication::translate("Tclef", "bass"); case Bass_F_8down: - return QStringLiteral("bass dropped"); // just in case it would appear somewhere - but it shouldn't - not used any more + return QStringLiteral("bass dropped"); // just in case it would appear somewhere - but it shouldn't - not used any more case Alto_C: - return QApplication::translate("Tclef", "alto"); + return QApplication::translate("Tclef", "alto"); case Tenor_C: - return QApplication::translate("Tclef", "tenor"); + return QApplication::translate("Tclef", "tenor"); case PianoStaffClefs: - return QApplication::translate("Tclef", "grand staff"); + return QApplication::translate("Tclef", "grand staff"); case NoClef: - return QApplication::translate("Tclef", "neutral"); - default : - return QString(); - } + return QApplication::translate("Tclef", "neutral"); + default: + return QString(); + } } - -QString Tclef::desc() const { - QString br = QStringLiteral("<br>"); - switch(m_type) { +QString Tclef::desc() const +{ + QString br = QStringLiteral("<br>"); + switch (m_type) { case Treble_G: - return QApplication::translate("Tclef", "clef G") + br + - QApplication::translate("Tclef", "Common used clef (for violin, flute, saxophones, etc.)"); + return QApplication::translate("Tclef", "clef G") + br + QApplication::translate("Tclef", "Common used clef (for violin, flute, saxophones, etc.)"); case Treble_G_8down: - return QApplication::translate("Tclef", "clef G dropped octave down") + br + - QApplication::translate("Tclef", "Clef for guitars (classical, electric and so)"); + return QApplication::translate("Tclef", "clef G dropped octave down") + br + + QApplication::translate("Tclef", "Clef for guitars (classical, electric and so)"); case Bass_F: - return QApplication::translate("Tclef", "clef F") + br + - QApplication::translate("Tclef", "Clef for bass guitar and double bass, also used for cello and trombone."); + return QApplication::translate("Tclef", "clef F") + br + + QApplication::translate("Tclef", "Clef for bass guitar and double bass, also used for cello and trombone."); case Bass_F_8down: - return QString();// QApplication::translate("Tclef", "clef F dropped octave down"); used no more + return QString(); // QApplication::translate("Tclef", "clef F dropped octave down"); used no more case Alto_C: - return QApplication::translate("Tclef", "clef C") + br + - QApplication::translate("Tclef", "Sometimes it is called clef for viola and mostly used for this instrument."); + return QApplication::translate("Tclef", "clef C") + br + + QApplication::translate("Tclef", "Sometimes it is called clef for viola and mostly used for this instrument."); case Tenor_C: - return QApplication::translate("Tclef", "clef C"); + return QApplication::translate("Tclef", "clef C"); case PianoStaffClefs: - return QApplication::translate("Tclef", "treble and bass clefs"); + return QApplication::translate("Tclef", "treble and bass clefs"); case NoClef: - return QApplication::translate("Tclef", "percussion clef") + br + - QApplication::translate("Tclef", "For rhythms only, note pitch is undefined."); - default : - return QString(); - } + return QApplication::translate("Tclef", "percussion clef") + br + QApplication::translate("Tclef", "For rhythms only, note pitch is undefined."); + default: + return QString(); + } } - -QString Tclef::glyph() const { - switch(m_type) { +QString Tclef::glyph() const +{ + switch (m_type) { case Treble_G: - return QStringLiteral("\ue050"); + return QStringLiteral("\ue050"); case Treble_G_8down: - return QStringLiteral("\ue052"); + return QStringLiteral("\ue052"); case Bass_F: - return QStringLiteral("\ue062"); + return QStringLiteral("\ue062"); case Bass_F_8down: - return QStringLiteral("\ue064"); + return QStringLiteral("\ue064"); case Alto_C: - return QStringLiteral("\ue05c"); + return QStringLiteral("\ue05c"); case Tenor_C: - return QStringLiteral("\ue05c"); + return QStringLiteral("\ue05c"); case PianoStaffClefs: - return QStringLiteral("\ue050"); + return QStringLiteral("\ue050"); case NoClef: - return QStringLiteral("\ue069"); - default : - return QString(); - } + return QStringLiteral("\ue069"); + default: + return QString(); + } } - -QString Tclef::glyphOnStaff() const { - switch(m_type) { +QString Tclef::glyphOnStaff() const +{ + switch (m_type) { case NoClef: - return QStringLiteral("\ue041"); + return QStringLiteral("\ue041"); case Treble_G: - return QStringLiteral("\ue042"); + return QStringLiteral("\ue042"); case Treble_G_8down: - return QStringLiteral("\ue045"); + return QStringLiteral("\ue045"); case Bass_F: - return QStringLiteral("\ue043"); + return QStringLiteral("\ue043"); case Alto_C: - return QStringLiteral("\ue044"); + return QStringLiteral("\ue044"); case Tenor_C: - return QStringLiteral("\ue046"); + return QStringLiteral("\ue046"); case PianoStaffClefs: - return QStringLiteral("\ue047"); + return QStringLiteral("\ue047"); default: - return QString(); - } + return QString(); + } } +void Tclef::toXml(QXmlStreamWriter &xml) +{ + QString sign, line; + if (type() == Treble_G || type() == Treble_G_8down || type() == PianoStaffClefs) { + sign = QStringLiteral("G"); + line = QStringLiteral("2"); + } else if (type() == Bass_F || type() == Bass_F_8down) { + sign = QStringLiteral("F"); + line = QStringLiteral("4"); + } else if (type() == Alto_C) { + sign = QStringLiteral("C"); + line = QStringLiteral("3"); + } else if (type() == Tenor_C) { + sign = QStringLiteral("C"); + line = QStringLiteral("4"); + } else + return; - -void Tclef::toXml(QXmlStreamWriter& xml) { - QString sign, line; - if (type() == Treble_G || type() == Treble_G_8down || type() == PianoStaffClefs) { - sign = QStringLiteral("G"); - line = QStringLiteral("2"); - } else if (type() == Bass_F || type() == Bass_F_8down) { - sign = QStringLiteral("F"); - line = QStringLiteral("4"); - } else if (type() == Alto_C) { - sign = QStringLiteral("C"); - line = QStringLiteral("3"); - } else if (type() == Tenor_C) { - sign = QStringLiteral("C"); - line = QStringLiteral("4"); - } else - return; - - xml.writeStartElement(QLatin1String("clef")); + xml.writeStartElement(QLatin1String("clef")); if (type() == PianoStaffClefs) - xml.writeAttribute(QLatin1String("number") , QLatin1String("1")); + xml.writeAttribute(QLatin1String("number"), QLatin1String("1")); xml.writeTextElement(QLatin1String("sign"), sign); xml.writeTextElement(QLatin1String("line"), line); if (type() == Bass_F_8down || type() == Treble_G_8down) - xml.writeTextElement(QLatin1String("clef-octave-change"), QLatin1String("-1")); - xml.writeEndElement(); // clef - if (type() == PianoStaffClefs) { // another 'clef' element - xml.writeStartElement(QLatin1String("clef")); - xml.writeAttribute(QLatin1String("number") , QLatin1String("2")); - xml.writeTextElement(QLatin1String("sign"), QLatin1String("F")); - xml.writeTextElement(QLatin1String("line"), QLatin1String("4")); + xml.writeTextElement(QLatin1String("clef-octave-change"), QLatin1String("-1")); xml.writeEndElement(); // clef - } + if (type() == PianoStaffClefs) { // another 'clef' element + xml.writeStartElement(QLatin1String("clef")); + xml.writeAttribute(QLatin1String("number"), QLatin1String("2")); + xml.writeTextElement(QLatin1String("sign"), QLatin1String("F")); + xml.writeTextElement(QLatin1String("line"), QLatin1String("4")); + xml.writeEndElement(); // clef + } } - -void Tclef::fromXml(QXmlStreamReader& xml, QString* unsupported) { - QString sign; - int line = 0, oc = 0; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("sign")) - sign = xml.readElementText(); - else if (xml.name() == QLatin1String("line")) - line = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("clef-octave-change")) - oc = xml.readElementText().toInt(); - else - xml.skipCurrentElement(); - } - m_type = NoClef; // clef unsupported - if (sign == QLatin1String("G")) { - if (line == 2) { - if (oc == -1) - m_type = Treble_G_8down; - else if (oc == 0) - m_type = Treble_G; - } - } else if (sign == QLatin1String("F")) { - if (line == 4) { - if (oc == -1) - m_type = Bass_F_8down; - else if (oc == 0) - m_type = Bass_F; - } - } else if (sign == QLatin1String("C")) { - if (line == 3) - m_type = Alto_C; - else if (line == 4) - m_type = Tenor_C; - } - if (m_type == NoClef && unsupported) { - unsupported->append(sign); - } +void Tclef::fromXml(QXmlStreamReader &xml, QString *unsupported) +{ + QString sign; + int line = 0, oc = 0; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("sign")) + sign = xml.readElementText(); + else if (xml.name() == QLatin1String("line")) + line = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("clef-octave-change")) + oc = xml.readElementText().toInt(); + else + xml.skipCurrentElement(); + } + m_type = NoClef; // clef unsupported + if (sign == QLatin1String("G")) { + if (line == 2) { + if (oc == -1) + m_type = Treble_G_8down; + else if (oc == 0) + m_type = Treble_G; + } + } else if (sign == QLatin1String("F")) { + if (line == 4) { + if (oc == -1) + m_type = Bass_F_8down; + else if (oc == 0) + m_type = Bass_F; + } + } else if (sign == QLatin1String("C")) { + if (line == 3) + m_type = Alto_C; + else if (line == 4) + m_type = Tenor_C; + } + if (m_type == NoClef && unsupported) { + unsupported->append(sign); + } } - - - diff --git a/src/libs/core/music/tclef.h b/src/libs/core/music/tclef.h index f683d21e4bc886a76598cafaf730f5401f41e419..a4f94969c0c6f0ff7dac7c9dcbba6588611b054f 100644 --- a/src/libs/core/music/tclef.h +++ b/src/libs/core/music/tclef.h @@ -16,74 +16,71 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TCLEF_H #define TCLEF_H - -#include <nootkacoreglobal.h> #include <QtCore/qobject.h> - +#include <nootkacoreglobal.h> class QXmlStreamReader; class QXmlStreamWriter; - class NOOTKACORE_EXPORT Tclef { - Q_GADGET + Q_GADGET - Q_PROPERTY(EclefType type READ type WRITE setClef) + Q_PROPERTY(EclefType type READ type WRITE setClef) public: - enum EclefType { - NoClef = 0, /**< clef not defined or neutral clef (no pitch) */ - Treble_G = 1, /**< common treble clef */ - Bass_F = 2, /**< bass clef */ - Alto_C = 4, - Treble_G_8down = 8, /**< treble clef with "8" digit below (guitar) */ - Bass_F_8down = 16, /**< bass clef with "8" digit below (bass guitar) */ - Tenor_C = 32, - PianoStaffClefs = 128 /**< exactly it is not a clef */ - }; - - Q_ENUM(EclefType) + enum EclefType { + NoClef = 0, /**< clef not defined or neutral clef (no pitch) */ + Treble_G = 1, /**< common treble clef */ + Bass_F = 2, /**< bass clef */ + Alto_C = 4, + Treble_G_8down = 8, /**< treble clef with "8" digit below (guitar) */ + Bass_F_8down = 16, /**< bass clef with "8" digit below (bass guitar) */ + Tenor_C = 32, + PianoStaffClefs = 128 /**< exactly it is not a clef */ + }; + + Q_ENUM(EclefType) + + Tclef(EclefType type = Treble_G) + : m_type(type) + { + } + Tclef(const Tclef &other) { m_type = other.type(); } + ~Tclef() { } + + EclefType type() const { return m_type; } + void setClef(EclefType type) { m_type = type; } + + Q_INVOKABLE QString name() const; /**< short name of a clef */ + Q_INVOKABLE QString desc() const; /**< a clef description */ + Q_INVOKABLE QString glyph() const; + Q_INVOKABLE QString glyphOnStaff() const; /**< Clef symbol on the staff */ - Tclef(EclefType type = Treble_G) : m_type(type) {} - Tclef(const Tclef& other) { m_type = other.type(); } - ~Tclef() {} - - EclefType type() const { return m_type; } - void setClef(EclefType type) { m_type = type; } - - Q_INVOKABLE QString name() const; /**< short name of a clef */ - Q_INVOKABLE QString desc() const; /**< a clef description */ - Q_INVOKABLE QString glyph() const; - Q_INVOKABLE QString glyphOnStaff() const; /**< Clef symbol on the staff */ - - /** - * Adds 'clef' key to XML stream compatible with MusicXML format with current clef - * <clef> - * <sign>G</sign> - * <line>2</line> - * <clef-octave-change>-1</clef-octave-change> - * </clef> - */ -void toXml(QXmlStreamWriter& xml); + /** + * Adds 'clef' key to XML stream compatible with MusicXML format with current clef + * <clef> + * <sign>G</sign> + * <line>2</line> + * <clef-octave-change>-1</clef-octave-change> + * </clef> + */ + void toXml(QXmlStreamWriter &xml); /** * Reads this clef from XML stream. * When @p unsupported is set - write there what was detected in <clef> key * but couldn't be converted into any clef supported by Nootka. */ -void fromXml(QXmlStreamReader& xml, QString* unsupported = nullptr); - -static EclefType defaultType; /** Default clef type for whole application */ + void fromXml(QXmlStreamReader &xml, QString *unsupported = nullptr); + static EclefType defaultType; /** Default clef type for whole application */ private: - EclefType m_type; - + EclefType m_type; }; #endif // TCLEF_H diff --git a/src/libs/core/music/timportscore.cpp b/src/libs/core/music/timportscore.cpp index 1b329860c55f51dc5deddca7059e1b84ad244c59..688f2a1c8c4fa8d500f8e62ba753c4e5c6be2f27 100644 --- a/src/libs/core/music/timportscore.cpp +++ b/src/libs/core/music/timportscore.cpp @@ -17,778 +17,768 @@ ***************************************************************************/ #include "timportscore.h" -#include "tmelody.h" -#include "tchunk.h" -#include "score/tscoreobject.h" -#include "score/tnoteitem.h" #include "score/tdummychord.h" +#include "score/tnoteitem.h" +#include "score/tscoreobject.h" +#include "tchunk.h" +#include "tmelody.h" +#include <QtCore/qthread.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlengine.h> #include <QtQuick/qquickitem.h> -#include <QtCore/qthread.h> #include <QtCore/qdebug.h> - #define SIX_DUR (6) // duration of Sixteenth - lowest supported rhythmic value +TimportScore *TimportScore::m_instance = nullptr; +int TimportScore::m_splitEveryBarNr = 0; -TimportScore* TimportScore::m_instance = nullptr; -int TimportScore::m_splitEveryBarNr = 0; - - -TimportScore::TimportScore(const QString& xmlFileName, Tmelody* melody, QObject *parent) : - QObject(parent), - m_melody(melody) +TimportScore::TimportScore(const QString &xmlFileName, Tmelody *melody, QObject *parent) + : QObject(parent) + , m_melody(melody) { - if (m_instance) { - qDebug() << "[TimportScore] instance already exists!"; - return; - } - m_instance = this; - m_mainThread = thread(); - m_xmlThread = new TxmlThread(xmlFileName, melody); - connect(m_xmlThread, &TxmlThread::musicXmlRead, this, &TimportScore::musicXmlReadySlot); + if (m_instance) { + qDebug() << "[TimportScore] instance already exists!"; + return; + } + m_instance = this; + m_mainThread = thread(); + m_xmlThread = new TxmlThread(xmlFileName, melody); + connect(m_xmlThread, &TxmlThread::musicXmlRead, this, &TimportScore::musicXmlReadySlot); } - -TimportScore::TimportScore(const QString& xmlFileName, QObject* parent) : - QObject(parent) +TimportScore::TimportScore(const QString &xmlFileName, QObject *parent) + : QObject(parent) { - if (m_instance) { - qDebug() << "[TimportScore] instance already exists!"; - return; - } - m_instance = this; - m_mainThread = thread(); - m_xmlThread = new TxmlThread(xmlFileName); - m_melody = m_xmlThread->mainMelody(); - connect(m_xmlThread, &TxmlThread::musicXmlRead, this, &TimportScore::musicXmlReadySlot); + if (m_instance) { + qDebug() << "[TimportScore] instance already exists!"; + return; + } + m_instance = this; + m_mainThread = thread(); + m_xmlThread = new TxmlThread(xmlFileName); + m_melody = m_xmlThread->mainMelody(); + connect(m_xmlThread, &TxmlThread::musicXmlRead, this, &TimportScore::musicXmlReadySlot); } - TimportScore::~TimportScore() { - if (m_xmlThread) - delete m_xmlThread; - qDeleteAll(m_parts); - m_instance = nullptr; + if (m_xmlThread) + delete m_xmlThread; + qDeleteAll(m_parts); + m_instance = nullptr; } - -void TimportScore::runXmlThread() { - if (m_xmlThread) { - /** - * When @p Tmelody invokes @p TimportScore methods from another thread - * @p TimportScore has to be also in this thread, - * but after music XML is parsed @p TimportScore backs to main thread - * due to QML requires @p TmelodyPart-s it there. - */ - moveToThread(m_xmlThread->thread()); - m_xmlThread->start(); - } -} - - -void TimportScore::addNote(int partId, int staff, int voice, const Tchunk ¬e, bool skip) { - if (partId == m_parts.count() + 1) - m_parts << new TmelodyPart(nullptr, partId, staff, voice); - else if (partId > m_parts.count()) { - qDebug() << "[TimportScore] Wrong parts order in the score file!" << partId << m_parts.count(); - return; - } - if (m_lastPart && partId > m_lastPart->part()) - copyBowings(); - auto staffPart = m_parts[partId - 1]; - if (staff == staffPart->count() + 1) - staffPart->parts << new TmelodyPart(staffPart, partId, staff, voice); - else if (staff > staffPart->count()) { - qDebug() << "[TimportScore] Wrong staves order in the score file!" << staff << staffPart->count(); - return; - } - auto voicePart = staffPart->parts[staff - 1]; - if (voice > voicePart->count()) { - while (voicePart->count() < voice) { - auto p = new TmelodyPart(voicePart, partId, staff, voicePart->count() + 1); - // Create part name text by using part name obtained from <part-list><score-part><part-name> - // or just 'part number' if not that tag in musicXML. - if (partId <= IMPORT_SCORE->partNames().size()) - p->setPartName(IMPORT_SCORE->partNames()[partId - 1]); - if (p->partName().isEmpty()) - p->setPartName(tr("part", "it is a part of a musical score, like violin part or piano part, also called as 'voice'") - + QString(": %1").arg(partId)); - p->setPartName(p->partName() + QLatin1String(", ") + tr("staff") + QString(": %1").arg(staff) + QLatin1String(", ") - + tr("voice", "like in multivocal piece") + QString(": %1").arg(voice)); - voicePart->parts << p; +void TimportScore::runXmlThread() +{ + if (m_xmlThread) { + /** + * When @p Tmelody invokes @p TimportScore methods from another thread + * @p TimportScore has to be also in this thread, + * but after music XML is parsed @p TimportScore backs to main thread + * due to QML requires @p TmelodyPart-s it there. + */ + moveToThread(m_xmlThread->thread()); + m_xmlThread->start(); } - } +} - if (!skip) { - auto snippPart = voicePart->parts[voice - 1]; - if (snippPart->parts.isEmpty()) - snippPart->parts << new TmelodyPart(snippPart, partId, staff, voice); - else { - if (voicePart->splitBarNr() > 0 && snippPart->parts.last()->melody()->lastMeasure().isFull() - && snippPart->parts.last()->melody()->measuresCount() % voicePart->splitBarNr() == 0) - snippPart->parts << new TmelodyPart(snippPart, partId, staff, voice); +void TimportScore::addNote(int partId, int staff, int voice, const Tchunk ¬e, bool skip) +{ + if (partId == m_parts.count() + 1) + m_parts << new TmelodyPart(nullptr, partId, staff, voice); + else if (partId > m_parts.count()) { + qDebug() << "[TimportScore] Wrong parts order in the score file!" << partId << m_parts.count(); + return; } - - auto currSnipp = snippPart->parts.last(); - if (!currSnipp->melody()) { - auto m = new Tmelody(m_melody->title(), m_melody->key()); - m->setComposer(m_melody->composer()); - m->setMeter(m_melody->meter()->meter()); - m->setTempo(m_melody->tempo()); - m->setClef(m_melody->clef()); - currSnipp->setMelody(m); + if (m_lastPart && partId > m_lastPart->part()) + copyBowings(); + auto staffPart = m_parts[partId - 1]; + if (staff == staffPart->count() + 1) + staffPart->parts << new TmelodyPart(staffPart, partId, staff, voice); + else if (staff > staffPart->count()) { + qDebug() << "[TimportScore] Wrong staves order in the score file!" << staff << staffPart->count(); + return; } - currSnipp->melody()->addNote(note); - if (note.bowing()) { - Tmeasure& lm = currSnipp->melody()->lastMeasure(); - // QRect(X: bar nr, Y: notes duration from the bar beginning, W: beaming code). - m_bowings << QRect(lm.number() - 1, lm.duration() - note.duration(), static_cast<int>(note.bowing()), 0); + auto voicePart = staffPart->parts[staff - 1]; + if (voice > voicePart->count()) { + while (voicePart->count() < voice) { + auto p = new TmelodyPart(voicePart, partId, staff, voicePart->count() + 1); + // Create part name text by using part name obtained from <part-list><score-part><part-name> + // or just 'part number' if not that tag in musicXML. + if (partId <= IMPORT_SCORE->partNames().size()) + p->setPartName(IMPORT_SCORE->partNames()[partId - 1]); + if (p->partName().isEmpty()) + p->setPartName(tr("part", "it is a part of a musical score, like violin part or piano part, also called as 'voice'") + + QString(": %1").arg(partId)); + p->setPartName(p->partName() + QLatin1String(", ") + tr("staff") + QString(": %1").arg(staff) + QLatin1String(", ") + + tr("voice", "like in multivocal piece") + QString(": %1").arg(voice)); + voicePart->parts << p; + } } - m_lastPart = currSnipp; - } - setHasMoreParts(partId > 1 || staff > 1 || voice > 1); -} + if (!skip) { + auto snippPart = voicePart->parts[voice - 1]; + if (snippPart->parts.isEmpty()) + snippPart->parts << new TmelodyPart(snippPart, partId, staff, voice); + else { + if (voicePart->splitBarNr() > 0 && snippPart->parts.last()->melody()->lastMeasure().isFull() + && snippPart->parts.last()->melody()->measuresCount() % voicePart->splitBarNr() == 0) + snippPart->parts << new TmelodyPart(snippPart, partId, staff, voice); + } -void TimportScore::addChordNote(const Tchunk& note) { - if (m_lastPart && m_lastPart->melody()) { - m_lastPart->addChordNote(m_lastPart, note); - setHasMoreParts(true); - } else - qDebug() << "[TimportScore] Cannot add chord note to not existing part/melody."; + auto currSnipp = snippPart->parts.last(); + if (!currSnipp->melody()) { + auto m = new Tmelody(m_melody->title(), m_melody->key()); + m->setComposer(m_melody->composer()); + m->setMeter(m_melody->meter()->meter()); + m->setTempo(m_melody->tempo()); + m->setClef(m_melody->clef()); + currSnipp->setMelody(m); + } + currSnipp->melody()->addNote(note); + if (note.bowing()) { + Tmeasure &lm = currSnipp->melody()->lastMeasure(); + // QRect(X: bar nr, Y: notes duration from the bar beginning, W: beaming code). + m_bowings << QRect(lm.number() - 1, lm.duration() - note.duration(), static_cast<int>(note.bowing()), 0); + } + m_lastPart = currSnipp; + } + setHasMoreParts(partId > 1 || staff > 1 || voice > 1); } - -void TimportScore::setUnsupported(int partId, int staff, int voice, int error) { - if (m_lastPart && m_lastPart->part() == partId && m_lastPart->staff() == staff && m_lastPart->voice() == voice) - m_lastPart->setUnsupported(error); - else - qDebug() << "[TimportScore] part, staff or score mismatch with last part"; +void TimportScore::addChordNote(const Tchunk ¬e) +{ + if (m_lastPart && m_lastPart->melody()) { + m_lastPart->addChordNote(m_lastPart, note); + setHasMoreParts(true); + } else + qDebug() << "[TimportScore] Cannot add chord note to not existing part/melody."; } +void TimportScore::setUnsupported(int partId, int staff, int voice, int error) +{ + if (m_lastPart && m_lastPart->part() == partId && m_lastPart->staff() == staff && m_lastPart->voice() == voice) + m_lastPart->setUnsupported(error); + else + qDebug() << "[TimportScore] part, staff or score mismatch with last part"; +} -void TimportScore::sumarize() { - copyBowings(); - for (auto p : m_parts) { - for (auto s : p->parts) { - for (auto v : s->parts) { - if (v->count()) - m_partsModel << v; - } +void TimportScore::sumarize() +{ + copyBowings(); + for (auto p : m_parts) { + for (auto s : p->parts) { + for (auto v : s->parts) { + if (v->count()) + m_partsModel << v; + } + } } - } } - /** * @p splitNr = 0 means merge every voice snippets into single part */ -void TimportScore::setSplitBarNr(int splitNr) { - if (splitNr != m_splitEveryBarNr || splitNr == 0) { - m_splitEveryBarNr = splitNr; - if (m_instance) { - for (auto p : *m_instance->parts()) { - for (auto s : p->parts) { - for (auto v : s->parts) { - if (v->count()) - v->setSplitBarNr(m_splitEveryBarNr); - } +void TimportScore::setSplitBarNr(int splitNr) +{ + if (splitNr != m_splitEveryBarNr || splitNr == 0) { + m_splitEveryBarNr = splitNr; + if (m_instance) { + for (auto p : *m_instance->parts()) { + for (auto s : p->parts) { + for (auto v : s->parts) { + if (v->count()) + v->setSplitBarNr(m_splitEveryBarNr); + } + } + } } - } } - } } - -void TimportScore::selectNoteInChords(int noteNr, bool fromTop) { - for (auto p : m_parts) { - for (auto s : p->parts) { - for (auto v : s->parts) { - if (v->count()) - v->selectNoteInChords(noteNr, fromTop); - } +void TimportScore::selectNoteInChords(int noteNr, bool fromTop) +{ + for (auto p : m_parts) { + for (auto s : p->parts) { + for (auto v : s->parts) { + if (v->count()) + v->selectNoteInChords(noteNr, fromTop); + } + } } - } } +void TimportScore::addPartName(const QString &pn) +{ + m_partNames << pn; + if (m_partNames.count() == 2) // we are already sure that import dialog will be necessary + setHasMoreParts(true); +} +void TimportScore::setContextObject(QObject *c) +{ + m_contextObj = c; +} -void TimportScore::addPartName(const QString& pn) { - m_partNames << pn; - if (m_partNames.count() == 2) // we are already sure that import dialog will be necessary +void TimportScore::keyChanged(const TkeySignature &newKey) +{ setHasMoreParts(true); + if (m_lastPart) { + copyBowings(); + for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves + for (auto voicePart : staffPart->parts) { // and all voices + if (!voicePart->parts.isEmpty()) { + auto lastSnip = voicePart->parts.last(); + if (lastSnip->melody() && lastSnip->melody()->length()) { + // when there are notes in last snipped melody - add new snipped to this voice + auto m = newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); + m->setKey(newKey); // and set new key in its melody + } else // or just set new key in empty melody + lastSnip->melody()->setKey(newKey); + } + } + } + } else + qDebug() << "[TimportScore] Something wrong with XML!"; } - -void TimportScore::setContextObject(QObject* c) { - m_contextObj = c; +void TimportScore::meterChanged(const Tmeter &newMeter) +{ + setHasMoreParts(true); + if (m_lastPart) { + copyBowings(); + for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves + for (auto voicePart : staffPart->parts) { // and all voices + if (!voicePart->parts.isEmpty()) { + auto lastSnip = voicePart->parts.last(); + if (lastSnip->melody() && lastSnip->melody()->length()) { + // when there are notes in last snipped melody - add new snipped to this voice + auto m = newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); + m->setMeter(newMeter.meter()); // and set new meter in its melody + } else // or just set new meter in empty melody + lastSnip->melody()->setMeter(newMeter.meter()); + } + } + } + } else + qDebug() << "[TimportScore] Something wrong with XML!"; } - -void TimportScore::keyChanged(const TkeySignature& newKey) { - setHasMoreParts(true); - if (m_lastPart) { - copyBowings(); - for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves - for (auto voicePart: staffPart->parts) { // and all voices - if (!voicePart->parts.isEmpty()) { - auto lastSnip = voicePart->parts.last(); - if (lastSnip->melody() && lastSnip->melody()->length()) { - // when there are notes in last snipped melody - add new snipped to this voice - auto m = newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); - m->setKey(newKey); // and set new key in its melody - } else // or just set new key in empty melody - lastSnip->melody()->setKey(newKey); - } - } - } - } else - qDebug() << "[TimportScore] Something wrong with XML!"; -} - - -void TimportScore::meterChanged(const Tmeter& newMeter) { - setHasMoreParts(true); - if (m_lastPart) { - copyBowings(); - for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves - for (auto voicePart: staffPart->parts) { // and all voices - if (!voicePart->parts.isEmpty()) { - auto lastSnip = voicePart->parts.last(); - if (lastSnip->melody() && lastSnip->melody()->length()) { - // when there are notes in last snipped melody - add new snipped to this voice - auto m =newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); - m->setMeter(newMeter.meter()); // and set new meter in its melody - } else // or just set new meter in empty melody - lastSnip->melody()->setMeter(newMeter.meter()); - } - } - } - } else - qDebug() << "[TimportScore] Something wrong with XML!"; -} - - -void TimportScore::clefChanged(Tclef::EclefType newClef) { - setHasMoreParts(true); - if (m_lastPart) { - copyBowings(); - for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves - for (auto voicePart: staffPart->parts) { - if (!voicePart->parts.isEmpty()) { - auto lastSnip = voicePart->parts.last(); - if (lastSnip->melody() && lastSnip->melody()->length()) { - // when there are notes in last snipped melody - add new snipped to this voice - auto m = newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); - m->setClef(newClef); // and set new clef in its melody - } else // or just set new clef in empty melody - lastSnip->melody()->setClef(newClef); - } +void TimportScore::clefChanged(Tclef::EclefType newClef) +{ + setHasMoreParts(true); + if (m_lastPart) { + copyBowings(); + for (auto staffPart : m_parts[m_lastPart->part() - 1]->parts) { // split all staves + for (auto voicePart : staffPart->parts) { + if (!voicePart->parts.isEmpty()) { + auto lastSnip = voicePart->parts.last(); + if (lastSnip->melody() && lastSnip->melody()->length()) { + // when there are notes in last snipped melody - add new snipped to this voice + auto m = newSnippet(voicePart, lastSnip->part(), lastSnip->staff(), lastSnip->voice(), m_lastPart->melody()); + m->setClef(newClef); // and set new clef in its melody + } else // or just set new clef in empty melody + lastSnip->melody()->setClef(newClef); + } + } } - } - } else - qDebug() << "[TimportScore] Something wrong with XML!"; + } else + qDebug() << "[TimportScore] Something wrong with XML!"; } - -bool TimportScore::xmlReadFinished() const { - return m_xmlThread && m_xmlThread->isFinished(); +bool TimportScore::xmlReadFinished() const +{ + return m_xmlThread && m_xmlThread->isFinished(); } - -void TimportScore::arpeggiateChords() { - for (auto p : m_parts) { - for (auto s : p->parts) { - for (auto v : s->parts) { - if (v->count()) - v->arpeggiateChords(); - } +void TimportScore::arpeggiateChords() +{ + for (auto p : m_parts) { + for (auto s : p->parts) { + for (auto v : s->parts) { + if (v->count()) + v->arpeggiateChords(); + } + } } - } } - -void TimportScore::fillPartialBar(int partId) { - for (auto s : m_parts[partId - 1]->parts) { - for (auto v : s->parts) - v->fillPartialBar(); - } +void TimportScore::fillPartialBar(int partId) +{ + for (auto s : m_parts[partId - 1]->parts) { + for (auto v : s->parts) + v->fillPartialBar(); + } } - -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# /** * Common routine which appends a new @p TmelodyPart to given @p voicePart * and adds melody to that new part, then returns pointer to it. */ -Tmelody* TimportScore::newSnippet(TmelodyPart* voicePart, int partId, int staffNr, int voiceNr, Tmelody* melody) { - voicePart->parts << new TmelodyPart(voicePart, partId, staffNr, voiceNr); - auto m = new Tmelody(melody->title(), melody->key()); - m->setComposer(melody->composer()); - m->setMeter(melody->meter()->meter()); - m->setTempo(melody->tempo()); - m->setClef(melody->clef()); - voicePart->parts.last()->setMelody(m); - return m; -} - - -void TimportScore::setHasMoreParts(bool moreParts) { - if (!m_hasMoreParts && moreParts) { - m_hasMoreParts = true; - emit wantDialog(); - } -} - - -void TimportScore::copyBowings() { - if (m_lastPart && !m_bowings.isEmpty()) { - auto p = m_parts[m_lastPart->part() - 1]; - for (auto s : p->parts) { - // TODO: We are copying bowings for every staff! - // It is valid for bandoneon but not for violin (so far unsupported) - for (auto v : s->parts) { - if (v->count()) { - auto snipp = v->parts.last(); - for (QRect& bow : m_bowings) { - if (bow.x() < snipp->melody()->measuresCount()) { // X: bar number - Tmeasure& m = snipp->melody()->measure(bow.x()); - int dur = 0; - for (int n = 0; n < m.count(); ++n) { - if (dur >= bow.y()) { // Y: duration where bowing occurs - // width: bowing itself - m.note(n).setBowing(static_cast<Ttechnical::EbowDirection>(bow.width())); - break; +Tmelody *TimportScore::newSnippet(TmelodyPart *voicePart, int partId, int staffNr, int voiceNr, Tmelody *melody) +{ + voicePart->parts << new TmelodyPart(voicePart, partId, staffNr, voiceNr); + auto m = new Tmelody(melody->title(), melody->key()); + m->setComposer(melody->composer()); + m->setMeter(melody->meter()->meter()); + m->setTempo(melody->tempo()); + m->setClef(melody->clef()); + voicePart->parts.last()->setMelody(m); + return m; +} + +void TimportScore::setHasMoreParts(bool moreParts) +{ + if (!m_hasMoreParts && moreParts) { + m_hasMoreParts = true; + emit wantDialog(); + } +} + +void TimportScore::copyBowings() +{ + if (m_lastPart && !m_bowings.isEmpty()) { + auto p = m_parts[m_lastPart->part() - 1]; + for (auto s : p->parts) { + // TODO: We are copying bowings for every staff! + // It is valid for bandoneon but not for violin (so far unsupported) + for (auto v : s->parts) { + if (v->count()) { + auto snipp = v->parts.last(); + for (QRect &bow : m_bowings) { + if (bow.x() < snipp->melody()->measuresCount()) { // X: bar number + Tmeasure &m = snipp->melody()->measure(bow.x()); + int dur = 0; + for (int n = 0; n < m.count(); ++n) { + if (dur >= bow.y()) { // Y: duration where bowing occurs + // width: bowing itself + m.note(n).setBowing(static_cast<Ttechnical::EbowDirection>(bow.width())); + break; + } + dur += m.note(n).duration(); + } + } + } } - dur += m.note(n).duration(); - } } - } } - } + m_bowings.clear(); } - m_bowings.clear(); - } -} - - -void TimportScore::musicXmlReadySlot() { - // All is back to main thread when parts are ready - moveToThread(m_mainThread); - for (auto p : m_parts) // parts have no parent, so move them as well - p->moveToThread(m_mainThread); - emit xmlWasRead(); } - -//################################################################################################# -//################### TpartMelody ############################################ -//################################################################################################# - -TmelodyPart::TmelodyPart(TmelodyPart* parent, int partId, int staffNr, int voiceNr) : - QObject(parent), - m_partId(partId), - m_staffNr(staffNr), - m_voiceNr(voiceNr) +void TimportScore::musicXmlReadySlot() { - m_splitBarNr = TimportScore::splitEveryBarNr(); + // All is back to main thread when parts are ready + moveToThread(m_mainThread); + for (auto p : m_parts) // parts have no parent, so move them as well + p->moveToThread(m_mainThread); + emit xmlWasRead(); +} + +// ################################################################################################# +// ################### TpartMelody ############################################ +// ################################################################################################# + +TmelodyPart::TmelodyPart(TmelodyPart *parent, int partId, int staffNr, int voiceNr) + : QObject(parent) + , m_partId(partId) + , m_staffNr(staffNr) + , m_voiceNr(voiceNr) +{ + m_splitBarNr = TimportScore::splitEveryBarNr(); } - TmelodyPart::~TmelodyPart() { - if (!chords.isEmpty()) { - qDeleteAll(chords); - } - if (m_melody) - delete m_melody; - if (!parts.isEmpty()) { - qDeleteAll(parts); - } + if (!chords.isEmpty()) { + qDeleteAll(chords); + } + if (m_melody) + delete m_melody; + if (!parts.isEmpty()) { + qDeleteAll(parts); + } } - -void TmelodyPart::setMelody(Tmelody* m) { - if (m != m_melody) { - m_melody = m; - emit melodyChanged(); - } +void TmelodyPart::setMelody(Tmelody *m) +{ + if (m != m_melody) { + m_melody = m; + emit melodyChanged(); + } } +void TmelodyPart::setScoreObject(TscoreObject *sObj) +{ + m_scoreObj = sObj; + if (m_melody) { + m_scoreObj->setMelody(m_melody); + for (int c = 0; c < chords.count(); ++c) { + auto noteSeg = m_scoreObj->note(chords[c]->noteNr()); + TdummyChord *chIt; + if (chords[c]->dummyChord()) { + chIt = chords[c]->dummyChord(); + } else { + QQmlComponent comp(m_scoreObj->qmlEngine(), QUrl(QStringLiteral("qrc:/score/DummyChord.qml"))); + auto con = QQmlEngine::contextForObject(IMPORT_SCORE->contextObj()); + chIt = qobject_cast<TdummyChord *>(comp.create(con)); + chIt->setParent(IMPORT_SCORE); + } + chIt->setParentItem(noteSeg); + chIt->setChord(chords[c]); + } + } +} -void TmelodyPart::setScoreObject(TscoreObject* sObj) { - m_scoreObj = sObj; - if (m_melody) { - m_scoreObj->setMelody(m_melody); - for (int c = 0; c < chords.count(); ++c) { - auto noteSeg = m_scoreObj->note(chords[c]->noteNr()); - TdummyChord* chIt; - if (chords[c]->dummyChord()) { - chIt = chords[c]->dummyChord(); - } else { - QQmlComponent comp(m_scoreObj->qmlEngine(), QUrl(QStringLiteral("qrc:/score/DummyChord.qml"))); - auto con = QQmlEngine::contextForObject(IMPORT_SCORE->contextObj()); - chIt = qobject_cast<TdummyChord*>(comp.create(con)); - chIt->setParent(IMPORT_SCORE); - } - chIt->setParentItem(noteSeg); - chIt->setChord(chords[c]); +void TmelodyPart::setSelected(bool sel) +{ + if (sel != m_selected) { + m_selected = sel; + emit selectedChanged(); } - } -} - - -void TmelodyPart::setSelected(bool sel) { - if (sel != m_selected) { - m_selected = sel; - emit selectedChanged(); - } -} - - -void TmelodyPart::setSplitBarNr(int splitNr) { - if (splitNr != m_splitBarNr || (m_splitBarNr == 0 && parts.count() > 1)) { - m_splitBarNr = splitNr; - if (!parts.isEmpty() && parts.first()->melody()) { - // append all melodies to the first one - auto firstPart = parts.first(); - for (int m = 1; m < parts.size(); ++m) { - auto p = parts[m]; - firstPart->melody()->appendMelody(p->melody()); - if (!p->chords.isEmpty()) { - int lastNoteNr = firstPart->melody()->length() - p->melody()->length(); - for (auto ch : p->chords) - ch->setNoteNr(lastNoteNr + ch->noteNr()); - while (!p->chords.isEmpty()) { - firstPart->chords.append(p->chords.takeFirst()); - firstPart->chords.last()->part = firstPart; - } - } - } - // delete the rest of the parts - int partsCount = parts.count(); - for (int m = 1; m < partsCount; ++m) - delete parts.takeLast(); - // then divide by new bars number. if any - bool hasChords = !firstPart->chords.isEmpty(); - if (m_splitBarNr) { - QList<Tmelody*> splitList; - firstPart->melody()->split(m_splitBarNr, splitList); - int len = firstPart->melody()->length(); - if (!splitList.isEmpty()) { - for (auto m : splitList) { - auto mp = new TmelodyPart(this, m_partId, m_staffNr, m_voiceNr); - parts << mp; - mp->setMelody(m); - // split chords into just divided parts - if (hasChords) { - int c = 0; - while (c < firstPart->chords.count()) { - int nr = firstPart->chords[c]->noteNr(); - // lookup by note number a chord is attached to - if (nr >= len && nr < len + m->length()) { - // take the chord from first part, add to actual one and fix attached note number - mp->chords << firstPart->chords.takeAt(c); - mp->chords.last()->setNoteNr(mp->chords.last()->noteNr() - len); - mp->chords.last()->part = mp; - } else if (nr >= len + mp->melody()->length()) - break; - else - c++; - } +} + +void TmelodyPart::setSplitBarNr(int splitNr) +{ + if (splitNr != m_splitBarNr || (m_splitBarNr == 0 && parts.count() > 1)) { + m_splitBarNr = splitNr; + if (!parts.isEmpty() && parts.first()->melody()) { + // append all melodies to the first one + auto firstPart = parts.first(); + for (int m = 1; m < parts.size(); ++m) { + auto p = parts[m]; + firstPart->melody()->appendMelody(p->melody()); + if (!p->chords.isEmpty()) { + int lastNoteNr = firstPart->melody()->length() - p->melody()->length(); + for (auto ch : p->chords) + ch->setNoteNr(lastNoteNr + ch->noteNr()); + while (!p->chords.isEmpty()) { + firstPart->chords.append(p->chords.takeFirst()); + firstPart->chords.last()->part = firstPart; + } + } + } + // delete the rest of the parts + int partsCount = parts.count(); + for (int m = 1; m < partsCount; ++m) + delete parts.takeLast(); + // then divide by new bars number. if any + bool hasChords = !firstPart->chords.isEmpty(); + if (m_splitBarNr) { + QList<Tmelody *> splitList; + firstPart->melody()->split(m_splitBarNr, splitList); + int len = firstPart->melody()->length(); + if (!splitList.isEmpty()) { + for (auto m : splitList) { + auto mp = new TmelodyPart(this, m_partId, m_staffNr, m_voiceNr); + parts << mp; + mp->setMelody(m); + // split chords into just divided parts + if (hasChords) { + int c = 0; + while (c < firstPart->chords.count()) { + int nr = firstPart->chords[c]->noteNr(); + // lookup by note number a chord is attached to + if (nr >= len && nr < len + m->length()) { + // take the chord from first part, add to actual one and fix attached note number + mp->chords << firstPart->chords.takeAt(c); + mp->chords.last()->setNoteNr(mp->chords.last()->noteNr() - len); + mp->chords.last()->part = mp; + } else if (nr >= len + mp->melody()->length()) + break; + else + c++; + } + } + len += m->length(); + } + } } - len += m->length(); - } + emit melodyChanged(); } - } - emit melodyChanged(); + emit splitBarNrChanged(); } - emit splitBarNrChanged(); - } } +int TmelodyPart::key() const +{ + if (m_melody) + return static_cast<int>(m_melody->key().value()); + else if (!parts.isEmpty() && parts.first()->melody()) + return static_cast<int>(parts.first()->melody()->key().value()); -int TmelodyPart::key() const { - if (m_melody) - return static_cast<int>(m_melody->key().value()); - else if (!parts.isEmpty() && parts.first()->melody()) - return static_cast<int>(parts.first()->melody()->key().value()); - - return 0; + return 0; } - -void TmelodyPart::setKey(int k) { - if (m_melody) - m_melody->setKey(TkeySignature(static_cast<char>(k))); - else if (!parts.isEmpty()) { - for (auto p : parts) { - if (p->melody()) - p->melody()->setKey(TkeySignature(static_cast<char>(k))); - } - } -} - - -void TmelodyPart::addChordNote(TmelodyPart* part, const Tchunk& n) { - Tchunk chordNote(n); - chordNote.p().setRhythm(Trhythm(Trhythm::NoRhythm)); - if (!chords.isEmpty() && chords.last()->noteNr() == part->melody()->length() - 1) - chords.last()->add(chordNote); - else { - chords << new TalaChord(part); - chords.last()->add(chordNote); - auto m = chords.last()->notes(); - m->setClef(m_melody->clef()); - m->setKey(m_melody->key()); - } -} - - -void TmelodyPart::selectNoteInChords(int noteNr, bool fromTop) { - for (auto snippPart : parts) { - if (!snippPart->chords.isEmpty()) { - for (auto alaCh : snippPart->chords) { - // We cannot be sure that notes in the cord are in order. - // Actually they are but better safe than sorry, so: - // 1. Create map <chromatic nr, note nr in chord> - QMap<int, int> chromMap; - for (int n = 0; n < alaCh->count(); ++n) - chromMap[alaCh->notes()->note(n)->p().chromatic()] = n; - // 2. sort it by note pitch (obtained order is reversed (ascending)) and we want descending - auto sorted = chromMap.values(); - // so swap meaning of fromTop variable - int s = fromTop ? qMax(alaCh->count() - noteNr, 0) : qMin(noteNr, alaCh->count()) - 1; - // 3. select note but filtered by sorted list - alaCh->dummyChord()->setSelected(sorted[s]); - } +void TmelodyPart::setKey(int k) +{ + if (m_melody) + m_melody->setKey(TkeySignature(static_cast<char>(k))); + else if (!parts.isEmpty()) { + for (auto p : parts) { + if (p->melody()) + p->melody()->setKey(TkeySignature(static_cast<char>(k))); + } } - } } - -void TmelodyPart::arpeggiateChord(TalaChord* alaChord) { - if (alaChord->notes()->note(0)->p().rhythm() == Trhythm::NoRhythm) { - if (!alaChord->setRhythm()) - return; - } - - int chordId = 0; - // find ID number of the chord in the list - for (int c = 0; c < chords.count(); ++c) { - if (alaChord == chords[c]) { - chordId = c; - break; +void TmelodyPart::addChordNote(TmelodyPart *part, const Tchunk &n) +{ + Tchunk chordNote(n); + chordNote.p().setRhythm(Trhythm(Trhythm::NoRhythm)); + if (!chords.isEmpty() && chords.last()->noteNr() == part->melody()->length() - 1) + chords.last()->add(chordNote); + else { + chords << new TalaChord(part); + chords.last()->add(chordNote); + auto m = chords.last()->notes(); + m->setClef(m_melody->clef()); + m->setKey(m_melody->key()); } - } - QList<Tchunk> chunks; - alaChord->notes()->toList(chunks); - // squeeze notes of this dummy chord into melody - m_melody->swapWithNotes(alaChord->noteNr(), chunks); - // then remove this dummy chord (from the list and its QML item) - alaChord->dummyChord()->deleteLater(); - chords.removeAt(chordId); - chordId--; - if (m_scoreObj) - m_scoreObj->setMelody(m_melody); - for (int c = 0; c < chords.count(); ++c) { - auto ch = chords[c]; - if (c > chordId) // fix note number ID of the chord after adding notes to the melody - ch->setNoteNr(ch->noteNr() + chunks.count() - 1); - if (m_scoreObj) // fix parent of TdummyChord after melody was reloaded - ch->dummyChord()->setParentItem(m_scoreObj->note(ch->noteNr())); - } -} - - -void TmelodyPart::arpeggiateChords() { - for (auto snipp : parts) { - if (!snipp->chords.isEmpty()) { - if (snipp->unsupported()) // skip unsupported - continue; - - QList<TalaChord*> shortChords; // list of chords impossible to arpeggiate - // Iterating backwards so appending chord notes doesn't affect note id number - // of other chords - all occur earlier - for (int c = snipp->chords.count() - 1; c >= 0; --c) { - auto chord = snipp->chords[c]; - if (!chord->setRhythm()) { - shortChords.prepend(chord); // backward iteration - add at beginning - chord->dummyChord()->setParentItem(nullptr); // HACK parent TnoteItem will not be the same - continue; - } - QList<Tchunk> chunks; - chord->notes()->toList(chunks); - snipp->melody()->swapWithNotes(chord->noteNr(), chunks); - delete chord->dummyChord(); - chord->setDummyChord(nullptr); - if (!shortChords.isEmpty()) { // fix note ids - for (auto shortCh : shortChords) - shortCh->setNoteNr(shortCh->noteNr() + chunks.count() - 1); +} + +void TmelodyPart::selectNoteInChords(int noteNr, bool fromTop) +{ + for (auto snippPart : parts) { + if (!snippPart->chords.isEmpty()) { + for (auto alaCh : snippPart->chords) { + // We cannot be sure that notes in the cord are in order. + // Actually they are but better safe than sorry, so: + // 1. Create map <chromatic nr, note nr in chord> + QMap<int, int> chromMap; + for (int n = 0; n < alaCh->count(); ++n) + chromMap[alaCh->notes()->note(n)->p().chromatic()] = n; + // 2. sort it by note pitch (obtained order is reversed (ascending)) and we want descending + auto sorted = chromMap.values(); + // so swap meaning of fromTop variable + int s = fromTop ? qMax(alaCh->count() - noteNr, 0) : qMin(noteNr, alaCh->count()) - 1; + // 3. select note but filtered by sorted list + alaCh->dummyChord()->setSelected(sorted[s]); + } } - } - - if (snipp->score()) - snipp->score()->setMelody(snipp->melody()); - if (shortChords.isEmpty()) { - qDeleteAll(snipp->chords); - snipp->chords.clear(); - } else { - for (int c = 0; c < snipp->chords.count(); ++c) { - auto ch = snipp->chords[c]; - if (ch->dummyChord()) { - if (snipp->score()) // fix parent of TdummyChord after melody was reloaded - ch->dummyChord()->setParentItem(snipp->score()->note(ch->noteNr())); - } else - delete snipp->chords.takeAt(c); - } - } } - } } +void TmelodyPart::arpeggiateChord(TalaChord *alaChord) +{ + if (alaChord->notes()->note(0)->p().rhythm() == Trhythm::NoRhythm) { + if (!alaChord->setRhythm()) + return; + } -void TmelodyPart::fillPartialBar() { - for (auto snipp : parts) { - if (snipp->melody()) { - if (snipp->melody()->measuresCount() == 1) { - Tmeasure& m = snipp->melody()->measure(0); - auto rests = Trhythm::resolve(m.meter().duration() - m.duration()); - for (auto r : rests) { - r.setRest(true); - snipp->melody()->prepend(Tchunk(Tnote(0, 0, 0, r), Ttechnical(NO_TECHNICALS))); + int chordId = 0; + // find ID number of the chord in the list + for (int c = 0; c < chords.count(); ++c) { + if (alaChord == chords[c]) { + chordId = c; + break; } - } } - } + QList<Tchunk> chunks; + alaChord->notes()->toList(chunks); + // squeeze notes of this dummy chord into melody + m_melody->swapWithNotes(alaChord->noteNr(), chunks); + // then remove this dummy chord (from the list and its QML item) + alaChord->dummyChord()->deleteLater(); + chords.removeAt(chordId); + chordId--; + if (m_scoreObj) + m_scoreObj->setMelody(m_melody); + for (int c = 0; c < chords.count(); ++c) { + auto ch = chords[c]; + if (c > chordId) // fix note number ID of the chord after adding notes to the melody + ch->setNoteNr(ch->noteNr() + chunks.count() - 1); + if (m_scoreObj) // fix parent of TdummyChord after melody was reloaded + ch->dummyChord()->setParentItem(m_scoreObj->note(ch->noteNr())); + } } +void TmelodyPart::arpeggiateChords() +{ + for (auto snipp : parts) { + if (!snipp->chords.isEmpty()) { + if (snipp->unsupported()) // skip unsupported + continue; + + QList<TalaChord *> shortChords; // list of chords impossible to arpeggiate + // Iterating backwards so appending chord notes doesn't affect note id number + // of other chords - all occur earlier + for (int c = snipp->chords.count() - 1; c >= 0; --c) { + auto chord = snipp->chords[c]; + if (!chord->setRhythm()) { + shortChords.prepend(chord); // backward iteration - add at beginning + chord->dummyChord()->setParentItem(nullptr); // HACK parent TnoteItem will not be the same + continue; + } + QList<Tchunk> chunks; + chord->notes()->toList(chunks); + snipp->melody()->swapWithNotes(chord->noteNr(), chunks); + delete chord->dummyChord(); + chord->setDummyChord(nullptr); + if (!shortChords.isEmpty()) { // fix note ids + for (auto shortCh : shortChords) + shortCh->setNoteNr(shortCh->noteNr() + chunks.count() - 1); + } + } -QList<QObject*> TmelodyPart::snippets() { - QList<QObject*> s; - for (auto p : parts) - s << qobject_cast<QObject*>(p); - return s; + if (snipp->score()) + snipp->score()->setMelody(snipp->melody()); + if (shortChords.isEmpty()) { + qDeleteAll(snipp->chords); + snipp->chords.clear(); + } else { + for (int c = 0; c < snipp->chords.count(); ++c) { + auto ch = snipp->chords[c]; + if (ch->dummyChord()) { + if (snipp->score()) // fix parent of TdummyChord after melody was reloaded + ch->dummyChord()->setParentItem(snipp->score()->note(ch->noteNr())); + } else + delete snipp->chords.takeAt(c); + } + } + } + } } -//################################################################################################# -//################### TalaChord ############################################ -//################################################################################################# - -TalaChord::TalaChord(TmelodyPart* mp) +void TmelodyPart::fillPartialBar() { - part = mp; - if (mp->melody()) { - m_noteNr = part->melody()->length() - 1; - auto fChN = mp->melody()->note(m_noteNr); - m_notes.addNote(Tchunk(Tnote(fChN->p(), Trhythm(Trhythm::NoRhythm)), fChN->t())); - } else - qDebug() << "[TalaChord] FIXME! No melody in the part!"; + for (auto snipp : parts) { + if (snipp->melody()) { + if (snipp->melody()->measuresCount() == 1) { + Tmeasure &m = snipp->melody()->measure(0); + auto rests = Trhythm::resolve(m.meter().duration() - m.duration()); + for (auto r : rests) { + r.setRest(true); + snipp->melody()->prepend(Tchunk(Tnote(0, 0, 0, r), Ttechnical(NO_TECHNICALS))); + } + } + } + } } - -void TalaChord::setDummyChord(TdummyChord* dCh) { - m_dummyChord = dCh; +QList<QObject *> TmelodyPart::snippets() +{ + QList<QObject *> s; + for (auto p : parts) + s << qobject_cast<QObject *>(p); + return s; } +// ################################################################################################# +// ################### TalaChord ############################################ +// ################################################################################################# -bool TalaChord::setRhythm() { - if (m_notes.note(0)->p().rhythm() != Trhythm::NoRhythm) - return true; // all was set already - - if (!canArpeggiate()) - return false; - - int dur = part->melody()->note(m_noteNr)->p().duration(); - m_notes.setMeter(part->melody()->meter()->meter()); - int lastNoteDur = dur - (count() - 1) * SIX_DUR; - auto notesAtEnd = Trhythm::resolve(lastNoteDur); - for (int n = 0; n < count() - 1; ++n) - m_notes.note(n)->p().setRhythm(Trhythm::Sixteenth); - if (notesAtEnd.isEmpty()) { - // error - } else { - auto last = m_notes.note(count() - 1); - if (notesAtEnd.count() > 1) - notesAtEnd.last().setTie(Trhythm::e_tieStart); - last->p().setRhythm(notesAtEnd.last()); - for (int r = notesAtEnd.count() - 2; r >= 0; --r) { - if (r == 0) - notesAtEnd[r].setTie(Trhythm::e_tieEnd); - else - notesAtEnd[r].setTie(Trhythm::e_tieCont); - m_notes.addNote(Tchunk(Tnote(last->p(), notesAtEnd[r]), last->t())); - } - } - return true; +TalaChord::TalaChord(TmelodyPart *mp) +{ + part = mp; + if (mp->melody()) { + m_noteNr = part->melody()->length() - 1; + auto fChN = mp->melody()->note(m_noteNr); + m_notes.addNote(Tchunk(Tnote(fChN->p(), Trhythm(Trhythm::NoRhythm)), fChN->t())); + } else + qDebug() << "[TalaChord] FIXME! No melody in the part!"; } - -void TalaChord::arpeggiateChord() { - part->arpeggiateChord(this); +void TalaChord::setDummyChord(TdummyChord *dCh) +{ + m_dummyChord = dCh; } - -bool TalaChord::canArpeggiate() const { - return count() * SIX_DUR <= part->melody()->note(m_noteNr)->p().duration(); +bool TalaChord::setRhythm() +{ + if (m_notes.note(0)->p().rhythm() != Trhythm::NoRhythm) + return true; // all was set already + + if (!canArpeggiate()) + return false; + + int dur = part->melody()->note(m_noteNr)->p().duration(); + m_notes.setMeter(part->melody()->meter()->meter()); + int lastNoteDur = dur - (count() - 1) * SIX_DUR; + auto notesAtEnd = Trhythm::resolve(lastNoteDur); + for (int n = 0; n < count() - 1; ++n) + m_notes.note(n)->p().setRhythm(Trhythm::Sixteenth); + if (notesAtEnd.isEmpty()) { + // error + } else { + auto last = m_notes.note(count() - 1); + if (notesAtEnd.count() > 1) + notesAtEnd.last().setTie(Trhythm::e_tieStart); + last->p().setRhythm(notesAtEnd.last()); + for (int r = notesAtEnd.count() - 2; r >= 0; --r) { + if (r == 0) + notesAtEnd[r].setTie(Trhythm::e_tieEnd); + else + notesAtEnd[r].setTie(Trhythm::e_tieCont); + m_notes.addNote(Tchunk(Tnote(last->p(), notesAtEnd[r]), last->t())); + } + } + return true; } -//################################################################################################# -//################### TxmlThread ############################################ -//################################################################################################# +void TalaChord::arpeggiateChord() +{ + part->arpeggiateChord(this); +} -TxmlThread::TxmlThread(const QString& xmlFileName, QObject* parent) : - QObject(parent), - m_xmlFileName(xmlFileName), - m_thread(new QThread) +bool TalaChord::canArpeggiate() const { - m_melody = new Tmelody(); - m_melodyCreated = true; - commonConstructor(); + return count() * SIX_DUR <= part->melody()->note(m_noteNr)->p().duration(); } +// ################################################################################################# +// ################### TxmlThread ############################################ +// ################################################################################################# -TxmlThread::TxmlThread(const QString& xmlFileName, Tmelody* m, QObject* parent) : - QObject(parent), - m_xmlFileName(xmlFileName), - m_thread(new QThread) +TxmlThread::TxmlThread(const QString &xmlFileName, QObject *parent) + : QObject(parent) + , m_xmlFileName(xmlFileName) + , m_thread(new QThread) { - m_melody = m; - commonConstructor(); + m_melody = new Tmelody(); + m_melodyCreated = true; + commonConstructor(); } +TxmlThread::TxmlThread(const QString &xmlFileName, Tmelody *m, QObject *parent) + : QObject(parent) + , m_xmlFileName(xmlFileName) + , m_thread(new QThread) +{ + m_melody = m; + commonConstructor(); +} TxmlThread::~TxmlThread() { - delete m_thread; - if (m_melodyCreated) - delete m_melody; + delete m_thread; + if (m_melodyCreated) + delete m_melody; } - -void TxmlThread::start() { - m_thread->start(); +void TxmlThread::start() +{ + m_thread->start(); } - -bool TxmlThread::isFinished() const { - return m_thread->isFinished(); +bool TxmlThread::isFinished() const +{ + return m_thread->isFinished(); } - -void TxmlThread::commonConstructor() { - moveToThread(m_thread); - connect(m_thread, &QThread::started, this, [=]{ - m_melody->grabFromMusicXml(m_xmlFileName); - m_thread->quit(); - }); - connect(m_thread, &QThread::finished, this, &TxmlThread::musicXmlRead); +void TxmlThread::commonConstructor() +{ + moveToThread(m_thread); + connect(m_thread, &QThread::started, this, [=] { + m_melody->grabFromMusicXml(m_xmlFileName); + m_thread->quit(); + }); + connect(m_thread, &QThread::finished, this, &TxmlThread::musicXmlRead); } diff --git a/src/libs/core/music/timportscore.h b/src/libs/core/music/timportscore.h index 7125497352ba18353b422d8b58ad51a17bf57ac4..88824b6f7b2753b2bb4edb8d27c8e385da7ebfb7 100644 --- a/src/libs/core/music/timportscore.h +++ b/src/libs/core/music/timportscore.h @@ -19,15 +19,12 @@ #ifndef TIMPORTSCORE_H #define TIMPORTSCORE_H - -#include <nootkacoreglobal.h> #include "music/tmelody.h" #include <QtCore/qobject.h> - +#include <nootkacoreglobal.h> #define IMPORT_SCORE TimportScore::instance() - class Tchunk; class TscoreObject; class TmelodyPart; @@ -35,7 +32,6 @@ class TdummyChord; class QQuickItem; class QThread; - /** * class @class TalaChord those are notes of a chord read from musicXML. * They are stored in @p Tmelody @p notes() member @@ -49,164 +45,160 @@ class QThread; * and @p DummyChord QML control. * Needed during @p TmelodyPart transformations (split/merge, transpose) */ -class NOOTKACORE_EXPORT TalaChord { - +class NOOTKACORE_EXPORT TalaChord +{ public: - TalaChord(TmelodyPart* mp); + TalaChord(TmelodyPart *mp); - /** - * Number of a note in melody with this chord notes. - */ - int noteNr() const { return m_noteNr; } - void setNoteNr(int nr) { m_noteNr = nr; } + /** + * Number of a note in melody with this chord notes. + */ + int noteNr() const { return m_noteNr; } + void setNoteNr(int nr) { m_noteNr = nr; } - /** - * Notes of the chord stored in @p Tmelody - */ - Tmelody* notes() { return &m_notes; } + /** + * Notes of the chord stored in @p Tmelody + */ + Tmelody *notes() { return &m_notes; } - TmelodyPart *part = nullptr; + TmelodyPart *part = nullptr; - int count() const { return m_notes.length(); } - void add(const Tchunk& n) { m_notes.addNote(n); } + int count() const { return m_notes.length(); } + void add(const Tchunk &n) { m_notes.addNote(n); } - /** - * Store pointer to QML @p TdummyChord instance - */ - void setDummyChord(TdummyChord* dCh); - TdummyChord* dummyChord() { return m_dummyChord; } + /** + * Store pointer to QML @p TdummyChord instance + */ + void setDummyChord(TdummyChord *dCh); + TdummyChord *dummyChord() { return m_dummyChord; } - bool setRhythm(); - void arpeggiateChord(); + bool setRhythm(); + void arpeggiateChord(); - /** - * Returns @p TRUE if duration of chord note is long enough - * to contain at least sixteenths of every chord note - */ - bool canArpeggiate() const; + /** + * Returns @p TRUE if duration of chord note is long enough + * to contain at least sixteenths of every chord note + */ + bool canArpeggiate() const; private: - Tmelody m_notes; - int m_noteNr = -1; - TdummyChord *m_dummyChord = nullptr; // QML item displaying this chord + Tmelody m_notes; + int m_noteNr = -1; + TdummyChord *m_dummyChord = nullptr; // QML item displaying this chord }; - /** * @class TmelodyPart is nested container for @class Tmelody * and list of score parts (staves, voices, snippets) */ class NOOTKACORE_EXPORT TmelodyPart : public QObject { - - Q_OBJECT - - Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) - Q_PROPERTY(int part READ part NOTIFY melodyChanged) - Q_PROPERTY(int staff READ staff NOTIFY melodyChanged) - Q_PROPERTY(int voice READ voice NOTIFY melodyChanged) - Q_PROPERTY(int splitBarNr READ splitBarNr WRITE setSplitBarNr NOTIFY splitBarNrChanged) - Q_PROPERTY(QList<QObject*> snippets READ snippets NOTIFY melodyChanged) - Q_PROPERTY(QString partName READ partName NOTIFY melodyChanged) - Q_PROPERTY(int key READ key WRITE setKey NOTIFY melodyChanged) - Q_PROPERTY(int count READ count NOTIFY melodyChanged) - Q_PROPERTY(int unsupported READ unsupported NOTIFY melodyChanged) + Q_OBJECT + + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) + Q_PROPERTY(int part READ part NOTIFY melodyChanged) + Q_PROPERTY(int staff READ staff NOTIFY melodyChanged) + Q_PROPERTY(int voice READ voice NOTIFY melodyChanged) + Q_PROPERTY(int splitBarNr READ splitBarNr WRITE setSplitBarNr NOTIFY splitBarNrChanged) + Q_PROPERTY(QList<QObject *> snippets READ snippets NOTIFY melodyChanged) + Q_PROPERTY(QString partName READ partName NOTIFY melodyChanged) + Q_PROPERTY(int key READ key WRITE setKey NOTIFY melodyChanged) + Q_PROPERTY(int count READ count NOTIFY melodyChanged) + Q_PROPERTY(int unsupported READ unsupported NOTIFY melodyChanged) public: - explicit TmelodyPart (TmelodyPart* parent = nullptr, int partId = 0, int staffNr = 0, int voiceNr = 0); - ~TmelodyPart() override; - - bool selected() const { return m_selected; } - void setSelected(bool sel); - int part() const { return m_partId; } - int staff() const { return m_staffNr; } - int voice() const { return m_voiceNr; } - int splitBarNr() const { return m_splitBarNr; } - void setSplitBarNr(int splitNr); - - /** - * Returns bit-wise enumerators of @p Tchunk::EimportResults - * when musicXML part contains score elements - * which are not handled by Nootka (32nd and more, tuplets, etc. ) - * or just @p 0 when all is supported. - * But skip @p Tchunk::e_xmlUnsupported - */ - int unsupported() const { return m_unsupported & ~Tchunk::e_xmlUnsupported; } - void setUnsupported(int unS) { m_unsupported |= unS; } - void resetUnsupported() { m_unsupported = 0; } - - Tmelody* melody() { return m_melody; } - void setMelody(Tmelody* m); - - int count() const { return parts.count(); } - - QList<TmelodyPart*> parts; - - /** - * HACK - * This is @p parts list of @p TmelodyPart - * converted to list of @p QObject for backward compatibility with Qt 5.12. - * TODO: Use @p parts here when Qt 5.15 will be minimal. - */ - QList<QObject*> snippets(); - QList<TalaChord*> chords; - - Q_INVOKABLE void setScoreObject(TscoreObject* sObj); - - QString partName() const { return m_partName; } - void setPartName(const QString& pn) { m_partName = pn; } - - /** - * Key signature is only valid when this @p TmelodyPart has melody - * or some of its @p parts has. - * Otherwise 0 is returned - */ - int key() const; - - /** - * It will set key in this melody @p m_melody if exists - * or in all melodies of @p parts. - * Due to purpose of this method is just to prepare melody - * for further transposition - it doesn't refresh corresponding score. - * To do so @p melodyChanged() signal has to be emitted. - */ - void setKey(int k); - - TscoreObject* score() { return m_scoreObj; } - - void addChordNote(TmelodyPart* part, const Tchunk& n); - - /** - * In every chord selects: - * @p noteNr [1st, 2nd, ...] note from the top (human number 1 to 10 not 0) - * if @p fromTop is TRUE or from the bottom - */ - Q_INVOKABLE void selectNoteInChords(int noteNr, bool fromTop); - - void arpeggiateChord(TalaChord* alaChord); - Q_INVOKABLE void arpeggiateChords(); - - void fillPartialBar(); + explicit TmelodyPart(TmelodyPart *parent = nullptr, int partId = 0, int staffNr = 0, int voiceNr = 0); + ~TmelodyPart() override; + + bool selected() const { return m_selected; } + void setSelected(bool sel); + int part() const { return m_partId; } + int staff() const { return m_staffNr; } + int voice() const { return m_voiceNr; } + int splitBarNr() const { return m_splitBarNr; } + void setSplitBarNr(int splitNr); + + /** + * Returns bit-wise enumerators of @p Tchunk::EimportResults + * when musicXML part contains score elements + * which are not handled by Nootka (32nd and more, tuplets, etc. ) + * or just @p 0 when all is supported. + * But skip @p Tchunk::e_xmlUnsupported + */ + int unsupported() const { return m_unsupported & ~Tchunk::e_xmlUnsupported; } + void setUnsupported(int unS) { m_unsupported |= unS; } + void resetUnsupported() { m_unsupported = 0; } + + Tmelody *melody() { return m_melody; } + void setMelody(Tmelody *m); + + int count() const { return parts.count(); } + + QList<TmelodyPart *> parts; + + /** + * HACK + * This is @p parts list of @p TmelodyPart + * converted to list of @p QObject for backward compatibility with Qt 5.12. + * TODO: Use @p parts here when Qt 5.15 will be minimal. + */ + QList<QObject *> snippets(); + QList<TalaChord *> chords; + + Q_INVOKABLE void setScoreObject(TscoreObject *sObj); + + QString partName() const { return m_partName; } + void setPartName(const QString &pn) { m_partName = pn; } + + /** + * Key signature is only valid when this @p TmelodyPart has melody + * or some of its @p parts has. + * Otherwise 0 is returned + */ + int key() const; + + /** + * It will set key in this melody @p m_melody if exists + * or in all melodies of @p parts. + * Due to purpose of this method is just to prepare melody + * for further transposition - it doesn't refresh corresponding score. + * To do so @p melodyChanged() signal has to be emitted. + */ + void setKey(int k); + + TscoreObject *score() { return m_scoreObj; } + + void addChordNote(TmelodyPart *part, const Tchunk &n); + + /** + * In every chord selects: + * @p noteNr [1st, 2nd, ...] note from the top (human number 1 to 10 not 0) + * if @p fromTop is TRUE or from the bottom + */ + Q_INVOKABLE void selectNoteInChords(int noteNr, bool fromTop); + + void arpeggiateChord(TalaChord *alaChord); + Q_INVOKABLE void arpeggiateChords(); + + void fillPartialBar(); signals: - void melodyChanged(); - void selectedChanged(); - void splitBarNrChanged(); + void melodyChanged(); + void selectedChanged(); + void splitBarNrChanged(); private: - int m_partId = 0; - int m_staffNr = 0; - int m_voiceNr = 0; - Tmelody *m_melody = nullptr; - TscoreObject *m_scoreObj = nullptr; - bool m_selected = false; - int m_splitBarNr = 0; - QString m_partName; - int m_unsupported = 0; + int m_partId = 0; + int m_staffNr = 0; + int m_voiceNr = 0; + Tmelody *m_melody = nullptr; + TscoreObject *m_scoreObj = nullptr; + bool m_selected = false; + int m_splitBarNr = 0; + QString m_partName; + int m_unsupported = 0; }; - - class TxmlThread; /** @@ -226,151 +218,149 @@ class TxmlThread; */ class NOOTKACORE_EXPORT TimportScore : public QObject { + Q_OBJECT - Q_OBJECT - - friend class TmelodyPart; + friend class TmelodyPart; public: - TimportScore(const QString& xmlFileName, Tmelody* melody, QObject *parent = nullptr); - TimportScore(const QString& xmlFileName, QObject *parent = nullptr); - ~TimportScore() override; - - static TimportScore* instance() { return m_instance; } - - void runXmlThread(); - - void addNote(int partId, int staff, int voice, const Tchunk& note, bool skip = false); - - /** - * Adds @p note to quasi chord of the latest snippet melody note. - * The quasi chord is a note list attached to the specific melody note. - */ - void addChordNote(const Tchunk& note); - - void setUnsupported(int partId, int staff, int voice, int error); - - /** - * Prepares @p model() with not empty parts - */ - void sumarize(); - - /** - * @p Tmelody instance which performs XML reading. - * It keeps melody parameters like meter, key, clef, title and so, - * but it is valid only inside @p addNote() method. - */ - Tmelody* mainMelody() { return m_melody; } - - QThread* mainThread() { return m_mainThread; } - - QList<TmelodyPart*>* parts() { return &m_parts; } - - QList<QObject*> model() { return m_partsModel; } - - /** - * @p TRUE when there are more melodies to import - */ - bool hasMoreParts() const { return m_hasMoreParts; } - - static int splitEveryBarNr() { return m_splitEveryBarNr; } - static void setSplitBarNr(int splitNr); - - /** - * Select given @p noteNr from every chord in every part - */ - void selectNoteInChords(int noteNr, bool fromTop); - - /** - * List of part names from <part-list><score-part><part-name>. - * They correspond with further <part> tags. - */ - QStringList& partNames() { return m_partNames; } - void addPartName(const QString& pn); - - bool multiSelect() const { return m_multiselect; } - void setMultiSelect(bool ms) { m_multiselect = ms; } - - /** - * Any @p QObject inside @p MelodyImport.qml to obtain QML context - * when new items are created from C++ - */ - QObject* contextObj() { return m_contextObj; } - void setContextObject(QObject* c); - - /** - * @p keyChanged(), @p meterChanged() and @p clefChanged() - * are methods invoked when during reading XML - * apparent change occurs. - * Such a change affects all staves so every one is split: - * in every voice of every staff a new snipped is added, - * with melody where this particular attribute is changed, - * so next note by @p addNote() will be appended there. - */ - void keyChanged(const TkeySignature& newKey); - void meterChanged(const Tmeter& newMeter); - void clefChanged(Tclef::EclefType newClef); - - /** - * @p TRUE only when XML thread was started and finished - */ - bool xmlReadFinished() const; - - /** - * Change all cords in the score into linear melody. - * If possible - chord duration can contain all new notes. - */ - void arpeggiateChords(); - - /** - * Fills first bar which is partial (anacrusis) of @p partId - * with rests - */ - void fillPartialBar(int partId); + TimportScore(const QString &xmlFileName, Tmelody *melody, QObject *parent = nullptr); + TimportScore(const QString &xmlFileName, QObject *parent = nullptr); + ~TimportScore() override; + + static TimportScore *instance() { return m_instance; } + + void runXmlThread(); + + void addNote(int partId, int staff, int voice, const Tchunk ¬e, bool skip = false); + + /** + * Adds @p note to quasi chord of the latest snippet melody note. + * The quasi chord is a note list attached to the specific melody note. + */ + void addChordNote(const Tchunk ¬e); + + void setUnsupported(int partId, int staff, int voice, int error); + + /** + * Prepares @p model() with not empty parts + */ + void sumarize(); + + /** + * @p Tmelody instance which performs XML reading. + * It keeps melody parameters like meter, key, clef, title and so, + * but it is valid only inside @p addNote() method. + */ + Tmelody *mainMelody() { return m_melody; } + + QThread *mainThread() { return m_mainThread; } + + QList<TmelodyPart *> *parts() { return &m_parts; } + + QList<QObject *> model() { return m_partsModel; } + + /** + * @p TRUE when there are more melodies to import + */ + bool hasMoreParts() const { return m_hasMoreParts; } + + static int splitEveryBarNr() { return m_splitEveryBarNr; } + static void setSplitBarNr(int splitNr); + + /** + * Select given @p noteNr from every chord in every part + */ + void selectNoteInChords(int noteNr, bool fromTop); + + /** + * List of part names from <part-list><score-part><part-name>. + * They correspond with further <part> tags. + */ + QStringList &partNames() { return m_partNames; } + void addPartName(const QString &pn); + + bool multiSelect() const { return m_multiselect; } + void setMultiSelect(bool ms) { m_multiselect = ms; } + + /** + * Any @p QObject inside @p MelodyImport.qml to obtain QML context + * when new items are created from C++ + */ + QObject *contextObj() { return m_contextObj; } + void setContextObject(QObject *c); + + /** + * @p keyChanged(), @p meterChanged() and @p clefChanged() + * are methods invoked when during reading XML + * apparent change occurs. + * Such a change affects all staves so every one is split: + * in every voice of every staff a new snipped is added, + * with melody where this particular attribute is changed, + * so next note by @p addNote() will be appended there. + */ + void keyChanged(const TkeySignature &newKey); + void meterChanged(const Tmeter &newMeter); + void clefChanged(Tclef::EclefType newClef); + + /** + * @p TRUE only when XML thread was started and finished + */ + bool xmlReadFinished() const; + + /** + * Change all cords in the score into linear melody. + * If possible - chord duration can contain all new notes. + */ + void arpeggiateChords(); + + /** + * Fills first bar which is partial (anacrusis) of @p partId + * with rests + */ + void fillPartialBar(int partId); signals: - void importReady(); - void xmlWasRead(); - void wantDialog(); + void importReady(); + void xmlWasRead(); + void wantDialog(); protected: - Tmelody* newSnippet(TmelodyPart* voicePart, int partId, int staffNr, int voiceNr, Tmelody* melody); - void setHasMoreParts(bool moreParts); - - /** - * Bowings symbols (so far for bandoneon) even if stuck to note/voice - * have meaning for every staff and voice at this point of score (piano staff). - * So, @p m_bowings stores all detected bowing symbols into @p QRect - * QRect(X: bar nr, Y: notes duration from the bar beginning, W: beaming code). - * Then whenever voice is split or new part is going - * entire list of bolings is copied to every voice in every staff/ - */ - void copyBowings(); - - /** - * Called when @p TxmlThread finish parsing music XML data. - * It moves @p TimportScore back to main thread - * and moves @p m_parts list to main thread also. - */ - void musicXmlReadySlot(); + Tmelody *newSnippet(TmelodyPart *voicePart, int partId, int staffNr, int voiceNr, Tmelody *melody); + void setHasMoreParts(bool moreParts); + + /** + * Bowings symbols (so far for bandoneon) even if stuck to note/voice + * have meaning for every staff and voice at this point of score (piano staff). + * So, @p m_bowings stores all detected bowing symbols into @p QRect + * QRect(X: bar nr, Y: notes duration from the bar beginning, W: beaming code). + * Then whenever voice is split or new part is going + * entire list of bolings is copied to every voice in every staff/ + */ + void copyBowings(); + + /** + * Called when @p TxmlThread finish parsing music XML data. + * It moves @p TimportScore back to main thread + * and moves @p m_parts list to main thread also. + */ + void musicXmlReadySlot(); private: - static TimportScore *m_instance; - QList<TmelodyPart*> m_parts; - QList<QRect> m_bowings; - QList<QObject*> m_partsModel; - bool m_hasMoreParts = false; - Tmelody *m_melody; /**< Main melody */ - static int m_splitEveryBarNr; - QStringList m_partNames; - bool m_multiselect = false; - TmelodyPart *m_lastPart = nullptr; /**< Part where note was added recently */ - QObject *m_contextObj = nullptr; - TxmlThread *m_xmlThread = nullptr; - QThread *m_mainThread; + static TimportScore *m_instance; + QList<TmelodyPart *> m_parts; + QList<QRect> m_bowings; + QList<QObject *> m_partsModel; + bool m_hasMoreParts = false; + Tmelody *m_melody; /**< Main melody */ + static int m_splitEveryBarNr; + QStringList m_partNames; + bool m_multiselect = false; + TmelodyPart *m_lastPart = nullptr; /**< Part where note was added recently */ + QObject *m_contextObj = nullptr; + TxmlThread *m_xmlThread = nullptr; + QThread *m_mainThread; }; - /** * @p class TxmlThread parses music XML data in separate thread. * @p Tmelody::grabFromMusicXml() @@ -380,33 +370,32 @@ private: */ class NOOTKACORE_EXPORT TxmlThread : public QObject { + Q_OBJECT - Q_OBJECT - - friend class TimportScore; + friend class TimportScore; public: - explicit TxmlThread(const QString& xmlFileName, QObject* parent = nullptr); - explicit TxmlThread(const QString& xmlFileName, Tmelody* m, QObject* parent = nullptr); - ~TxmlThread() override; + explicit TxmlThread(const QString &xmlFileName, QObject *parent = nullptr); + explicit TxmlThread(const QString &xmlFileName, Tmelody *m, QObject *parent = nullptr); + ~TxmlThread() override; - void start(); + void start(); - Tmelody* mainMelody() { return m_melody; } + Tmelody *mainMelody() { return m_melody; } - bool isFinished() const; + bool isFinished() const; signals: - void musicXmlRead(); + void musicXmlRead(); private: - void commonConstructor(); + void commonConstructor(); private: - Tmelody *m_melody = nullptr; - QString m_xmlFileName; - QThread *m_thread; - bool m_melodyCreated = false; + Tmelody *m_melody = nullptr; + QString m_xmlFileName; + QThread *m_thread; + bool m_melodyCreated = false; }; #endif // TIMPORTSCORE_H diff --git a/src/libs/core/music/tinstrument.cpp b/src/libs/core/music/tinstrument.cpp index 74c1d17024bf21df08b18fa0aea33d1060d865e6..453d3c827247626e29844a1a630d76774a781ca8 100644 --- a/src/libs/core/music/tinstrument.cpp +++ b/src/libs/core/music/tinstrument.cpp @@ -20,132 +20,140 @@ #include <QtGui/qguiapplication.h> - -//################################################################################################# -//################### static const definitions ############################################ -//################################################################################################# - -static const char* const nameArray[INSTR_COUNT] = { - QT_TRANSLATE_NOOP("Tinstrument", "other instrument"), QT_TRANSLATE_NOOP("Tinstrument", "Classical Guitar"), - QT_TRANSLATE_NOOP("Tinstrument", "Electric Guitar"), QT_TRANSLATE_NOOP("Tinstrument", "Bass Guitar"), - QT_TRANSLATE_NOOP("Tinstrument", "Piano"), QT_TRANSLATE_NOOP("Tinstrument", "Bandoneon"), - QT_TRANSLATE_NOOP("Tinstrument", "Alt Saxophone"), QT_TRANSLATE_NOOP("Tinstrument", "Tenor Saxophone"), - QT_TRANSLATE_NOOP("Tinstrument", "Ukulele") -}; - -//################################################################################################# -//################### Tinstrument ############################################ -//################################################################################################# -Tinstrument::Tinstrument(Tinstrument::Etype type) : - m_type(type) +// ################################################################################################# +// ################### static const definitions ############################################ +// ################################################################################################# + +static const char *const nameArray[INSTR_COUNT] = {QT_TRANSLATE_NOOP("Tinstrument", "other instrument"), + QT_TRANSLATE_NOOP("Tinstrument", "Classical Guitar"), + QT_TRANSLATE_NOOP("Tinstrument", "Electric Guitar"), + QT_TRANSLATE_NOOP("Tinstrument", "Bass Guitar"), + QT_TRANSLATE_NOOP("Tinstrument", "Piano"), + QT_TRANSLATE_NOOP("Tinstrument", "Bandoneon"), + QT_TRANSLATE_NOOP("Tinstrument", "Alt Saxophone"), + QT_TRANSLATE_NOOP("Tinstrument", "Tenor Saxophone"), + QT_TRANSLATE_NOOP("Tinstrument", "Ukulele")}; + +// ################################################################################################# +// ################### Tinstrument ############################################ +// ################################################################################################# +Tinstrument::Tinstrument(Tinstrument::Etype type) + : m_type(type) { } - -QString Tinstrument::name() const { - return staticName(m_type); +QString Tinstrument::name() const +{ + return staticName(m_type); } - -QString Tinstrument::staticName(Tinstrument::Etype t) { - int ti = static_cast<int>(t); - return ti < 0 || ti > INSTR_COUNT - 1 ? QString() : QGuiApplication::translate("Tinstrument", nameArray[ti]); +QString Tinstrument::staticName(Tinstrument::Etype t) +{ + int ti = static_cast<int>(t); + return ti < 0 || ti > INSTR_COUNT - 1 ? QString() : QGuiApplication::translate("Tinstrument", nameArray[ti]); } - -QString Tinstrument::glyph() const { - static const char* const glyphArray[INSTR_COUNT] = { "v", "h", "i", "j", "f", "e", "P", "Q", "u" }; - return QString(glyphArray[static_cast<int>(m_type)]); +QString Tinstrument::glyph() const +{ + static const char *const glyphArray[INSTR_COUNT] = {"v", "h", "i", "j", "f", "e", "P", "Q", "u"}; + return QString(glyphArray[static_cast<int>(m_type)]); } - -int Tinstrument::clef() const { - static const quint8 clefArray[INSTR_COUNT] = { 1, 8, 8, 2, 128, 128, 1, 1, 1 }; - return clefArray[static_cast<int>(m_type)]; +int Tinstrument::clef() const +{ + static const quint8 clefArray[INSTR_COUNT] = {1, 8, 8, 2, 128, 128, 1, 1, 1}; + return clefArray[static_cast<int>(m_type)]; } - -QString Tinstrument::qmlFile() const { - static const char* const qmlFileArray[INSTR_COUNT] = { - "", "Guitar", "Guitar", "Guitar", "Piano", "Bandoneon", "Sax", "Sax", "Ukulele" - }; - return QString(qmlFileArray[static_cast<int>(m_type)]); +QString Tinstrument::qmlFile() const +{ + static const char *const qmlFileArray[INSTR_COUNT] = {"", "Guitar", "Guitar", "Guitar", "Piano", "Bandoneon", "Sax", "Sax", "Ukulele"}; + return QString(qmlFileArray[static_cast<int>(m_type)]); } - -int Tinstrument::transposition() const { - static const qint8 transArray[INSTR_COUNT] = { 0, 0, 0, -12, 0, 0, -9, -14, 0 }; - return transArray[static_cast<int>(m_type)]; +int Tinstrument::transposition() const +{ + static const qint8 transArray[INSTR_COUNT] = {0, 0, 0, -12, 0, 0, -9, -14, 0}; + return transArray[static_cast<int>(m_type)]; } - -int Tinstrument::fretNumber() const { - switch (m_type) { - case ClassicalGuitar: return 19; - case ElectricGuitar: return 23; - case BassGuitar: return 20; - case Ukulele: return 17; - default: return 0; - } +int Tinstrument::fretNumber() const +{ + switch (m_type) { + case ClassicalGuitar: + return 19; + case ElectricGuitar: + return 23; + case BassGuitar: + return 20; + case Ukulele: + return 17; + default: + return 0; + } } - -bool Tinstrument::isFadeOut() const { - switch (m_type) { +bool Tinstrument::isFadeOut() const +{ + switch (m_type) { case ClassicalGuitar: case ElectricGuitar: case BassGuitar: case Piano: case Ukulele: - return true; - default: return false; - } + return true; + default: + return false; + } } - -QString Tinstrument::levelsDir() const { - switch (m_type) { +QString Tinstrument::levelsDir() const +{ + switch (m_type) { case ClassicalGuitar: case ElectricGuitar: - return QStringLiteral("guitar"); + return QStringLiteral("guitar"); case BassGuitar: - return QStringLiteral("bass-guitar"); + return QStringLiteral("bass-guitar"); case Piano: - return QStringLiteral("piano"); + return QStringLiteral("piano"); case Bandoneon: - return QStringLiteral("bandoneon"); + return QStringLiteral("bandoneon"); case AltSax: case TenorSax: - return QStringLiteral("sax"); + return QStringLiteral("sax"); case Ukulele: - return QStringLiteral("ukulele"); + return QStringLiteral("ukulele"); - default: return QString(); - } + default: + return QString(); + } } -int Tinstrument::getItemHeight(int mainWindowHeight) { - switch (m_type) { +int Tinstrument::getItemHeight(int mainWindowHeight) +{ + switch (m_type) { case ClassicalGuitar: case ElectricGuitar: case BassGuitar: - return mainWindowHeight / 4; + return mainWindowHeight / 4; case Ukulele: - return mainWindowHeight / 5; + return mainWindowHeight / 5; case Piano: -#if defined (Q_OS_ANDROID) - return qRound(static_cast<qreal>(mainWindowHeight) * 0.22); +#if defined(Q_OS_ANDROID) + return qRound(static_cast<qreal>(mainWindowHeight) * 0.22); case Bandoneon: - return mainWindowHeight / 3; + return mainWindowHeight / 3; #else - return mainWindowHeight / 5; + return mainWindowHeight / 5; case Bandoneon: - return qRound(static_cast<qreal>(mainWindowHeight) / 3.5); + return qRound(static_cast<qreal>(mainWindowHeight) / 3.5); #endif case AltSax: case TenorSax: - return mainWindowHeight; + return mainWindowHeight; - default: return 0; - } + default: + return 0; + } } diff --git a/src/libs/core/music/tinstrument.h b/src/libs/core/music/tinstrument.h index 94d4b11752b412766f3012e6a3576cfe3fd3c3b2..83c57ed65ca16b8823623c9fac7a52006b0828a3 100644 --- a/src/libs/core/music/tinstrument.h +++ b/src/libs/core/music/tinstrument.h @@ -19,140 +19,133 @@ #ifndef TINSTRUMENT_H #define TINSTRUMENT_H - -#include <nootkacoreglobal.h> #include <QtCore/qobject.h> - +#include <nootkacoreglobal.h> #define INSTR_COUNT (9) // number of instruments supported by Nootka - /** * Describes instrument */ -class NOOTKACORE_EXPORT Tinstrument { - - Q_GADGET - - Q_PROPERTY(Etype type READ type WRITE setType) - Q_PROPERTY(QString name READ name) - Q_PROPERTY(int typeINT READ typeINT) - Q_PROPERTY(QString glyph READ glyph) - Q_PROPERTY(int clef READ clef) - Q_PROPERTY(QString qmlFile READ qmlFile) - Q_PROPERTY(int transposition READ transposition) - Q_PROPERTY(int fretNumber READ fretNumber) - Q_PROPERTY(bool isFadeOut READ isFadeOut) - - Q_PROPERTY(bool isGuitar READ isGuitar) // all guitars (including ukulele) - Q_PROPERTY(bool isSax READ isSax) // all saxophones - Q_PROPERTY(bool none READ none) - Q_PROPERTY(bool classicGuitar READ classicGuitar) - Q_PROPERTY(bool electricGuitar READ electricGuitar) - Q_PROPERTY(bool bassGuitar READ bassGuitar) - Q_PROPERTY(bool piano READ piano) - Q_PROPERTY(bool bandoneon READ bandoneon) - Q_PROPERTY(bool altSax READ altSax) - Q_PROPERTY(bool tenorSax READ tenorSax) - Q_PROPERTY(bool ukulele READ ukulele) +class NOOTKACORE_EXPORT Tinstrument +{ + Q_GADGET + + Q_PROPERTY(Etype type READ type WRITE setType) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(int typeINT READ typeINT) + Q_PROPERTY(QString glyph READ glyph) + Q_PROPERTY(int clef READ clef) + Q_PROPERTY(QString qmlFile READ qmlFile) + Q_PROPERTY(int transposition READ transposition) + Q_PROPERTY(int fretNumber READ fretNumber) + Q_PROPERTY(bool isFadeOut READ isFadeOut) + + Q_PROPERTY(bool isGuitar READ isGuitar) // all guitars (including ukulele) + Q_PROPERTY(bool isSax READ isSax) // all saxophones + Q_PROPERTY(bool none READ none) + Q_PROPERTY(bool classicGuitar READ classicGuitar) + Q_PROPERTY(bool electricGuitar READ electricGuitar) + Q_PROPERTY(bool bassGuitar READ bassGuitar) + Q_PROPERTY(bool piano READ piano) + Q_PROPERTY(bool bandoneon READ bandoneon) + Q_PROPERTY(bool altSax READ altSax) + Q_PROPERTY(bool tenorSax READ tenorSax) + Q_PROPERTY(bool ukulele READ ukulele) public: - - enum Etype { - NoInstrument = 0, // 0, however level and exam save it as 255 for backward comparability - ClassicalGuitar = 1, - ElectricGuitar = 2, - BassGuitar = 3, - Piano = 4, - Bandoneon = 5, - AltSax = 6, - TenorSax = 7, - Ukulele = 8 - }; - Q_ENUM(Etype) - - Tinstrument(Etype type = NoInstrument); - - Etype type() const { return m_type; } - Q_INVOKABLE void setType(Tinstrument::Etype t) { m_type = t; } - int typeINT() const { return static_cast<int>(m_type); } + enum Etype { + NoInstrument = 0, // 0, however level and exam save it as 255 for backward comparability + ClassicalGuitar = 1, + ElectricGuitar = 2, + BassGuitar = 3, + Piano = 4, + Bandoneon = 5, + AltSax = 6, + TenorSax = 7, + Ukulele = 8 + }; + Q_ENUM(Etype) + + Tinstrument(Etype type = NoInstrument); + + Etype type() const { return m_type; } + Q_INVOKABLE void setType(Tinstrument::Etype t) { m_type = t; } + int typeINT() const { return static_cast<int>(m_type); } /** * Translated name of an instrument. */ - QString name() const; - - QString static staticName(Etype t); - - /** - * letter of instrument symbol (singer glyph for NoInstrument). - */ - QString glyph() const; - - /** - * Preferred clef for @p Etype of instrument - */ - int clef() const; - - /** - * @p TRUE for all kinds of guitar - */ - bool isGuitar() const { return m_type == ClassicalGuitar || m_type == ElectricGuitar || m_type == BassGuitar || m_type == Ukulele; } - - /** - * @p TRUE for all kinds of saxophones - */ - bool isSax() const { return m_type == AltSax || m_type == TenorSax; } - - bool none() const { return m_type == NoInstrument; } - bool classicGuitar() const { return m_type == ClassicalGuitar; } - bool electricGuitar() const { return m_type == ElectricGuitar; } - bool bassGuitar() const { return m_type == BassGuitar; } - bool piano() const { return m_type == Piano; } - bool bandoneon() const { return m_type == Bandoneon; } - bool altSax() const { return m_type == AltSax; } - bool tenorSax() const { return m_type == TenorSax; } - bool ukulele() const { return m_type == Ukulele; } - - /** - * File implementing QML side of the instrument - */ - QString qmlFile() const; - - /** - * Default transposition of the instrument - */ - int transposition() const; - - /** - * Number of frets for guitars or null for other instruments - */ - int fretNumber() const; - - /** - * @p True for guitars and piano - instruments with sound that fades out. - * In contrary the sound of saxophones, bandoneon, bowed strings, etc. - * is continuous. - * @p NoInstrument type is defined as continuous here as well. - */ - bool isFadeOut() const; - - /** - * Name of subdirectory in the 'levels' directory - * with level *.nel files for actual instrument - */ - QString levelsDir() const; - - /** - * Returns desired instrument item height calculated from Nootka main window height - */ - Q_INVOKABLE int getItemHeight(int mainWindowHeight); + QString name() const; -private: - Etype m_type; + QString static staticName(Etype t); -}; + /** + * letter of instrument symbol (singer glyph for NoInstrument). + */ + QString glyph() const; + + /** + * Preferred clef for @p Etype of instrument + */ + int clef() const; + + /** + * @p TRUE for all kinds of guitar + */ + bool isGuitar() const { return m_type == ClassicalGuitar || m_type == ElectricGuitar || m_type == BassGuitar || m_type == Ukulele; } + + /** + * @p TRUE for all kinds of saxophones + */ + bool isSax() const { return m_type == AltSax || m_type == TenorSax; } + + bool none() const { return m_type == NoInstrument; } + bool classicGuitar() const { return m_type == ClassicalGuitar; } + bool electricGuitar() const { return m_type == ElectricGuitar; } + bool bassGuitar() const { return m_type == BassGuitar; } + bool piano() const { return m_type == Piano; } + bool bandoneon() const { return m_type == Bandoneon; } + bool altSax() const { return m_type == AltSax; } + bool tenorSax() const { return m_type == TenorSax; } + bool ukulele() const { return m_type == Ukulele; } + /** + * File implementing QML side of the instrument + */ + QString qmlFile() const; + + /** + * Default transposition of the instrument + */ + int transposition() const; -#endif //TINSTRUMENT_H + /** + * Number of frets for guitars or null for other instruments + */ + int fretNumber() const; + + /** + * @p True for guitars and piano - instruments with sound that fades out. + * In contrary the sound of saxophones, bandoneon, bowed strings, etc. + * is continuous. + * @p NoInstrument type is defined as continuous here as well. + */ + bool isFadeOut() const; + + /** + * Name of subdirectory in the 'levels' directory + * with level *.nel files for actual instrument + */ + QString levelsDir() const; + + /** + * Returns desired instrument item height calculated from Nootka main window height + */ + Q_INVOKABLE int getItemHeight(int mainWindowHeight); + +private: + Etype m_type; +}; +#endif // TINSTRUMENT_H diff --git a/src/libs/core/music/tkeysignature.cpp b/src/libs/core/music/tkeysignature.cpp index c622fb6a879ff0795f72feab716ce91c579f0da7..c3a569a8cd30b7080e30a8d1293db079e3735722 100644 --- a/src/libs/core/music/tkeysignature.cpp +++ b/src/libs/core/music/tkeysignature.cpp @@ -16,94 +16,114 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tkeysignature.h" #include "tglobals.h" #include "tscoreparams.h" -#include <QtCore/qxmlstream.h> #include <QtCore/qvariant.h> - +#include <QtCore/qxmlstream.h> /*static*/ const char TkeySignature::scalesDefArr[15][7] = { - {-1,-1,-1,-1,-1,-1,-1}, // Cb/ab - {-1,-1,-1, 0,-1,-1,-1}, // Gb/eb - { 0,-1,-1, 0,-1,-1,-1}, // Db/bb - { 0,-1,-1, 0, 0,-1,-1}, // Ab/f - { 0, 0,-1, 0, 0,-1,-1}, // Eb/c - { 0, 0,-1, 0, 0, 0,-1}, // B/g - { 0, 0, 0, 0, 0, 0,-1}, // F/d - { 0, 0, 0, 0, 0, 0, 0}, // C/a - { 0, 0, 0, 1, 0, 0, 0}, // G/e - { 1, 0, 0, 1, 0, 0, 0}, // D/h - { 1, 0, 0, 1, 1, 0, 0}, // A/f# - { 1, 1, 0, 1, 1, 0, 0}, // E/c# - { 1, 1, 0, 1, 1, 1, 0}, // B/g# - { 1, 1, 1, 1, 1, 1, 0}, // F#/d# - { 1, 1, 1, 1, 1, 1, 1}, // C#/a# + {-1, -1, -1, -1, -1, -1, -1}, // Cb/ab + {-1, -1, -1, 0, -1, -1, -1}, // Gb/eb + {0, -1, -1, 0, -1, -1, -1}, // Db/bb + {0, -1, -1, 0, 0, -1, -1}, // Ab/f + {0, 0, -1, 0, 0, -1, -1}, // Eb/c + {0, 0, -1, 0, 0, 0, -1}, // B/g + {0, 0, 0, 0, 0, 0, -1}, // F/d + {0, 0, 0, 0, 0, 0, 0}, // C/a + {0, 0, 0, 1, 0, 0, 0}, // G/e + {1, 0, 0, 1, 0, 0, 0}, // D/h + {1, 0, 0, 1, 1, 0, 0}, // A/f# + {1, 1, 0, 1, 1, 0, 0}, // E/c# + {1, 1, 0, 1, 1, 1, 0}, // B/g# + {1, 1, 1, 1, 1, 1, 0}, // F#/d# + {1, 1, 1, 1, 1, 1, 1}, // C#/a# }; -const char TkeySignature::majorKeys[15] = { 0, 4, 1, 5, 2, 6, 3, 0, 4, 1, 5, 2, 6, 3, 0 }; -const char TkeySignature::minorKeys[15] = { 5, 2, 6, 3, 0, 4, 1, 5, 2, 6, 3, 0, 4, 1, 5 }; - -QString TkeySignature::majorNames[15] = { QString(), QString(), QString(), QString(), QString(), - QString(), QString(), QString(), QString(), QString(), - QString(), QString(), QString(), QString(), QString()}; -QString TkeySignature::minorNames[15] = { QString(), QString(), QString(), QString(), QString(), - QString(), QString(), QString(), QString(), QString(), - QString(), QString(), QString(), QString(), QString()}; - -void TkeySignature::setNameStyle(Tnote::EnameStyle style, const QString& majSuf, const QString& minSuf) { - Tnote n; - QString majS, minS; - auto minus = QStringLiteral("-"); - if (majSuf.isEmpty()) { - majS = minus + majorSufixTxt(); - GLOB->S->majKeyNameSufix = majorSufixTxt(); - } else - if (majSuf != QLatin1String(" ")) +const char TkeySignature::majorKeys[15] = {0, 4, 1, 5, 2, 6, 3, 0, 4, 1, 5, 2, 6, 3, 0}; +const char TkeySignature::minorKeys[15] = {5, 2, 6, 3, 0, 4, 1, 5, 2, 6, 3, 0, 4, 1, 5}; + +QString TkeySignature::majorNames[15] = {QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString()}; +QString TkeySignature::minorNames[15] = {QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString(), + QString()}; + +void TkeySignature::setNameStyle(Tnote::EnameStyle style, const QString &majSuf, const QString &minSuf) +{ + Tnote n; + QString majS, minS; + auto minus = QStringLiteral("-"); + if (majSuf.isEmpty()) { + majS = minus + majorSufixTxt(); + GLOB->scoreParams->majKeyNameSufix = majorSufixTxt(); + } else if (majSuf != QLatin1String(" ")) majS = minus + majSuf; - if (minSuf.isEmpty()) { - minS = minus + minorSufixTxt(); - GLOB->S->minKeyNameSufix = minorSufixTxt(); - } else - if (minSuf != QLatin1String(" ")) - minS = minus + minSuf; + if (minSuf.isEmpty()) { + minS = minus + minorSufixTxt(); + GLOB->scoreParams->minKeyNameSufix = minorSufixTxt(); + } else if (minSuf != QLatin1String(" ")) + minS = minus + minSuf; for (int i = 0; i < 15; i++) { n = Tnote(majorKeys[i] + 1, 0, scalesDefArr[i][int(majorKeys[i])]); majorNames[i] = n.toText(style, false); majorNames[i] += majS; n = Tnote(minorKeys[i] + 1, 0, scalesDefArr[i][int(minorKeys[i])]); - minorNames[i] = n.toText(style, false ).toLower(); + minorNames[i] = n.toText(style, false).toLower(); minorNames[i] += minS; } } -Tnote TkeySignature::inKey(TkeySignature k, const Tnote& n) { +Tnote TkeySignature::inKey(TkeySignature k, const Tnote &n) +{ return inKeyPrivate(k.value(), n); } - TkeySignature::TkeySignature() { - m_key = 0; - m_isMinor = false; + m_key = 0; + m_isMinor = false; } - TkeySignature::TkeySignature(char keyS, bool isMinor) { - if (keyS > -8 && keyS < 8) - m_key = keyS; - else - m_key = 0; - m_isMinor = isMinor; + if (keyS > -8 && keyS < 8) + m_key = keyS; + else + m_key = 0; + m_isMinor = isMinor; } - -QString TkeySignature::accidNumber(bool inHtml) const { +QString TkeySignature::accidNumber(bool inHtml) const +{ QString a; if (m_key < 0) a = QStringLiteral("b"); @@ -118,100 +138,97 @@ QString TkeySignature::accidNumber(bool inHtml) const { return S; } - -Tnote TkeySignature::inKey(const Tnote& n) const { - return inKeyPrivate(value(), n); +Tnote TkeySignature::inKey(const Tnote &n) const +{ + return inKeyPrivate(value(), n); } - -Tnote TkeySignature::tonicNote(int octave) const { - char tonicNoteNr = isMinor() ? minorKeys[value() + 7] : majorKeys[value() + 7]; - return Tnote(tonicNoteNr + 1, octave, scalesDefArr[value() + 7][static_cast<int>(tonicNoteNr)]); +Tnote TkeySignature::tonicNote(int octave) const +{ + char tonicNoteNr = isMinor() ? minorKeys[value() + 7] : majorKeys[value() + 7]; + return Tnote(tonicNoteNr + 1, octave, scalesDefArr[value() + 7][static_cast<int>(tonicNoteNr)]); } - -void TkeySignature::toXml(QXmlStreamWriter& xml) { - xml.writeStartElement("key"); +void TkeySignature::toXml(QXmlStreamWriter &xml) +{ + xml.writeStartElement("key"); xml.writeTextElement("fifths", QVariant(static_cast<int>(value())).toString()); QString mode = isMinor() ? QLatin1String("minor") : QLatin1String("major"); xml.writeTextElement("mode", mode); - xml.writeEndElement(); // key + xml.writeEndElement(); // key } - -void TkeySignature::fromXml(QXmlStreamReader& xml) { - if (xml.name() == QLatin1String("key")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("fifths")) { - m_key = (char)qBound(-7, xml.readElementText().toInt(), 7); - } else if (xml.name() == QLatin1String("mode")) { - if (xml.readElementText() == QLatin1String("minor")) - m_isMinor = true; - else - m_isMinor = false; - } else - xml.skipCurrentElement(); +void TkeySignature::fromXml(QXmlStreamReader &xml) +{ + if (xml.name() == QLatin1String("key")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("fifths")) { + m_key = (char)qBound(-7, xml.readElementText().toInt(), 7); + } else if (xml.name() == QLatin1String("mode")) { + if (xml.readElementText() == QLatin1String("minor")) + m_isMinor = true; + else + m_isMinor = false; + } else + xml.skipCurrentElement(); + } } - } } - -int TkeySignature::difference(const TkeySignature& otherKey) const { - int diff = TkeySignature(otherKey.value()).tonicNote().chromatic() - TkeySignature(m_key).tonicNote().chromatic(); - if (qAbs(diff) > 6) { - diff = diff - 12; - if (diff < -6) - diff += 12; - } - return diff; +int TkeySignature::difference(const TkeySignature &otherKey) const +{ + int diff = TkeySignature(otherKey.value()).tonicNote().chromatic() - TkeySignature(m_key).tonicNote().chromatic(); + if (qAbs(diff) > 6) { + diff = diff - 12; + if (diff < -6) + diff += 12; + } + return diff; } - -QDataStream &operator << (QDataStream &out, TkeySignature &key) { +QDataStream &operator<<(QDataStream &out, TkeySignature &key) +{ quint8 kk = key.value(); if (key.isMinor()) - kk += 15; -// out << qint8(key.value()); + kk += 15; + // out << qint8(key.value()); out << kk; return out; } - -bool getKeyFromStream(QDataStream &in, TkeySignature &k) { +bool getKeyFromStream(QDataStream &in, TkeySignature &k) +{ bool ok = true; qint8 kk; in >> kk; if (kk < -7 || kk > 22) { - kk = 0; ok = false; + kk = 0; + ok = false; } - if (ok && kk > 7 ) // is minor key - k = TkeySignature(char(kk - 15), true); + if (ok && kk > 7) // is minor key + k = TkeySignature(char(kk - 15), true); else - k = TkeySignature(char(kk)); + k = TkeySignature(char(kk)); return ok; } - -//################################################################################################# -//################### PRIVATE ############################################ -//################################################################################################# -Tnote TkeySignature::inKeyPrivate(char val, const Tnote& n) { - int v = val + 7; - if (scalesDefArr[v][n.note() - 1] == n.alter()) - return n; - Tnote tmpN = n.showWithFlat(); - if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) - return tmpN; - tmpN = n.showWithSharp(); - if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) - return tmpN; - tmpN = n.showAsNatural(); - if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) - return tmpN; - - return Tnote(0, 0, 0); +// ################################################################################################# +// ################### PRIVATE ############################################ +// ################################################################################################# +Tnote TkeySignature::inKeyPrivate(char val, const Tnote &n) +{ + int v = val + 7; + if (scalesDefArr[v][n.note() - 1] == n.alter()) + return n; + Tnote tmpN = n.showWithFlat(); + if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) + return tmpN; + tmpN = n.showWithSharp(); + if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) + return tmpN; + tmpN = n.showAsNatural(); + if (scalesDefArr[v][tmpN.note() - 1] == tmpN.alter()) + return tmpN; + + return Tnote(0, 0, 0); } - - - - diff --git a/src/libs/core/music/tkeysignature.h b/src/libs/core/music/tkeysignature.h index 0601c806ef1c4a4fd6aa0787a7c24a61509c0958..9665965ada5301aab1cc08384b8ba908fa24301b 100644 --- a/src/libs/core/music/tkeysignature.h +++ b/src/libs/core/music/tkeysignature.h @@ -1,34 +1,31 @@ /*************************************************************************** -* Copyright (C) 2011-2018 by Tomasz Bojczuk * -* 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/>. * -***************************************************************************/ - + * Copyright (C) 2011-2018 by Tomasz Bojczuk * + * 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/>. * + ***************************************************************************/ #ifndef TKEYSIGNATURE_H #define TKEYSIGNATURE_H -#include <nootkacoreglobal.h> -#include <QtCore/qstring.h> #include "tnote.h" - +#include <QtCore/qstring.h> +#include <nootkacoreglobal.h> class QXmlStreamReader; class QXmlStreamWriter; - /** * This class describes a key signature. * It keeps it in char type. Also it keeps info about mode (minor/major) @@ -36,120 +33,117 @@ class QXmlStreamWriter; class NOOTKACORE_EXPORT TkeySignature { public: - TkeySignature(); - TkeySignature(char keyS, bool isMinor = false); - - /** - * Lists of keys names - */ - static QString majorNames[15], minorNames[15]; - - /** - * Array with definitions of each scale. - * @p scalesDefArr[7] is C-major/a-minor and has 7 times 0 (no accids). - * The order os from 7b [0] to 7# [15] - */ - static const char scalesDefArr[15][7]; - - /** - * Those smart arrays keep which note in @p scalesDefArr is 1-st in key scale. - * F.e. For G-major is @p majorKeys[8] = 4, - * like @p scalesDefArr[8][4] point on accid of a G note. - * This is faster way then calculate it and takes less memory. - */ - static const char majorKeys[15], minorKeys[15]; - - /** - * This method fulfills majorNames and minorNames arrays - * appropriate keys names depends on naming style - * and suffixes. - * Convention of suffixes is: - * - empty string "" means - suffix id default for language and taken from translations - * - string with space " " means - suffix is empty - * - any other string means - it is as it is - */ - static void setNameStyle(Tnote::EnameStyle style, const QString& majSuf = QString(), const QString& minSuf = QString()); - static QString majorSufixTxt() { return QObject::tr("major"); } - static QString minorSufixTxt() { return QObject::tr("minor"); } - - static QString getMajorName(char key) { return majorNames[key + 7]; } - static QString getMinorName(char key) { return minorNames[key + 7]; } - QString getMajorName() const { return majorNames[m_key+7]; } - QString getMinorName() const { return minorNames[m_key+7]; } + TkeySignature(); + TkeySignature(char keyS, bool isMinor = false); + + /** + * Lists of keys names + */ + static QString majorNames[15], minorNames[15]; + + /** + * Array with definitions of each scale. + * @p scalesDefArr[7] is C-major/a-minor and has 7 times 0 (no accids). + * The order os from 7b [0] to 7# [15] + */ + static const char scalesDefArr[15][7]; + + /** + * Those smart arrays keep which note in @p scalesDefArr is 1-st in key scale. + * F.e. For G-major is @p majorKeys[8] = 4, + * like @p scalesDefArr[8][4] point on accid of a G note. + * This is faster way then calculate it and takes less memory. + */ + static const char majorKeys[15], minorKeys[15]; + + /** + * This method fulfills majorNames and minorNames arrays + * appropriate keys names depends on naming style + * and suffixes. + * Convention of suffixes is: + * - empty string "" means - suffix id default for language and taken from translations + * - string with space " " means - suffix is empty + * - any other string means - it is as it is + */ + static void setNameStyle(Tnote::EnameStyle style, const QString &majSuf = QString(), const QString &minSuf = QString()); + static QString majorSufixTxt() { return QObject::tr("major"); } + static QString minorSufixTxt() { return QObject::tr("minor"); } + + static QString getMajorName(char key) { return majorNames[key + 7]; } + static QString getMinorName(char key) { return minorNames[key + 7]; } + QString getMajorName() const { return majorNames[m_key + 7]; } + QString getMinorName() const { return minorNames[m_key + 7]; } /** * Returns minor or major name depend on m_isMinor state. */ - QString getName() const { return m_isMinor ? getMinorName() : getMajorName(); } - - /** - * Return QString with accidentals number and their symbol. - * f.e.: 7# for @p m_key = 7 (Cis-major). - * @p inHtml points when HTML syntax is used. If true # and b symbols - * are formatted to better view. - */ - QString accidNumber(bool inHtml = false) const; - - /** - * Checks is given note @p n in given key signature @p k. - * In the method it checks all possibilities of the note n (flats, sharps, natural, - * and returns either founded note or Tnote(0,0,0) if didn't find. - */ - static Tnote inKey(TkeySignature k, const Tnote& n); - Tnote inKey(const Tnote& n) const; - bool isMinor() const { return m_isMinor; } - void setMinor(bool isMinor) { m_isMinor = isMinor; } - - char value() const { return m_key; } + QString getName() const { return m_isMinor ? getMinorName() : getMajorName(); } + + /** + * Return QString with accidentals number and their symbol. + * f.e.: 7# for @p m_key = 7 (Cis-major). + * @p inHtml points when HTML syntax is used. If true # and b symbols + * are formatted to better view. + */ + QString accidNumber(bool inHtml = false) const; + + /** + * Checks is given note @p n in given key signature @p k. + * In the method it checks all possibilities of the note n (flats, sharps, natural, + * and returns either founded note or Tnote(0,0,0) if didn't find. + */ + static Tnote inKey(TkeySignature k, const Tnote &n); + Tnote inKey(const Tnote &n) const; + bool isMinor() const { return m_isMinor; } + void setMinor(bool isMinor) { m_isMinor = isMinor; } + + char value() const { return m_key; } /** * Returns base (tonic) note of this key signature, i.e. for a-minor key it is note 'a'. * By default note is in small octave, but it can be set through @p octave parameter. */ - Tnote tonicNote(int octave = 0) const; + Tnote tonicNote(int octave = 0) const; /** * Adds 'key' key to XML stream compatible with MusicXML format with current key signature * <key> * <fifths>-2</fifths> * <mode>major</mode> - * </key> + * </key> */ - void toXml(QXmlStreamWriter& xml); + void toXml(QXmlStreamWriter &xml); - /** - * Reads this key signature from XML stream - */ - void fromXml(QXmlStreamReader& xml); + /** + * Reads this key signature from XML stream + */ + void fromXml(QXmlStreamReader &xml); - bool operator==(const TkeySignature& k) const { return m_key == k.value() && m_isMinor == k.isMinor(); } - bool operator!=(const TkeySignature& k) const { return m_key != k.value() || m_isMinor != k.isMinor(); } + bool operator==(const TkeySignature &k) const { return m_key == k.value() && m_isMinor == k.isMinor(); } + bool operator!=(const TkeySignature &k) const { return m_key != k.value() || m_isMinor != k.isMinor(); } - /** - * Returns difference in semitones between @p otherKey and this key signature. - * It compares major variants of both keys to get proper difference for transposition - */ - int difference(const TkeySignature& otherKey) const; + /** + * Returns difference in semitones between @p otherKey and this key signature. + * It compares major variants of both keys to get proper difference for transposition + */ + int difference(const TkeySignature &otherKey) const; private: - char m_key; - bool m_isMinor; + char m_key; + bool m_isMinor; - /** - * Common part for static and no static inKey() methods. - */ - static Tnote inKeyPrivate(char val, const Tnote& n); + /** + * Common part for static and no static inKey() methods. + */ + static Tnote inKeyPrivate(char val, const Tnote &n); }; +NOOTKACORE_EXPORT QDataStream &operator<<(QDataStream &out, TkeySignature &key); -NOOTKACORE_EXPORT QDataStream &operator<< (QDataStream &out, TkeySignature &key); - - /** - * This function is substitute of >> operator for @p TkeySignature. - * It checks is Tnote valid, and return @value bool about it. - */ +/** + * This function is substitute of >> operator for @p TkeySignature. + * It checks is Tnote valid, and return @value bool about it. + */ NOOTKACORE_EXPORT bool getKeyFromStream(QDataStream &in, TkeySignature &k); - #endif // TKEYSIGNATURE_H - diff --git a/src/libs/core/music/tmeasure.cpp b/src/libs/core/music/tmeasure.cpp index d4a8cc557f8ea215fa5815ee85df5475219a94ca..94bf3bcd93ff9999072a848fa362d5a4ae1fbc7b 100644 --- a/src/libs/core/music/tmeasure.cpp +++ b/src/libs/core/music/tmeasure.cpp @@ -19,47 +19,47 @@ #include "tmeasure.h" #include "tchunk.h" - -Tmeasure::Tmeasure(int nr, Tmeter::Emeter m) : - m_number(nr), - m_meter(m), - m_duration(0) +Tmeasure::Tmeasure(int nr, Tmeter::Emeter m) + : m_number(nr) + , m_meter(m) + , m_duration(0) { } - -void Tmeasure::addNote(const Tchunk& n) { - m_notes << n; - if (m_meter.meter() != Tmeter::NoMeter) - m_duration += n.duration(); +void Tmeasure::addNote(const Tchunk &n) +{ + m_notes << n; + if (m_meter.meter() != Tmeter::NoMeter) + m_duration += n.duration(); } - -void Tmeasure::removeLastNote() { - m_duration -= lastNote().duration(); - if (m_meter.meter() != Tmeter::NoMeter) - m_notes.removeLast(); +void Tmeasure::removeLastNote() +{ + m_duration -= lastNote().duration(); + if (m_meter.meter() != Tmeter::NoMeter) + m_notes.removeLast(); } - -bool Tmeasure::isFull() { - return m_duration == m_meter.duration(); +bool Tmeasure::isFull() +{ + return m_duration == m_meter.duration(); } - -void Tmeasure::swapWithNotes(int noteNr, const QList<Tchunk> ¬es) { - m_notes.replace(noteNr, notes.first()); - for (int n = 1; n < notes.count(); ++n) - m_notes.insert(noteNr + n, notes[n]); +void Tmeasure::swapWithNotes(int noteNr, const QList<Tchunk> ¬es) +{ + m_notes.replace(noteNr, notes.first()); + for (int n = 1; n < notes.count(); ++n) + m_notes.insert(noteNr + n, notes[n]); } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -void Tmeasure::prepend(const Tchunk &n) { - m_notes.prepend(n); - if (m_meter.meter() != Tmeter::NoMeter) - m_duration += n.duration(); - // In fact, anacrusis measures have no sense in when no meter, but... let's have this check +void Tmeasure::prepend(const Tchunk &n) +{ + m_notes.prepend(n); + if (m_meter.meter() != Tmeter::NoMeter) + m_duration += n.duration(); + // In fact, anacrusis measures have no sense in when no meter, but... let's have this check } diff --git a/src/libs/core/music/tmeasure.h b/src/libs/core/music/tmeasure.h index d831d2233140083968fb4b61b912a5bba7951b3b..4e4fa48a58c4351b2e3b358dd5a161e21daa7c24 100644 --- a/src/libs/core/music/tmeasure.h +++ b/src/libs/core/music/tmeasure.h @@ -19,10 +19,8 @@ #ifndef TMEASURE_H #define TMEASURE_H - #include "tmeter.h" - class Tchunk; /** @@ -30,61 +28,60 @@ class Tchunk; */ class NOOTKACORE_EXPORT Tmeasure { - - friend class Tmelody; + friend class Tmelody; public: - Tmeasure(int nr, Tmeter::Emeter m = Tmeter::NoMeter); + Tmeasure(int nr, Tmeter::Emeter m = Tmeter::NoMeter); - Tmeter& meter() { return m_meter; } + Tmeter &meter() { return m_meter; } - int number() { return m_number; } + int number() { return m_number; } - /** - * Adds a note. - */ - void addNote(const Tchunk& n); + /** + * Adds a note. + */ + void addNote(const Tchunk &n); - /** - * Returns given note in this measure - */ - Tchunk& note(int index) { return m_notes[index]; } - Tchunk& lastNote() { return m_notes.last(); } + /** + * Returns given note in this measure + */ + Tchunk ¬e(int index) { return m_notes[index]; } + Tchunk &lastNote() { return m_notes.last(); } - /** - * Current duration of the measure - */ - int duration() const { return m_duration; } + /** + * Current duration of the measure + */ + int duration() const { return m_duration; } - /** - * @p TRUE when measure is full - notes duration is equal meter duration - */ - bool isFull(); + /** + * @p TRUE when measure is full - notes duration is equal meter duration + */ + bool isFull(); - void removeLastNote(); + void removeLastNote(); - int count() { return m_notes.size(); } + int count() { return m_notes.size(); } - /** - * Replaces note @p noteNr with notes in the list. - * List of notes must have the same duration like replacing note. - */ - void swapWithNotes(int noteNr, const QList<Tchunk>& notes); + /** + * Replaces note @p noteNr with notes in the list. + * List of notes must have the same duration like replacing note. + */ + void swapWithNotes(int noteNr, const QList<Tchunk> ¬es); protected: - /** - * Adds note @p n at the beginning of this bar. - * Increases its duration. - * This method is intended to fill anacrusis measures with rests - * and is available only by @p Tmelody (protected) - */ - void prepend(const Tchunk& n); + /** + * Adds note @p n at the beginning of this bar. + * Increases its duration. + * This method is intended to fill anacrusis measures with rests + * and is available only by @p Tmelody (protected) + */ + void prepend(const Tchunk &n); private: - int m_number; - Tmeter m_meter; - QList<Tchunk> m_notes; - int m_duration; + int m_number; + Tmeter m_meter; + QList<Tchunk> m_notes; + int m_duration; }; #endif // TMEASURE_H diff --git a/src/libs/core/music/tmelody.cpp b/src/libs/core/music/tmelody.cpp index bcfc2064c098e3feb80a9934f3c989df649242f1..8b5b2638997dfb9a10775ab9410061eff8f24dab 100644 --- a/src/libs/core/music/tmelody.cpp +++ b/src/libs/core/music/tmelody.cpp @@ -17,30 +17,30 @@ ***************************************************************************/ #include "tmelody.h" -#include "tnotestruct.h" -#include "tclef.h" -#include "tmeter.h" -#include "tchunk.h" #include "nootkaconfig.h" +#include "tchunk.h" +#include "tclef.h" #include "timportscore.h" +#include "tmeter.h" +#include "tnotestruct.h" #include "minizip/tzip.h" -#include <QtCore/qvariant.h> +#include <QtCore/qdatetime.h> #include <QtCore/qdebug.h> #include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> - +#include <QtCore/qvariant.h> /* local static */ /** * Prints warning message and sets given clef reference to current default clef type. */ -void unsupportedClef(Tclef::EclefType& clefType) { - if (!IMPORT_SCORE) // Do not warn during importing score - qDebug() << "[Tmelody] Unsupported clef. Set to default" << Tclef(Tclef::defaultType).name(); - clefType = Tclef::defaultType; +void unsupportedClef(Tclef::EclefType &clefType) +{ + if (!IMPORT_SCORE) // Do not warn during importing score + qDebug() << "[Tmelody] Unsupported clef. Set to default" << Tclef(Tclef::defaultType).name(); + clefType = Tclef::defaultType; } /** @@ -55,445 +55,439 @@ static Ttechnical technical; static bool appendWarn = true; /*******************************************************************************************/ - -Tmelody::Tmelody(const QString& title, const TkeySignature& k) : - m_title(title), - m_tempo(120), - m_key(k), - m_meter(new Tmeter), - m_clef(Tclef::defaultType) +Tmelody::Tmelody(const QString &title, const TkeySignature &k) + : m_title(title) + , m_tempo(120) + , m_key(k) + , m_meter(new Tmeter) + , m_clef(Tclef::defaultType) { - appendWarn = true; + appendWarn = true; } - -Tmelody::Tmelody(const Tmelody& other) +Tmelody::Tmelody(const Tmelody &other) { - appendWarn = true; - m_title = other.title(); - m_composer = other.composer(); - m_tempo = other.tempo(); - m_clef = other.clef(); - m_key = other.key(); - m_meter = new Tmeter; - setMeter(other.meter()->meter()); - m_beat = other.beat(); - for (int n = 0; n < other.length(); ++n) - addNote(other.chunk(n)); + appendWarn = true; + m_title = other.title(); + m_composer = other.composer(); + m_tempo = other.tempo(); + m_clef = other.clef(); + m_key = other.key(); + m_meter = new Tmeter; + setMeter(other.meter()->meter()); + m_beat = other.beat(); + for (int n = 0; n < other.length(); ++n) + addNote(other.chunk(n)); } - - Tmelody::~Tmelody() { - delete m_meter; + delete m_meter; } -//#################################################################################################### -//########################################## PUBLIC ################################################## -//#################################################################################################### +// #################################################################################################### +// ########################################## PUBLIC ################################################## +// #################################################################################################### -void Tmelody::setMeter(int m) { - m_meter->setMeter(static_cast<Tmeter::Emeter>(m)); +void Tmelody::setMeter(int m) +{ + m_meter->setMeter(static_cast<Tmeter::Emeter>(m)); } - /** * IT DOESN'T CHECK NOTES DURATION!!!! * all notes added to melody have to be nicely divided to fit every measure duration exactly. */ -void Tmelody::addNote(const Tchunk& n) { - if (p_measures.isEmpty() || lastMeasure().isFull()) - p_measures << Tmeasure(p_measures.count() + 1, m_meter->meter()); +void Tmelody::addNote(const Tchunk &n) +{ + if (p_measures.isEmpty() || lastMeasure().isFull()) + p_measures << Tmeasure(p_measures.count() + 1, m_meter->meter()); - lastMeasure().addNote(n); - m_notes << &lastMeasure().lastNote(); + lastMeasure().addNote(n); + m_notes << &lastMeasure().lastNote(); } - -void Tmelody::clear(bool withCredits, bool withKey) { - m_notes.clear(); - p_measures.clear(); - if (withCredits) { - m_title.clear(); - m_composer.clear(); - } - if (withKey) - m_key = TkeySignature(); +void Tmelody::clear(bool withCredits, bool withKey) +{ + m_notes.clear(); + p_measures.clear(); + if (withCredits) { + m_title.clear(); + m_composer.clear(); + } + if (withKey) + m_key = TkeySignature(); } - -void Tmelody::toList(QList<Tchunk>& chunks) { - for (auto m : p_measures) { - for (int n = 0; n < m.count(); ++n) - chunks << m.note(n); - } +void Tmelody::toList(QList<Tchunk> &chunks) +{ + for (auto m : p_measures) { + for (int n = 0; n < m.count(); ++n) + chunks << m.note(n); + } } - -void Tmelody::swapWithNotes(int noteNr, const QList<Tchunk>& notes) { - int notesCnt = 0; - Tmeasure* barToSwapIn = nullptr; - for (Tmeasure& m : p_measures) { - notesCnt += m.count(); - if (notesCnt > noteNr) { - barToSwapIn = &m; - break; +void Tmelody::swapWithNotes(int noteNr, const QList<Tchunk> ¬es) +{ + int notesCnt = 0; + Tmeasure *barToSwapIn = nullptr; + for (Tmeasure &m : p_measures) { + notesCnt += m.count(); + if (notesCnt > noteNr) { + barToSwapIn = &m; + break; + } + } + int noteIdInBar = noteNr - (notesCnt - barToSwapIn->count()); + if (barToSwapIn) + barToSwapIn->swapWithNotes(noteIdInBar, notes); + for (int n = 1; n < notes.count(); ++n) { + m_notes.insert(noteNr + n, &barToSwapIn->note(noteIdInBar + n)); } - } - int noteIdInBar = noteNr - (notesCnt - barToSwapIn->count()); - if (barToSwapIn) - barToSwapIn->swapWithNotes(noteIdInBar, notes); - for (int n = 1; n < notes.count(); ++n) { - m_notes.insert(noteNr + n, &barToSwapIn->note(noteIdInBar + n)); - } } - -void Tmelody::toXml(QXmlStreamWriter& xml, int trans) { - for (int m = 0; m < p_measures.size(); ++m) { - xml.writeStartElement(QStringLiteral("measure")); - Tmeasure& bar = measure(m); - xml.writeAttribute(QStringLiteral("number"), QVariant(bar.number()).toString()); - if (bar.number() == 1) { - xml.writeStartElement(QStringLiteral("attributes")); - xml.writeTextElement(QStringLiteral("divisions"), QString("%1").arg(Trhythm(Trhythm::Quarter).duration())); - if (m_key.value() || m_key.isMinor()) - m_key.toXml(xml); - m_meter->toXml(xml); - if (m_clef == Tclef::PianoStaffClefs) - xml.writeTextElement(QStringLiteral("staves"), QStringLiteral("2")); - Tclef(m_clef).toXml(xml); - if (trans) { - xml.writeStartElement(QStringLiteral("transpose")); - xml.writeTextElement(QStringLiteral("chromatic"), QString::number(trans % 12)); - xml.writeTextElement(QStringLiteral("octave-change"), QString::number(trans / 12)); - xml.writeEndElement(); // transpose - } - xml.writeEndElement(); // attributes - xml.writeStartElement(QStringLiteral("direction")); - xml.writeAttribute(QStringLiteral("placement"), QStringLiteral("above")); - xml.writeStartElement(QStringLiteral("direction-type")); +void Tmelody::toXml(QXmlStreamWriter &xml, int trans) +{ + for (int m = 0; m < p_measures.size(); ++m) { + xml.writeStartElement(QStringLiteral("measure")); + Tmeasure &bar = measure(m); + xml.writeAttribute(QStringLiteral("number"), QVariant(bar.number()).toString()); + if (bar.number() == 1) { + xml.writeStartElement(QStringLiteral("attributes")); + xml.writeTextElement(QStringLiteral("divisions"), QString("%1").arg(Trhythm(Trhythm::Quarter).duration())); + if (m_key.value() || m_key.isMinor()) + m_key.toXml(xml); + m_meter->toXml(xml); + if (m_clef == Tclef::PianoStaffClefs) + xml.writeTextElement(QStringLiteral("staves"), QStringLiteral("2")); + Tclef(m_clef).toXml(xml); + if (trans) { + xml.writeStartElement(QStringLiteral("transpose")); + xml.writeTextElement(QStringLiteral("chromatic"), QString::number(trans % 12)); + xml.writeTextElement(QStringLiteral("octave-change"), QString::number(trans / 12)); + xml.writeEndElement(); // transpose + } + xml.writeEndElement(); // attributes + xml.writeStartElement(QStringLiteral("direction")); + xml.writeAttribute(QStringLiteral("placement"), QStringLiteral("above")); + xml.writeStartElement(QStringLiteral("direction-type")); xml.writeStartElement(QStringLiteral("metronome")); - QString beatUnitString = QStringLiteral("quarter"); - if (beat() == Tmeter::BeatEighth) + QString beatUnitString = QStringLiteral("quarter"); + if (beat() == Tmeter::BeatEighth) beatUnitString = QStringLiteral("eighth"); - else if (beat() == Tmeter::BeatHalf) + else if (beat() == Tmeter::BeatHalf) beatUnitString = QStringLiteral("half"); - xml.writeTextElement(QStringLiteral("beat-unit"), beatUnitString); - if (beat() == Tmeter::BeatQuarterDot) + xml.writeTextElement(QStringLiteral("beat-unit"), beatUnitString); + if (beat() == Tmeter::BeatQuarterDot) xml.writeEmptyElement(QStringLiteral("beat-unit-dot")); - xml.writeTextElement(QStringLiteral("per-minute"), QString::number(tempo())); + xml.writeTextElement(QStringLiteral("per-minute"), QString::number(tempo())); xml.writeEndElement(); // metronome - xml.writeEndElement(); // direction-type - xml.writeEndElement(); // direction - } - int staffNr_1 = 1, staffNr_2 = 2; - int *staffPtr = nullptr; - for (int n = 0; n < bar.count(); ++n) { - if (m_clef == Tclef::PianoStaffClefs) { - if (bar.note(n).p().onUpperStaff()) - staffPtr = &staffNr_1; - else - staffPtr = &staffNr_2; + xml.writeEndElement(); // direction-type + xml.writeEndElement(); // direction + } + int staffNr_1 = 1, staffNr_2 = 2; + int *staffPtr = nullptr; + for (int n = 0; n < bar.count(); ++n) { + if (m_clef == Tclef::PianoStaffClefs) { + if (bar.note(n).p().onUpperStaff()) + staffPtr = &staffNr_1; + else + staffPtr = &staffNr_2; + } + bar.note(n).toXml(xml, staffPtr); } - bar.note(n).toXml(xml, staffPtr); - } - xml.writeEndElement(); // measure - } + xml.writeEndElement(); // measure + } } - -bool Tmelody::fromXml(QXmlStreamReader& xml, bool madeWithNootka, int partId) { - bool ok = true; - int prevTie = -1; // -1 -> no tie, otherwise this number points to previous note that had a tie - m_notes.clear(); - p_measures.clear(); - m_meter->setMeter(Tmeter::NoMeter); - setTempo(0); // reset tempo, try to read from XML - int barNr = 0; - QStringList clefSuppList; - int fixTransposition = 0; - while (xml.readNextStartElement()) { -/** [measure] */ - if (xml.name() == QLatin1String("measure")) { - int tmpBarNr = xml.attributes().value(QStringLiteral("number")).toInt(); - barNr++; - if (tmpBarNr != barNr) { - qDebug() << "[Tmelody] Something wrong with measure numbers!" << barNr << "was expected, but" << tmpBarNr << "was read.\n" - << "Better check integrity of this music XML file!"; - } - if (tmpBarNr == 2 && !p_measures.isEmpty() && lastMeasure().duration() < m_meter->duration()) { - if (partId == 1) // print message just once - qDebug() << "[Tmelody] First measure was partial (anacrusis/pickup). Added rest at the beginning."; - auto rests = Trhythm::resolve(m_meter->duration() - lastMeasure().duration()); - for (auto r : rests) { - r.setRest(true); - prepend(Tchunk(Tnote(0, 0, 0, r), Ttechnical(NO_TECHNICALS))); - } - if (IMPORT_SCORE) - IMPORT_SCORE->fillPartialBar(partId); - } - while (xml.readNextStartElement()) { -/** [attributes] */ - if (xml.name() == QLatin1String("attributes")) { - Tclef::EclefType clef1 = Tclef::NoClef, clef2 = Tclef::NoClef; - bool clefChanged = false; - int staffCnt = 1; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("staves")) { - staffCnt = xml.readElementText().toInt(); - if (staffCnt > 2) { - qDebug() << "[Tmelody] Read from more staves is unsupported"; - staffCnt = 2; - } - } else if (xml.name() == QLatin1String("clef")) { - Tclef cl; - QString unsuppClefTxt; - cl.fromXml(xml, IMPORT_SCORE ? &unsuppClefTxt : nullptr); - clefSuppList << unsuppClefTxt; - Tclef::EclefType tmpClef = cl.type(); - if (tmpClef == Tclef::NoClef) - unsupportedClef(tmpClef); - if (clef1 == Tclef::NoClef) // detecting piano staff - clef1 = tmpClef; - else if (clef2 == Tclef::NoClef) - clef2 = tmpClef; - if (barNr > 1) - clefChanged = true; -/** [key signature] */ - } else if (xml.name() == QLatin1String("key")) { - if (barNr == 1) - m_key.fromXml(xml); - else { - if (IMPORT_SCORE) { - TkeySignature newKey; - newKey.fromXml(xml); - if (newKey.value() != m_key.value()) - IMPORT_SCORE->keyChanged(newKey); - } else { - xml.skipCurrentElement(); - qDebug() << "[Tmelody] Change key signature in the middle of a melody is not supported!"; - } - } -/** [meter (time signature)] */ - } else if (xml.name() == QLatin1String("time")) { - if (barNr == 1) - m_meter->fromXml(xml); - else { - if (IMPORT_SCORE) { - Tmeter newMeter; - newMeter.fromXml(xml); - if (newMeter.meter() != m_meter->meter()) - IMPORT_SCORE->meterChanged(newMeter); - } else { - xml.skipCurrentElement(); - qDebug() << "[Tmelody] Change time signature (meter) in the middle of a melody is not supported!"; - } - } - } else - xml.skipCurrentElement(); - } - if (barNr == 1) { - if (staffCnt == 2) { - if (clef1 == Tclef::Treble_G && clef2 == Tclef::Bass_F) - m_clef = Tclef::PianoStaffClefs; - else - unsupportedClef(m_clef); - } else - m_clef = clef1; - } else { - if (clefChanged) { - // WARNING: clef change (split staff) will work for all clefs except piano staff - if (IMPORT_SCORE) - IMPORT_SCORE->clefChanged(clef1); - else - qDebug() << "[Tmelody] Change clef in the middle of a melody is not supported!"; - } +bool Tmelody::fromXml(QXmlStreamReader &xml, bool madeWithNootka, int partId) +{ + bool ok = true; + int prevTie = -1; // -1 -> no tie, otherwise this number points to previous note that had a tie + m_notes.clear(); + p_measures.clear(); + m_meter->setMeter(Tmeter::NoMeter); + setTempo(0); // reset tempo, try to read from XML + int barNr = 0; + QStringList clefSuppList; + int fixTransposition = 0; + while (xml.readNextStartElement()) { + /** [measure] */ + if (xml.name() == QLatin1String("measure")) { + int tmpBarNr = xml.attributes().value(QStringLiteral("number")).toInt(); + barNr++; + if (tmpBarNr != barNr) { + qDebug() << "[Tmelody] Something wrong with measure numbers!" << barNr << "was expected, but" << tmpBarNr << "was read.\n" + << "Better check integrity of this music XML file!"; } -/** [note] */ - } else if (xml.name() == QLatin1String("note")) { - int staffNr = 0, voiceNr = 0; - int *staffPtr = nullptr, *voicePtr = nullptr; - if (m_clef == Tclef::PianoStaffClefs || IMPORT_SCORE) - staffPtr = &staffNr; - if (!madeWithNootka && IMPORT_SCORE) - voicePtr = &voiceNr; - Tchunk ch; - Tchunk* dblDotCh = nullptr; - auto chunkOk = ch.fromXml(xml, staffPtr, voicePtr); - if ((!madeWithNootka && IMPORT_SCORE) || !(chunkOk & Tchunk::e_xmlUnsupported)) { - if (ch.p().isRest() && ch.p().rhythm() == Trhythm::NoRhythm) { // Fix rest duration - if it is undefined - means entire measure - ch.p().setRhythm(m_meter->duration()); // it will reset 'rest' attribute - ch.p().setRest(true); - } - if (!(staffPtr && !ch.p().isValid() && ch.p().rhythm() == Trhythm::NoRhythm)) { - // Nootka is not able to import from grand staff of real score (XML) - // and above condition avoids it, but allows to import piano staves created by Nootka itself - - if (m_clef == Tclef::PianoStaffClefs) - ch.p().setOnUpperStaff(staffNr < 2); - if (prevTie > -1) { - // check and fix tie, Nootka supports them only between the same notes - // scoring app may set tie between different ones, but seems like in such case there is no 'stop' tag used (musescore) - Tnote& prevNote = m_notes[prevTie]->p(); - short prevChromatic = prevNote.chromatic(), currChromatic = ch.p().chromatic(); - if ((prevChromatic == currChromatic && ch.p().rtm.tie() == Trhythm::e_noTie) || prevChromatic != currChromatic) { - if (prevNote.rtm.tie() == Trhythm::e_tieCont) - prevNote.rtm.setTie(Trhythm::e_tieEnd); - else if (prevNote.rtm.tie() == Trhythm::e_tieStart) - prevNote.rtm.setTie(Trhythm::e_noTie); - } - } - prevTie = ch.p().rtm.tie() ? m_notes.count() : -1; - if (!technical.isEmpty()) { - // technical data was read before note, so approve it now. But data from <technical> have priority - if (!ch.bowing() && technical.bowing()) - ch.setBowing(technical.bowing()); - if (ch.finger() == -1 && technical.finger() != -1) - ch.setFinger(technical.finger()); - technical.reset(); // reset for the next note - } - if (m_meter->meter() == Tmeter::NoMeter && ch.p().rtm.isValid()) - ch.p().setRhythm(Trhythm::NoRhythm); - bool dblDot = false, tieCont = false; - if (ch.p().rtm.isValid() && chunkOk & Tchunk::e_xmlHasTwoDots) { - tieCont = ch.p().rtm.tie() == Trhythm::e_tieStart || ch.p().rtm.tie() == Trhythm::e_tieCont; - ch.p().rtm.setTie(ch.p().rtm.tie() > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); - dblDot = true; - } - addNote(ch); - if (dblDot) { - // XML import disallows eights with double dots, so divide rhythmic value easily - Trhythm r(static_cast<Trhythm::Erhythm>(ch.p().rhythm() + 2)); - r.setTie(tieCont ? Trhythm::e_tieCont : Trhythm::e_tieEnd); - dblDotCh = new Tchunk(Tnote(ch.p(), r), ch.t()); - addNote(*dblDotCh); + if (tmpBarNr == 2 && !p_measures.isEmpty() && lastMeasure().duration() < m_meter->duration()) { + if (partId == 1) // print message just once + qDebug() << "[Tmelody] First measure was partial (anacrusis/pickup). Added rest at the beginning."; + auto rests = Trhythm::resolve(m_meter->duration() - lastMeasure().duration()); + for (auto r : rests) { + r.setRest(true); + prepend(Tchunk(Tnote(0, 0, 0, r), Ttechnical(NO_TECHNICALS))); } - } + if (IMPORT_SCORE) + IMPORT_SCORE->fillPartialBar(partId); } - if (!madeWithNootka && IMPORT_SCORE) { - // NOTE: This is safe as long as it occurs only during import. - // Old Nootka versions used Dropped bass clef (levels, exams) - // but TlevelSelector, Texam handle that - converts all level stuff into ordinary bass clef - if (m_clef == Tclef::Bass_F_8down) { - fixTransposition = 12; - m_clef = Tclef::Bass_F; - } - if (fixTransposition) - ch.p().transpose(fixTransposition); - bool skip = !clefSuppList[staffNr - 1].isEmpty(); - if (chunkOk & Tchunk::e_xmlIsChord) { - if (!skip) { - IMPORT_SCORE->addChordNote(ch); - // TODO: double dots note - } - } else if (chunkOk & Tchunk::e_xmlIsGrace) { - // TODO: grace note if any - so far skipping - } else { - IMPORT_SCORE->addNote(partId, staffNr, voiceNr, ch, skip); - if (dblDotCh && !skip) { - if (fixTransposition) - dblDotCh->p().transpose(fixTransposition); - IMPORT_SCORE->addNote(partId, staffNr, voiceNr, *dblDotCh); - } - if (chunkOk & Tchunk::e_xmlIsStrangeRtm || chunkOk & Tchunk::e_xmlIsTupletStart|| chunkOk & Tchunk::e_xmlIsTupletStop) - IMPORT_SCORE->setUnsupported(partId, staffNr, voiceNr, chunkOk); - - } - } - if (dblDotCh) - delete dblDotCh; -/** [direction] with bowing/bellow and fingering detected from texts below/above note */ - } else if (xml.name() == QLatin1String("direction")) { while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("direction-type")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("words")) { // fingering and bowing can be read this way - auto words = xml.readElementText(); - if (words == QLatin1String("(A)")) - technical.setBowing(Ttechnical::BowDown); - else if (words == QLatin1String("(C)")) - technical.setBowing(Ttechnical::BowUp); - else { - bool isNumber = false; - int finger = words.toInt(&isNumber); - if (isNumber) - technical.setFinger(finger); + /** [attributes] */ + if (xml.name() == QLatin1String("attributes")) { + Tclef::EclefType clef1 = Tclef::NoClef, clef2 = Tclef::NoClef; + bool clefChanged = false; + int staffCnt = 1; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("staves")) { + staffCnt = xml.readElementText().toInt(); + if (staffCnt > 2) { + qDebug() << "[Tmelody] Read from more staves is unsupported"; + staffCnt = 2; + } + } else if (xml.name() == QLatin1String("clef")) { + Tclef cl; + QString unsuppClefTxt; + cl.fromXml(xml, IMPORT_SCORE ? &unsuppClefTxt : nullptr); + clefSuppList << unsuppClefTxt; + Tclef::EclefType tmpClef = cl.type(); + if (tmpClef == Tclef::NoClef) + unsupportedClef(tmpClef); + if (clef1 == Tclef::NoClef) // detecting piano staff + clef1 = tmpClef; + else if (clef2 == Tclef::NoClef) + clef2 = tmpClef; + if (barNr > 1) + clefChanged = true; + /** [key signature] */ + } else if (xml.name() == QLatin1String("key")) { + if (barNr == 1) + m_key.fromXml(xml); + else { + if (IMPORT_SCORE) { + TkeySignature newKey; + newKey.fromXml(xml); + if (newKey.value() != m_key.value()) + IMPORT_SCORE->keyChanged(newKey); + } else { + xml.skipCurrentElement(); + qDebug() << "[Tmelody] Change key signature in the middle of a melody is not supported!"; + } + } + /** [meter (time signature)] */ + } else if (xml.name() == QLatin1String("time")) { + if (barNr == 1) + m_meter->fromXml(xml); + else { + if (IMPORT_SCORE) { + Tmeter newMeter; + newMeter.fromXml(xml); + if (newMeter.meter() != m_meter->meter()) + IMPORT_SCORE->meterChanged(newMeter); + } else { + xml.skipCurrentElement(); + qDebug() << "[Tmelody] Change time signature (meter) in the middle of a melody is not supported!"; + } + } + } else + xml.skipCurrentElement(); + } + if (barNr == 1) { + if (staffCnt == 2) { + if (clef1 == Tclef::Treble_G && clef2 == Tclef::Bass_F) + m_clef = Tclef::PianoStaffClefs; + else + unsupportedClef(m_clef); + } else + m_clef = clef1; + } else { + if (clefChanged) { + // WARNING: clef change (split staff) will work for all clefs except piano staff + if (IMPORT_SCORE) + IMPORT_SCORE->clefChanged(clef1); + else + qDebug() << "[Tmelody] Change clef in the middle of a melody is not supported!"; } - } else if (xml.name() == QLatin1String("metronome")) { - int tempoWillBe = 120; - auto beatWillBe = Tmeter::EbeatUnit::BeatQuarter; - bool dotWillBe = false; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("beat-unit")) { - auto beatUnit = xml.readElementText(); - if (beatUnit == QLatin1String("quarter")) - beatWillBe = Tmeter::BeatQuarter; - else if (beatUnit == QLatin1String("eighth")) - beatWillBe = Tmeter::BeatEighth; - else if (beatUnit == QLatin1String("half")) - beatWillBe = Tmeter::BeatHalf; - else - qDebug() << "[Tmelody] Unknown 'beat-unit' value. Stay with 'quarter' beat."; - } else if (xml.name() == QLatin1String("beat-unit-dot")) { - dotWillBe = true; - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("per-minute")) { - tempoWillBe = xml.readElementText().toInt(); - } else - xml.skipCurrentElement(); + } + /** [note] */ + } else if (xml.name() == QLatin1String("note")) { + int staffNr = 0, voiceNr = 0; + int *staffPtr = nullptr, *voicePtr = nullptr; + if (m_clef == Tclef::PianoStaffClefs || IMPORT_SCORE) + staffPtr = &staffNr; + if (!madeWithNootka && IMPORT_SCORE) + voicePtr = &voiceNr; + Tchunk ch; + Tchunk *dblDotCh = nullptr; + auto chunkOk = ch.fromXml(xml, staffPtr, voicePtr); + if ((!madeWithNootka && IMPORT_SCORE) || !(chunkOk & Tchunk::e_xmlUnsupported)) { + if (ch.p().isRest() && ch.p().rhythm() == Trhythm::NoRhythm) { // Fix rest duration - if it is undefined - means entire measure + ch.p().setRhythm(m_meter->duration()); // it will reset 'rest' attribute + ch.p().setRest(true); } - if (dotWillBe) { - if (beatWillBe == Tmeter::BeatQuarter) - beatWillBe = Tmeter::BeatQuarterDot; - else - qDebug() << "[Tmelody] Metronome beat with dot only supports quarter. Ignore dot then!"; + if (!(staffPtr && !ch.p().isValid() && ch.p().rhythm() == Trhythm::NoRhythm)) { + // Nootka is not able to import from grand staff of real score (XML) + // and above condition avoids it, but allows to import piano staves created by Nootka itself + + if (m_clef == Tclef::PianoStaffClefs) + ch.p().setOnUpperStaff(staffNr < 2); + if (prevTie > -1) { + // check and fix tie, Nootka supports them only between the same notes + // scoring app may set tie between different ones, but seems like in such case there is no 'stop' tag used (musescore) + Tnote &prevNote = m_notes[prevTie]->p(); + short prevChromatic = prevNote.chromatic(), currChromatic = ch.p().chromatic(); + if ((prevChromatic == currChromatic && ch.p().rtm.tie() == Trhythm::e_noTie) || prevChromatic != currChromatic) { + if (prevNote.rtm.tie() == Trhythm::e_tieCont) + prevNote.rtm.setTie(Trhythm::e_tieEnd); + else if (prevNote.rtm.tie() == Trhythm::e_tieStart) + prevNote.rtm.setTie(Trhythm::e_noTie); + } + } + prevTie = ch.p().rtm.tie() ? m_notes.count() : -1; + if (!technical.isEmpty()) { + // technical data was read before note, so approve it now. But data from <technical> have priority + if (!ch.bowing() && technical.bowing()) + ch.setBowing(technical.bowing()); + if (ch.finger() == -1 && technical.finger() != -1) + ch.setFinger(technical.finger()); + technical.reset(); // reset for the next note + } + if (m_meter->meter() == Tmeter::NoMeter && ch.p().rtm.isValid()) + ch.p().setRhythm(Trhythm::NoRhythm); + bool dblDot = false, tieCont = false; + if (ch.p().rtm.isValid() && chunkOk & Tchunk::e_xmlHasTwoDots) { + tieCont = ch.p().rtm.tie() == Trhythm::e_tieStart || ch.p().rtm.tie() == Trhythm::e_tieCont; + ch.p().rtm.setTie(ch.p().rtm.tie() > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); + dblDot = true; + } + addNote(ch); + if (dblDot) { + // XML import disallows eights with double dots, so divide rhythmic value easily + Trhythm r(static_cast<Trhythm::Erhythm>(ch.p().rhythm() + 2)); + r.setTie(tieCont ? Trhythm::e_tieCont : Trhythm::e_tieEnd); + dblDotCh = new Tchunk(Tnote(ch.p(), r), ch.t()); + addNote(*dblDotCh); + } } - int quarterTempo = Tmeter::quarterTempo(tempoWillBe, beatWillBe); - if (barNr == 1 || tempo() == 0) { // read metronome tempo but only for 1st bar or if not yet set - if (quarterTempo >= 40 && quarterTempo <= 180) { - setTempo(tempoWillBe); - setBeat(beatWillBe); - } else // too fast or too slow - qDebug() << "[Tmelody]" << beatWillBe << "for tempo" << tempoWillBe << "is not supported. (Too fast or too slow)"; + } + if (!madeWithNootka && IMPORT_SCORE) { + // NOTE: This is safe as long as it occurs only during import. + // Old Nootka versions used Dropped bass clef (levels, exams) + // but TlevelSelector, Texam handle that - converts all level stuff into ordinary bass clef + if (m_clef == Tclef::Bass_F_8down) { + fixTransposition = 12; + m_clef = Tclef::Bass_F; } - } else - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("sound")) { - int t = xml.attributes().value(QStringLiteral("tempo")).toInt(); - if (m_tempo == 0) { // always prefer 'metronome' tag, so skip this, if tempo has been set already - if (t >= 40 && t <= 180) // but keep tempo in Nootka supported boundaries - setTempo(t); - else if (t > 180) - setTempo(180); - } - xml.skipCurrentElement(); - } else - xml.skipCurrentElement(); + if (fixTransposition) + ch.p().transpose(fixTransposition); + bool skip = !clefSuppList[staffNr - 1].isEmpty(); + if (chunkOk & Tchunk::e_xmlIsChord) { + if (!skip) { + IMPORT_SCORE->addChordNote(ch); + // TODO: double dots note + } + } else if (chunkOk & Tchunk::e_xmlIsGrace) { + // TODO: grace note if any - so far skipping + } else { + IMPORT_SCORE->addNote(partId, staffNr, voiceNr, ch, skip); + if (dblDotCh && !skip) { + if (fixTransposition) + dblDotCh->p().transpose(fixTransposition); + IMPORT_SCORE->addNote(partId, staffNr, voiceNr, *dblDotCh); + } + if (chunkOk & Tchunk::e_xmlIsStrangeRtm || chunkOk & Tchunk::e_xmlIsTupletStart || chunkOk & Tchunk::e_xmlIsTupletStop) + IMPORT_SCORE->setUnsupported(partId, staffNr, voiceNr, chunkOk); + } + } + if (dblDotCh) + delete dblDotCh; + /** [direction] with bowing/bellow and fingering detected from texts below/above note */ + } else if (xml.name() == QLatin1String("direction")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("direction-type")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("words")) { // fingering and bowing can be read this way + auto words = xml.readElementText(); + if (words == QLatin1String("(A)")) + technical.setBowing(Ttechnical::BowDown); + else if (words == QLatin1String("(C)")) + technical.setBowing(Ttechnical::BowUp); + else { + bool isNumber = false; + int finger = words.toInt(&isNumber); + if (isNumber) + technical.setFinger(finger); + } + } else if (xml.name() == QLatin1String("metronome")) { + int tempoWillBe = 120; + auto beatWillBe = Tmeter::EbeatUnit::BeatQuarter; + bool dotWillBe = false; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("beat-unit")) { + auto beatUnit = xml.readElementText(); + if (beatUnit == QLatin1String("quarter")) + beatWillBe = Tmeter::BeatQuarter; + else if (beatUnit == QLatin1String("eighth")) + beatWillBe = Tmeter::BeatEighth; + else if (beatUnit == QLatin1String("half")) + beatWillBe = Tmeter::BeatHalf; + else + qDebug() << "[Tmelody] Unknown 'beat-unit' value. Stay with 'quarter' beat."; + } else if (xml.name() == QLatin1String("beat-unit-dot")) { + dotWillBe = true; + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("per-minute")) { + tempoWillBe = xml.readElementText().toInt(); + } else + xml.skipCurrentElement(); + } + if (dotWillBe) { + if (beatWillBe == Tmeter::BeatQuarter) + beatWillBe = Tmeter::BeatQuarterDot; + else + qDebug() << "[Tmelody] Metronome beat with dot only supports quarter. Ignore dot then!"; + } + int quarterTempo = Tmeter::quarterTempo(tempoWillBe, beatWillBe); + if (barNr == 1 || tempo() == 0) { // read metronome tempo but only for 1st bar or if not yet set + if (quarterTempo >= 40 && quarterTempo <= 180) { + setTempo(tempoWillBe); + setBeat(beatWillBe); + } else // too fast or too slow + qDebug() << "[Tmelody]" << beatWillBe << "for tempo" << tempoWillBe << "is not supported. (Too fast or too slow)"; + } + } else + xml.skipCurrentElement(); + } + } else if (xml.name() == QLatin1String("sound")) { + int t = xml.attributes().value(QStringLiteral("tempo")).toInt(); + if (m_tempo == 0) { // always prefer 'metronome' tag, so skip this, if tempo has been set already + if (t >= 40 && t <= 180) // but keep tempo in Nootka supported boundaries + setTempo(t); + else if (t > 180) + setTempo(180); + } + xml.skipCurrentElement(); + } else + xml.skipCurrentElement(); + } + } else + xml.skipCurrentElement(); } - } - else + } else xml.skipCurrentElement(); - } - } else - xml.skipCurrentElement(); - - } - if (tempo() == 0) { - setBeat(m_meter->optimalBeat()); - setTempo(qRound(60.0 * Tmeter::beatTempoFactor(m_beat))); -// qDebug() << "[Tmelody] Tempo was not read from this melody file. Set it to" << m_tempo << "with beat" << m_beat; - } - return ok; + } + if (tempo() == 0) { + setBeat(m_meter->optimalBeat()); + setTempo(qRound(60.0 * Tmeter::beatTempoFactor(m_beat))); + // qDebug() << "[Tmelody] Tempo was not read from this melody file. Set it to" << m_tempo << "with beat" << m_beat; + } + return ok; } - -bool Tmelody::saveToMusicXml(const QString& xmlFileName, int transposition) { +bool Tmelody::saveToMusicXml(const QString &xmlFileName, int transposition) +{ // if compressed file then invoke its compression/deflation if (xmlFileName.endsWith(QStringLiteral(".mxl"))) - return saveToMXL(xmlFileName, transposition); + return saveToMXL(xmlFileName, transposition); QFile file(xmlFileName); if (file.open(QIODevice::WriteOnly)) { @@ -505,316 +499,316 @@ bool Tmelody::saveToMusicXml(const QString& xmlFileName, int transposition) { return false; } - -void Tmelody::writeXmlStream(QXmlStreamWriter &xml, int transposition) { +void Tmelody::writeXmlStream(QXmlStreamWriter &xml, int transposition) +{ xml.setAutoFormatting(true); xml.setAutoFormattingIndent(2); xml.writeStartDocument(); - xml.writeDTD(QStringLiteral("<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 3.1 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">")); + xml.writeDTD( + QStringLiteral("<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 3.1 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">")); xml.writeStartElement(QStringLiteral("score-partwise")); - xml.writeStartElement(QStringLiteral("work")); - xml.writeTextElement(QStringLiteral("work-title"), title()); - xml.writeEndElement(); // work - xml.writeStartElement(QStringLiteral("identification")); - xml.writeStartElement(QStringLiteral("creator")); - xml.writeAttribute(QStringLiteral("type"), QStringLiteral("composer")); - xml.writeCharacters(composer()); - xml.writeEndElement(); // creator composer - xml.writeStartElement(QStringLiteral("encoding")); - xml.writeTextElement(QStringLiteral("software"), QLatin1String("Nootka ") + QString(NOOTKA_VERSION)); - xml.writeTextElement(QStringLiteral("encoding-date"), QDate::currentDate().toString(Qt::ISODate)); - xml.writeEndElement(); // encoding - xml.writeEndElement(); // identification - xml.writeStartElement(QStringLiteral("part-list")); - xml.writeStartElement(QStringLiteral("score-part")); - xml.writeAttribute(QStringLiteral("id"), QStringLiteral("P1")); - xml.writeTextElement(QStringLiteral("part-name"), QStringLiteral("Nootka")); -// xml.writeTextElement("part-name", "instrument"); - xml.writeEndElement(); // score-part - xml.writeEndElement(); //part-list - xml.writeStartElement(QStringLiteral("part")); - xml.writeAttribute(QStringLiteral("id"), QStringLiteral("P1")); - toXml(xml, transposition); - xml.writeEndElement(); // part + xml.writeStartElement(QStringLiteral("work")); + xml.writeTextElement(QStringLiteral("work-title"), title()); + xml.writeEndElement(); // work + xml.writeStartElement(QStringLiteral("identification")); + xml.writeStartElement(QStringLiteral("creator")); + xml.writeAttribute(QStringLiteral("type"), QStringLiteral("composer")); + xml.writeCharacters(composer()); + xml.writeEndElement(); // creator composer + xml.writeStartElement(QStringLiteral("encoding")); + xml.writeTextElement(QStringLiteral("software"), QLatin1String("Nootka ") + QString(NOOTKA_VERSION)); + xml.writeTextElement(QStringLiteral("encoding-date"), QDate::currentDate().toString(Qt::ISODate)); + xml.writeEndElement(); // encoding + xml.writeEndElement(); // identification + xml.writeStartElement(QStringLiteral("part-list")); + xml.writeStartElement(QStringLiteral("score-part")); + xml.writeAttribute(QStringLiteral("id"), QStringLiteral("P1")); + xml.writeTextElement(QStringLiteral("part-name"), QStringLiteral("Nootka")); + // xml.writeTextElement("part-name", "instrument"); + xml.writeEndElement(); // score-part + xml.writeEndElement(); // part-list + xml.writeStartElement(QStringLiteral("part")); + xml.writeAttribute(QStringLiteral("id"), QStringLiteral("P1")); + toXml(xml, transposition); + xml.writeEndElement(); // part xml.writeEndElement(); // score-partwise xml.writeEndDocument(); } - -bool Tmelody::saveToMXL(const QString& xmlFileName, int transposition) { - QByteArray xmlData; - QXmlStreamWriter xml(&xmlData); - writeXmlStream(xml, transposition); - return Tzip::zipMusicXml(xmlFileName, &xmlData); +bool Tmelody::saveToMXL(const QString &xmlFileName, int transposition) +{ + QByteArray xmlData; + QXmlStreamWriter xml(&xmlData); + writeXmlStream(xml, transposition); + return Tzip::zipMusicXml(xmlFileName, &xmlData); } +bool Tmelody::grabFromMusicXml(const QString &xmlFileName) +{ + QFile file(xmlFileName); + bool ok = true; -bool Tmelody::grabFromMusicXml(const QString& xmlFileName) { - QFile file(xmlFileName); - bool ok = true; - - // if compressed file then invoke its extraction - if (xmlFileName.endsWith(QStringLiteral(".mxl"))) - return grabFromMXL(xmlFileName); + // if compressed file then invoke its extraction + if (xmlFileName.endsWith(QStringLiteral(".mxl"))) + return grabFromMXL(xmlFileName); - if (file.open(QIODevice::ReadOnly)) { - QXmlStreamReader xml(&file); - ok = processXMLData(xml); - file.close(); - }; + if (file.open(QIODevice::ReadOnly)) { + QXmlStreamReader xml(&file); + ok = processXMLData(xml); + file.close(); + }; - return ok; + return ok; } - -bool Tmelody::grabFromMXL(const QString& xmlFileName) { - bool ok = true; - - QByteArray xmlData; - Tzip::getXmlFromZip(xmlFileName, &xmlData); - if (xmlData.size() > 0) { - QXmlStreamReader xmlScore(xmlData); - if (xmlScore.error() != QXmlStreamReader::NoError) +bool Tmelody::grabFromMXL(const QString &xmlFileName) +{ + bool ok = true; + + QByteArray xmlData; + Tzip::getXmlFromZip(xmlFileName, &xmlData); + if (xmlData.size() > 0) { + QXmlStreamReader xmlScore(xmlData); + if (xmlScore.error() != QXmlStreamReader::NoError) + ok = false; + else + ok = processXMLData(xmlScore); + } else ok = false; - else - ok = processXMLData(xmlScore); - } else - ok = false; - return ok; + return ok; } - -void Tmelody::appendMelody(Tmelody* otherM) { - if (!otherM) - return; - if (!lastMeasure().isFull() && appendWarn) { - qDebug() << "[Tmelody] appending melody but the last measure is not finished. Notes in appended measures will be shifted!"; - appendWarn = false; - } - for (int n = 0; n < otherM->length(); ++n) - addNote(*otherM->note(n)); +void Tmelody::appendMelody(Tmelody *otherM) +{ + if (!otherM) + return; + if (!lastMeasure().isFull() && appendWarn) { + qDebug() << "[Tmelody] appending melody but the last measure is not finished. Notes in appended measures will be shifted!"; + appendWarn = false; + } + for (int n = 0; n < otherM->length(); ++n) + addNote(*otherM->note(n)); } - -void Tmelody::split(int byEveryBar, QList<Tmelody*>& parts) { - if (m_meter->meter() == Tmeter::NoMeter) { // only one measure containing all bareheaded notes of a melody - Tmeasure& firstBar = p_measures.first(); - if (firstBar.count() > byEveryBar) { - for (int n = byEveryBar; n < firstBar.count(); ++n) { - Tmelody* m; - if (n % byEveryBar == 0) { // create new Tmelody instance every byEveryBar number - m = new Tmelody(title(), key()); - m->setMeter(meter()->meter()); - m->setTempo(tempo()); - m->setClef(clef()); - parts << m; - } else { // or just melody created before - m = parts.last(); - } - // then add to it byEveryBar number of notes - m->addNote(firstBar.note(n)); - } - // remove notes moved to another melodies from the list - int nCnt = firstBar.count() - 1; - for (int n = nCnt; n > byEveryBar - 1; --n) { - firstBar.removeLastNote(); - m_notes.removeLast(); - } - } - } else { - if (measuresCount() > byEveryBar) { - auto lastNote = &p_measures[byEveryBar - 1].lastNote(); - if (lastNote->p().rtm.tie()) { - if (lastNote->p().rtm.tie() == Trhythm::e_tieStart) - lastNote->p().rtm.setTie(Trhythm::e_noTie); - else - lastNote->p().rtm.setTie(Trhythm::e_tieEnd); - } - for (int b = byEveryBar; b < measuresCount(); ++b) { - Tmelody* m; - if (b % byEveryBar == 0) { // create new Tmelody instance every byEveryBar number - m = new Tmelody(title(), key()); - m->setMeter(meter()->meter()); - m->setTempo(tempo()); - m->setClef(clef()); - parts << m; - } else { // or just melody created before - m = parts.last(); - } - // then add add to it all notes from the current measure - Tmeasure& bar = p_measures[b]; - for (int n = 0; n < bar.count(); ++n) { - Tchunk& ch = bar.note(n); - if (n == 0) { - if (ch.p().rtm.tie()) { - if (ch.p().rtm.tie() == Trhythm::e_tieEnd) - ch.p().rtm.setTie(Trhythm::e_noTie); - else - ch.p().rtm.setTie(Trhythm::e_tieStart); +void Tmelody::split(int byEveryBar, QList<Tmelody *> &parts) +{ + if (m_meter->meter() == Tmeter::NoMeter) { // only one measure containing all bareheaded notes of a melody + Tmeasure &firstBar = p_measures.first(); + if (firstBar.count() > byEveryBar) { + for (int n = byEveryBar; n < firstBar.count(); ++n) { + Tmelody *m; + if (n % byEveryBar == 0) { // create new Tmelody instance every byEveryBar number + m = new Tmelody(title(), key()); + m->setMeter(meter()->meter()); + m->setTempo(tempo()); + m->setClef(clef()); + parts << m; + } else { // or just melody created before + m = parts.last(); } - } else if (n == bar.count() - 1) { - if (ch.p().rtm.tie()) { - if (ch.p().rtm.tie() == Trhythm::e_tieStart) - ch.p().rtm.setTie(Trhythm::e_noTie); - else - ch.p().rtm.setTie(Trhythm::e_tieEnd); - } + // then add to it byEveryBar number of notes + m->addNote(firstBar.note(n)); + } + // remove notes moved to another melodies from the list + int nCnt = firstBar.count() - 1; + for (int n = nCnt; n > byEveryBar - 1; --n) { + firstBar.removeLastNote(); + m_notes.removeLast(); } - m->addNote(ch); - } } - // remove notes moved to another melodies from the list - int l = length(); - bool oneToRemoveFound = false; - for (int n = 0; n < l; ++n) { - if (oneToRemoveFound) - m_notes.removeLast(); - else if (note(n) == lastNote) - oneToRemoveFound = true; + } else { + if (measuresCount() > byEveryBar) { + auto lastNote = &p_measures[byEveryBar - 1].lastNote(); + if (lastNote->p().rtm.tie()) { + if (lastNote->p().rtm.tie() == Trhythm::e_tieStart) + lastNote->p().rtm.setTie(Trhythm::e_noTie); + else + lastNote->p().rtm.setTie(Trhythm::e_tieEnd); + } + for (int b = byEveryBar; b < measuresCount(); ++b) { + Tmelody *m; + if (b % byEveryBar == 0) { // create new Tmelody instance every byEveryBar number + m = new Tmelody(title(), key()); + m->setMeter(meter()->meter()); + m->setTempo(tempo()); + m->setClef(clef()); + parts << m; + } else { // or just melody created before + m = parts.last(); + } + // then add add to it all notes from the current measure + Tmeasure &bar = p_measures[b]; + for (int n = 0; n < bar.count(); ++n) { + Tchunk &ch = bar.note(n); + if (n == 0) { + if (ch.p().rtm.tie()) { + if (ch.p().rtm.tie() == Trhythm::e_tieEnd) + ch.p().rtm.setTie(Trhythm::e_noTie); + else + ch.p().rtm.setTie(Trhythm::e_tieStart); + } + } else if (n == bar.count() - 1) { + if (ch.p().rtm.tie()) { + if (ch.p().rtm.tie() == Trhythm::e_tieStart) + ch.p().rtm.setTie(Trhythm::e_noTie); + else + ch.p().rtm.setTie(Trhythm::e_tieEnd); + } + } + m->addNote(ch); + } + } + // remove notes moved to another melodies from the list + int l = length(); + bool oneToRemoveFound = false; + for (int n = 0; n < l; ++n) { + if (oneToRemoveFound) + m_notes.removeLast(); + else if (note(n) == lastNote) + oneToRemoveFound = true; + } + // remove moved measures either + int mc = measuresCount(); + for (int b = byEveryBar; b < mc; ++b) + p_measures.removeLast(); } - // remove moved measures either - int mc = measuresCount(); - for (int b = byEveryBar; b < mc; ++b) - p_measures.removeLast(); - } - } + } } +void Tmelody::transpose(int semis, bool outScaleToRest, const Tnote &loNote, const Tnote &hiNote) +{ + if (semis == 0 || length() == 0) + return; // nothing to transpose + + bool doInScaleCheck = loNote.isValid() && hiNote.isValid(); + auto lo = doInScaleCheck ? loNote.chromatic() : 0; + auto hi = doInScaleCheck ? hiNote.chromatic() : 0; + + for (int n = 0; n < length(); ++n) { + Tnote ¬eSeg = m_notes[n]->p(); + int transOff = 0; + Trhythm transRtm(noteSeg.rtm); + auto transChrom = noteSeg.chromatic() + semis; + if (doInScaleCheck) { + if (outScaleToRest) { + if (transChrom > hi || transChrom < lo) { + transRtm.setRest(true); + transRtm.setTie(Trhythm::e_noTie); + transRtm.setBeam(Trhythm::e_noBeam); + } + } else { + if (transChrom > hi) + transOff = -12; // when too high drop octave down + else if (transChrom < lo) + transOff = 12; // when too low raise octave up + } + } -void Tmelody::transpose(int semis, bool outScaleToRest, const Tnote& loNote, const Tnote& hiNote) { - if (semis == 0 || length() == 0) - return; // nothing to transpose - - bool doInScaleCheck = loNote.isValid() && hiNote.isValid(); - auto lo = doInScaleCheck ? loNote.chromatic() : 0; - auto hi = doInScaleCheck ? hiNote.chromatic() : 0; - - for (int n = 0; n < length(); ++n) { - Tnote& noteSeg = m_notes[n]->p(); - int transOff = 0; - Trhythm transRtm(noteSeg.rtm); - auto transChrom = noteSeg.chromatic() + semis; - if (doInScaleCheck) { - if (outScaleToRest) { - if (transChrom > hi || transChrom < lo) { - transRtm.setRest(true); - transRtm.setTie(Trhythm::e_noTie); - transRtm.setBeam(Trhythm::e_noBeam); - } - } else { - if (transChrom > hi) - transOff = -12; // when too high drop octave down - else if (transChrom < lo) - transOff = 12; // when too low raise octave up - } - } + Tnote transposed(noteSeg, transRtm); + if (transRtm.isRest()) + transposed.setNote(0); + else + transposed.transpose(semis + transOff); - Tnote transposed(noteSeg, transRtm); - if (transRtm.isRest()) - transposed.setNote(0); - else - transposed.transpose(semis + transOff); - - auto inKeyNote = m_key.inKey(transposed); - if (inKeyNote.isValid()) { - transposed.setNote(inKeyNote.note()); - transposed.setOctave(inKeyNote.octave()); - transposed.setAlter(inKeyNote.alter()); - } + auto inKeyNote = m_key.inKey(transposed); + if (inKeyNote.isValid()) { + transposed.setNote(inKeyNote.note()); + transposed.setOctave(inKeyNote.octave()); + transposed.setAlter(inKeyNote.alter()); + } - noteSeg = transposed; - } + noteSeg = transposed; + } } +void Tmelody::fromNoteStruct(QList<TnoteStruct> &ns) +{ + for (int i = 0; i < ns.size(); ++i) + addNote(Tchunk(ns[i].pitch)); +} +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -void Tmelody::fromNoteStruct(QList<TnoteStruct>& ns) { - for (int i = 0; i < ns.size(); ++i) - addNote(Tchunk(ns[i].pitch)); -} +bool Tmelody::processXMLData(QXmlStreamReader &xml) +{ + bool ok = true; + if (xml.error() != QXmlStreamReader::NoError) { + qDebug() << "[Tmelody] XML stream error:" << xml.error(); + return false; + } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -bool Tmelody::processXMLData(QXmlStreamReader& xml) { - bool ok = true; - if (xml.error() != QXmlStreamReader::NoError) { - qDebug() << "[Tmelody] XML stream error:" << xml.error(); - return false; - } - - // DTD is ignored, only <score-partwise> key is required as the main - if (xml.readNextStartElement()) { - if (xml.name() != QLatin1String("score-partwise")) { - qDebug() << "[Tmelody] File is not MusicXML format."; - return false; + // DTD is ignored, only <score-partwise> key is required as the main + if (xml.readNextStartElement()) { + if (xml.name() != QLatin1String("score-partwise")) { + qDebug() << "[Tmelody] File is not MusicXML format."; + return false; + } } - } - - bool madeWithNootka = false; - int partId = 0; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("movement-title")) { - m_title = xml.readElementText(); - } else if (xml.name() == QLatin1String("work")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("work-title")) + + bool madeWithNootka = false; + int partId = 0; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("movement-title")) { m_title = xml.readElementText(); - else - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("identification")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("creator")) { - if (xml.attributes().value("type").toString() == QLatin1String("composer")) - m_composer = xml.readElementText(); - else - xml.skipCurrentElement(); - } else if (xml.name() == QLatin1String("encoding")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("software")) { - if (xml.readElementText().startsWith(QLatin1String("Nootka"))) - madeWithNootka = true; - } else + } else if (xml.name() == QLatin1String("work")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("work-title")) + m_title = xml.readElementText(); + else xml.skipCurrentElement(); - } - } else - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("part-list")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("score-part")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("part-name")) { - if (IMPORT_SCORE) - IMPORT_SCORE->addPartName(xml.readElementText()); + } + } else if (xml.name() == QLatin1String("identification")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("creator")) { + if (xml.attributes().value("type").toString() == QLatin1String("composer")) + m_composer = xml.readElementText(); else - xml.skipCurrentElement(); + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("encoding")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("software")) { + if (xml.readElementText().startsWith(QLatin1String("Nootka"))) + madeWithNootka = true; + } else + xml.skipCurrentElement(); + } } else xml.skipCurrentElement(); - } - } else - xml.skipCurrentElement(); - } - } else if (xml.name() == QLatin1String("part")) { - partId++; - if (!fromXml(xml, madeWithNootka, partId)) - ok = false; - } else - xml.skipCurrentElement(); - } + } + } else if (xml.name() == QLatin1String("part-list")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("score-part")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("part-name")) { + if (IMPORT_SCORE) + IMPORT_SCORE->addPartName(xml.readElementText()); + else + xml.skipCurrentElement(); + } else + xml.skipCurrentElement(); + } + } else + xml.skipCurrentElement(); + } + } else if (xml.name() == QLatin1String("part")) { + partId++; + if (!fromXml(xml, madeWithNootka, partId)) + ok = false; + } else + xml.skipCurrentElement(); + } - if (IMPORT_SCORE) - IMPORT_SCORE->sumarize(); + if (IMPORT_SCORE) + IMPORT_SCORE->sumarize(); - return ok; + return ok; } - -void Tmelody::prepend(const Tchunk& n) { - p_measures.first().prepend(n); - m_notes.prepend(&p_measures.first().note(0)); +void Tmelody::prepend(const Tchunk &n) +{ + p_measures.first().prepend(n); + m_notes.prepend(&p_measures.first().note(0)); } - diff --git a/src/libs/core/music/tmelody.h b/src/libs/core/music/tmelody.h index f4117305270ae39e2e65ec18106bc6f588514590..3c62041f9b72f3ce90eda2c819afe952cc2a30db 100644 --- a/src/libs/core/music/tmelody.h +++ b/src/libs/core/music/tmelody.h @@ -19,174 +19,173 @@ #ifndef TMELODY_H #define TMELODY_H - -#include <QtCore/qstring.h> -#include <QtCore/qlist.h> -#include "tkeysignature.h" -#include "tclef.h" #include "tchunk.h" +#include "tclef.h" +#include "tkeysignature.h" #include "tmeasure.h" - +#include <QtCore/qlist.h> +#include <QtCore/qstring.h> class Tmeter; class TnoteStruct; - /** * Class describing a musical melody - sequence of notes (Tchunk) * Also it is able to save/load a melody into/from MusicXML structure. */ class NOOTKACORE_EXPORT Tmelody { - - friend class TmelodyPart; + friend class TmelodyPart; public: - Tmelody(const QString& title = QString(), const TkeySignature& k = TkeySignature()); - Tmelody(const Tmelody& other); - ~Tmelody(); - - QString title() const { return m_title; } - void setTitle(const QString& t) { m_title = t; } - - QString composer() const { return m_composer; } - void setComposer(const QString& c) { m_composer = c; } - - /** - * A length of the melody (notes number) - */ - int length() const { return m_notes.size(); } - - void addNote(const Tchunk& n); - - /** - * Deletes all notes from the melody. - * Also title, composer and resets key, if set to true - */ - void clear(bool withCredits = true, bool withKey = true); - - /** - * Copies all notes to given @p chunks list - */ - void toList(QList<Tchunk>& chunks); - - /** - * Replaces note @p noteNr with notes in the list. - * List of notes must have the same duration like replacing note. - */ - void swapWithNotes(int noteNr, const QList<Tchunk>& notes); - - /** - * A pointer to note @p index - */ - Tchunk* note(int index) { return m_notes[index]; } - Tchunk chunk(int index) const { return *m_notes[index]; } - - Tmeasure& measure(int nr) { return p_measures[nr]; } - Tmeasure& lastMeasure() { return p_measures.last(); } - int measuresCount() const { return p_measures.count(); } - - int tempo() const { return m_tempo; } - void setTempo(int tmp) { m_tempo = tmp; } - - /** - * Tempo of quarter notes (per minute), independent on beat unit - */ - int quarterTempo() const { return Tmeter::quarterTempo(m_tempo, m_beat); } - - /** - * Set both tempo and beat at once - */ - void setMetronome(int mTempo, Tmeter::EbeatUnit beatUnit) { setTempo(mTempo); setBeat(beatUnit); } - - Tmeter::EbeatUnit beat() const { return m_beat; } - void setBeat(Tmeter::EbeatUnit bu) { m_beat = bu; } - - TkeySignature key() const { return m_key; } - void setKey(const TkeySignature& k) { m_key = k; } - - Tclef::EclefType clef() const { return m_clef; } - void setClef(Tclef::EclefType type) { m_clef = type; } - - Tmeter* meter() const { return m_meter; } - void setMeter(int m); - - /** - * @p trans is instrument transposition in semitones, - * but doesn't changes notes pitch. - * For music XML structure it is converted to tags: - * <chromatic> (less than 12) - * <octave-change> above 12 - * but <diatonic> key is never used so far - * see: http://usermanuals.musicxml.com/MusicXML/MusicXML.htm#EL-MusicXML-transpose.htm - */ - void toXml(QXmlStreamWriter& xml, int trans = 0); - bool fromXml(QXmlStreamReader& xml, bool madeWithNootka = false, int partId = 0); - bool saveToMusicXml(const QString& xmlFileName, int transposition); - void writeXmlStream(QXmlStreamWriter &xml, int transposition); - bool saveToMXL(const QString& xmlFileName, int transposition); - - bool grabFromMusicXml(const QString& xmlFileName); - bool grabFromMXL(const QString& xmlFileName); - - /** - * Adds notes of @p otherM (other melody) to this one. - * Doesn't check for meter/key/clef match - * and is the last measure full. - */ - void appendMelody(Tmelody* otherM); - - /** - * Divides this melody by @p byEveryBar number - * and stores further parts into @p QList<Tmelody*>. - */ - void split(int byEveryBar, QList<Tmelody*>& parts); - - /** - * Transposes all notes in this melody by @p semis semitones: - * when @p semis is positive - transposes up, when negative - down. - * @p outScaleToRest determines when notes out of scale is changed to rest - * or raised/dropped octave down/up (when set to @p FALSE). - * @p loNote and @p hiNote determines scale (ambitus) - * in which transposed notes have to fit. - * But if loNote or hiNote is invalid, checking is skipped. - * NOTE: When transposition is performed by a key signature - * set the new key signature before @p transpose() call. - */ - void transpose(int semis, bool outScaleToRest, const Tnote& loNote, const Tnote& hiNote); - - /** - * Converts given list to melody - */ - void fromNoteStruct(QList<TnoteStruct>& ns); + Tmelody(const QString &title = QString(), const TkeySignature &k = TkeySignature()); + Tmelody(const Tmelody &other); + ~Tmelody(); + + QString title() const { return m_title; } + void setTitle(const QString &t) { m_title = t; } + + QString composer() const { return m_composer; } + void setComposer(const QString &c) { m_composer = c; } + + /** + * A length of the melody (notes number) + */ + int length() const { return m_notes.size(); } + + void addNote(const Tchunk &n); + + /** + * Deletes all notes from the melody. + * Also title, composer and resets key, if set to true + */ + void clear(bool withCredits = true, bool withKey = true); + + /** + * Copies all notes to given @p chunks list + */ + void toList(QList<Tchunk> &chunks); + + /** + * Replaces note @p noteNr with notes in the list. + * List of notes must have the same duration like replacing note. + */ + void swapWithNotes(int noteNr, const QList<Tchunk> ¬es); + + /** + * A pointer to note @p index + */ + Tchunk *note(int index) { return m_notes[index]; } + Tchunk chunk(int index) const { return *m_notes[index]; } + + Tmeasure &measure(int nr) { return p_measures[nr]; } + Tmeasure &lastMeasure() { return p_measures.last(); } + int measuresCount() const { return p_measures.count(); } + + int tempo() const { return m_tempo; } + void setTempo(int tmp) { m_tempo = tmp; } + + /** + * Tempo of quarter notes (per minute), independent on beat unit + */ + int quarterTempo() const { return Tmeter::quarterTempo(m_tempo, m_beat); } + + /** + * Set both tempo and beat at once + */ + void setMetronome(int mTempo, Tmeter::EbeatUnit beatUnit) + { + setTempo(mTempo); + setBeat(beatUnit); + } + + Tmeter::EbeatUnit beat() const { return m_beat; } + void setBeat(Tmeter::EbeatUnit bu) { m_beat = bu; } + + TkeySignature key() const { return m_key; } + void setKey(const TkeySignature &k) { m_key = k; } + + Tclef::EclefType clef() const { return m_clef; } + void setClef(Tclef::EclefType type) { m_clef = type; } + + Tmeter *meter() const { return m_meter; } + void setMeter(int m); + + /** + * @p trans is instrument transposition in semitones, + * but doesn't changes notes pitch. + * For music XML structure it is converted to tags: + * <chromatic> (less than 12) + * <octave-change> above 12 + * but <diatonic> key is never used so far + * see: http://usermanuals.musicxml.com/MusicXML/MusicXML.htm#EL-MusicXML-transpose.htm + */ + void toXml(QXmlStreamWriter &xml, int trans = 0); + bool fromXml(QXmlStreamReader &xml, bool madeWithNootka = false, int partId = 0); + bool saveToMusicXml(const QString &xmlFileName, int transposition); + void writeXmlStream(QXmlStreamWriter &xml, int transposition); + bool saveToMXL(const QString &xmlFileName, int transposition); + + bool grabFromMusicXml(const QString &xmlFileName); + bool grabFromMXL(const QString &xmlFileName); + + /** + * Adds notes of @p otherM (other melody) to this one. + * Doesn't check for meter/key/clef match + * and is the last measure full. + */ + void appendMelody(Tmelody *otherM); + + /** + * Divides this melody by @p byEveryBar number + * and stores further parts into @p QList<Tmelody*>. + */ + void split(int byEveryBar, QList<Tmelody *> &parts); + + /** + * Transposes all notes in this melody by @p semis semitones: + * when @p semis is positive - transposes up, when negative - down. + * @p outScaleToRest determines when notes out of scale is changed to rest + * or raised/dropped octave down/up (when set to @p FALSE). + * @p loNote and @p hiNote determines scale (ambitus) + * in which transposed notes have to fit. + * But if loNote or hiNote is invalid, checking is skipped. + * NOTE: When transposition is performed by a key signature + * set the new key signature before @p transpose() call. + */ + void transpose(int semis, bool outScaleToRest, const Tnote &loNote, const Tnote &hiNote); + + /** + * Converts given list to melody + */ + void fromNoteStruct(QList<TnoteStruct> &ns); protected: + /** + * Common routine to parse musicXML data in @p QXmlStreamReader. + */ + bool processXMLData(QXmlStreamReader &xml); - /** - * Common routine to parse musicXML data in @p QXmlStreamReader. - */ - bool processXMLData(QXmlStreamReader& xml); - - /** - * Adds @p n note at the beginning of this melody. - * This method is intended to fill anacrusis measures with rests. - */ - void prepend(const Tchunk& n); + /** + * Adds @p n note at the beginning of this melody. + * This method is intended to fill anacrusis measures with rests. + */ + void prepend(const Tchunk &n); protected: - QList<Tmeasure> p_measures; + QList<Tmeasure> p_measures; private: - QString m_title; - QString m_composer; - QList<Tchunk*> m_notes; /**< List of pointers to ordered notes */ - int m_tempo; - Tmeter::EbeatUnit m_beat = Tmeter::BeatQuarter; - TkeySignature m_key; - Tmeter *m_meter = nullptr; - Tclef::EclefType m_clef; + QString m_title; + QString m_composer; + QList<Tchunk *> m_notes; /**< List of pointers to ordered notes */ + int m_tempo; + Tmeter::EbeatUnit m_beat = Tmeter::BeatQuarter; + TkeySignature m_key; + Tmeter *m_meter = nullptr; + Tclef::EclefType m_clef; }; -Q_DECLARE_METATYPE(Tmelody*) +Q_DECLARE_METATYPE(Tmelody *) #endif // TMELODY_H diff --git a/src/libs/core/music/tmeter.cpp b/src/libs/core/music/tmeter.cpp index da99067c9d4d332b4e71d21bd4d31cc5184daa8e..74fc3def17180097588890d603ea844d1d1fd5a5 100644 --- a/src/libs/core/music/tmeter.cpp +++ b/src/libs/core/music/tmeter.cpp @@ -21,186 +21,216 @@ #include <QtCore/qdebug.h> - -int Tmeter::upper() const { - switch (m_meter) { - case Meter_2_4: return 2; +int Tmeter::upper() const +{ + switch (m_meter) { + case Meter_2_4: + return 2; case Meter_3_4: - case Meter_3_8: return 3; - case Meter_4_4: return 4; + case Meter_3_8: + return 3; + case Meter_4_4: + return 4; case Meter_5_4: - case Meter_5_8: return 5; + case Meter_5_8: + return 5; case Meter_6_4: - case Meter_6_8: return 6; + case Meter_6_8: + return 6; case Meter_7_4: - case Meter_7_8: return 7; - case Meter_9_8: return 9; - case Meter_12_8: return 12; - default: return 0; - } + case Meter_7_8: + return 7; + case Meter_9_8: + return 9; + case Meter_12_8: + return 12; + default: + return 0; + } } - -int Tmeter::lower() const { - int v = static_cast<int>(m_meter); - if (v > 0) { - if (v <= 32) - return 4; - else - return 8; - } else - return 0; +int Tmeter::lower() const +{ + int v = static_cast<int>(m_meter); + if (v > 0) { + if (v <= 32) + return 4; + else + return 8; + } else + return 0; } - -int Tmeter::duration() const { - if (m_meter == NoMeter) - return 1; - else - return (RVALUE / lower()) * upper(); +int Tmeter::duration() const +{ + if (m_meter == NoMeter) + return 1; + else + return (RVALUE / lower()) * upper(); } - #define ZERO (0xe0c0) // first digit -QString Tmeter::symbol() const { - if (m_meter == NoMeter) - return QString(); - return QString(QChar(ZERO + qRound(log(static_cast<qreal>(m_meter)) / 0.69314718055994530941723212145818))); //s; +QString Tmeter::symbol() const +{ + if (m_meter == NoMeter) + return QString(); + return QString(QChar(ZERO + qRound(log(static_cast<qreal>(m_meter)) / 0.69314718055994530941723212145818))); // s; } - -void Tmeter::debug(const QString& text) { - qDebug() << text << "Meter" << QString("%1/%2").arg(upper()).arg(lower()); +void Tmeter::debug(const QString &text) +{ + qDebug() << text << "Meter" << QString("%1/%2").arg(upper()).arg(lower()); } - -int Tmeter::countTo() const { - switch (m_meter) { - case Meter_2_4: return 2; +int Tmeter::countTo() const +{ + switch (m_meter) { + case Meter_2_4: + return 2; case Meter_3_4: - case Meter_3_8: return 3; - case Meter_4_4: return 4; + case Meter_3_8: + return 3; + case Meter_4_4: + return 4; case Meter_5_4: - case Meter_5_8: return 5; + case Meter_5_8: + return 5; case Meter_6_4: - case Meter_6_8: return 3; + case Meter_6_8: + return 3; case Meter_7_4: - case Meter_7_8: return 7; + case Meter_7_8: + return 7; case Meter_9_8: - case Meter_12_8: return 3; - default: return 4; - } + case Meter_12_8: + return 3; + default: + return 4; + } } - -void Tmeter::toXml(QXmlStreamWriter& xml) const { - if (m_meter != NoMeter) { - xml.writeStartElement(QLatin1String("time")); - xml.writeTextElement(QLatin1String("beats"), QString::number(upper())); - xml.writeTextElement(QLatin1String("beat-type"), QString::number(lower())); - xml.writeEndElement(); // time - } +void Tmeter::toXml(QXmlStreamWriter &xml) const +{ + if (m_meter != NoMeter) { + xml.writeStartElement(QLatin1String("time")); + xml.writeTextElement(QLatin1String("beats"), QString::number(upper())); + xml.writeTextElement(QLatin1String("beat-type"), QString::number(lower())); + xml.writeEndElement(); // time + } } - -bool Tmeter::fromXml(QXmlStreamReader& xml) { - bool ok = true; - int up = 0, lo = 0; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("beats")) - up = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("beat-type")) - lo = xml.readElementText().toInt(); - else - xml.skipCurrentElement(); - } - m_meter = valueToMeter(up, lo); - if ((up || lo) && m_meter == NoMeter) { - qDebug() << "[Tmeter] XML unsupported meter" << up << "/" << lo << "revert to 4/4"; - m_meter = Meter_4_4; - ok = false; - } - return ok; +bool Tmeter::fromXml(QXmlStreamReader &xml) +{ + bool ok = true; + int up = 0, lo = 0; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("beats")) + up = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("beat-type")) + lo = xml.readElementText().toInt(); + else + xml.skipCurrentElement(); + } + m_meter = valueToMeter(up, lo); + if ((up || lo) && m_meter == NoMeter) { + qDebug() << "[Tmeter] XML unsupported meter" << up << "/" << lo << "revert to 4/4"; + m_meter = Meter_4_4; + ok = false; + } + return ok; } - -Tmeter::Emeter Tmeter::valueToMeter(int up, int lo) { - if (lo == 8) { - switch (up) { - case 3: return Meter_3_8; - case 5: return Meter_5_8; - case 6: return Meter_6_8; - case 7: return Meter_7_8; - case 9: return Meter_9_8; - case 12: return Meter_12_8; - default: return NoMeter; - } - } else if (lo == 4) { - switch (up) { - case 2: return Meter_2_4; - case 3: return Meter_3_4; - case 4: return Meter_4_4; - case 5: return Meter_5_4; - case 6: return Meter_6_4; - case 7: return Meter_7_4; - default: return NoMeter; - } - } - return NoMeter; +Tmeter::Emeter Tmeter::valueToMeter(int up, int lo) +{ + if (lo == 8) { + switch (up) { + case 3: + return Meter_3_8; + case 5: + return Meter_5_8; + case 6: + return Meter_6_8; + case 7: + return Meter_7_8; + case 9: + return Meter_9_8; + case 12: + return Meter_12_8; + default: + return NoMeter; + } + } else if (lo == 4) { + switch (up) { + case 2: + return Meter_2_4; + case 3: + return Meter_3_4; + case 4: + return Meter_4_4; + case 5: + return Meter_5_4; + case 6: + return Meter_6_4; + case 7: + return Meter_7_4; + default: + return NoMeter; + } + } + return NoMeter; } - /** * By assigning duration of @p 1 to meter group of @p Tmeter::NoMeter we are bending score reality. * There is no note with such duration, but this way @p TmeasureObject will keep one note per measure * and shifting measures among staves will work without any changes - the same way as such as for rhythmic notes. * Only drawbacks are: many measure objects (wasting RAM), accidentals for every single note (measure contains only one note) */ -void Tmeter::fillMeterGroups(QList<quint8>& durationList) { - durationList.clear(); - if (m_meter == NoMeter) - durationList << 1; - else if (lower() == 4) { // simple grouping: one group for each quarter - durationList << 24 << 48; // 2/4 and above - if (m_meter > Meter_2_4) - durationList << 72; - if (m_meter > Meter_3_4) - durationList << 96; - if (m_meter > Meter_4_4) - durationList << 120; - if (m_meter > Meter_5_4) - durationList << 144; - if (m_meter > Meter_6_4) - durationList << 168; - } else { - if (m_meter == Meter_3_8) - durationList << 36; - else if (m_meter == Meter_5_8) - durationList << 36 << 60; - else if (m_meter == Meter_6_8) - durationList << 36 << 72; - else if (m_meter == Meter_7_8) - durationList << 36 << 60 << 84; - else if (m_meter == Meter_9_8) - durationList << 36 << 72 << 108; - else if (m_meter == Meter_12_8) - durationList << 36 << 72 << 108 << 144; - } +void Tmeter::fillMeterGroups(QList<quint8> &durationList) +{ + durationList.clear(); + if (m_meter == NoMeter) + durationList << 1; + else if (lower() == 4) { // simple grouping: one group for each quarter + durationList << 24 << 48; // 2/4 and above + if (m_meter > Meter_2_4) + durationList << 72; + if (m_meter > Meter_3_4) + durationList << 96; + if (m_meter > Meter_4_4) + durationList << 120; + if (m_meter > Meter_5_4) + durationList << 144; + if (m_meter > Meter_6_4) + durationList << 168; + } else { + if (m_meter == Meter_3_8) + durationList << 36; + else if (m_meter == Meter_5_8) + durationList << 36 << 60; + else if (m_meter == Meter_6_8) + durationList << 36 << 72; + else if (m_meter == Meter_7_8) + durationList << 36 << 60 << 84; + else if (m_meter == Meter_9_8) + durationList << 36 << 72 << 108; + else if (m_meter == Meter_12_8) + durationList << 36 << 72 << 108 << 144; + } } - -Tmeter::EbeatUnit Tmeter::optimalBeat(Tmeter::Emeter m) { - if (m <= Meter_7_4) // all time signatures with quarter, also when no meter NoMeter - return BeatQuarter; - if (m == Meter_6_8 || m == Meter_9_8 || m == Meter_12_8) - return BeatQuarterDot; - return BeatEighth; +Tmeter::EbeatUnit Tmeter::optimalBeat(Tmeter::Emeter m) +{ + if (m <= Meter_7_4) // all time signatures with quarter, also when no meter NoMeter + return BeatQuarter; + if (m == Meter_6_8 || m == Meter_9_8 || m == Meter_12_8) + return BeatQuarterDot; + return BeatEighth; } - -qreal Tmeter::beatTempoFactor(Tmeter::EbeatUnit bu) { - static const qreal beatsArray[4] = { 1.0, 2.0, 0.66666666666, 0.5 }; - return beatsArray[static_cast<int>(bu)]; +qreal Tmeter::beatTempoFactor(Tmeter::EbeatUnit bu) +{ + static const qreal beatsArray[4] = {1.0, 2.0, 0.66666666666, 0.5}; + return beatsArray[static_cast<int>(bu)]; } - diff --git a/src/libs/core/music/tmeter.h b/src/libs/core/music/tmeter.h index 69ba8f8f5631419f167043a92d9ae287e24a076e..4ca9de62a059a15bf1bb2d88619fe702d8fbbb36 100644 --- a/src/libs/core/music/tmeter.h +++ b/src/libs/core/music/tmeter.h @@ -19,120 +19,120 @@ #ifndef TMETER_H #define TMETER_H - -#include <nootkacoreglobal.h> #include <QtCore/qobject.h> #include <QtCore/qxmlstream.h> - +#include <nootkacoreglobal.h> /** * Describes musical time signature */ class NOOTKACORE_EXPORT Tmeter { - - Q_GADGET + Q_GADGET public: - - enum Emeter { - NoMeter = 0, - Meter_2_4 = 1, Meter_3_4 = 2, Meter_4_4 = 4, Meter_5_4 = 8, Meter_6_4 = 16, Meter_7_4 = 32, - Meter_3_8 = 64, Meter_5_8 = 128, Meter_6_8 = 256, Meter_7_8 = 512, Meter_9_8 = 1024, Meter_12_8 = 2048 - }; - - Q_ENUM(Emeter) - - Tmeter(Emeter meter = NoMeter) : m_meter(meter) {} - - Q_INVOKABLE Tmeter::Emeter meter() const { return m_meter; } - Q_INVOKABLE void setMeter(Tmeter::Emeter m) { m_meter = m; } - - /** - * Returns upper digit of time signature - */ - Q_INVOKABLE int upper() const; - - /** - * Returns lower digit of time signature - */ - Q_INVOKABLE int lower() const; - - /** - * Returns text symbol of this time signature, - * containing Scorek (or Bravura) font characters - */ - Q_INVOKABLE QString symbol() const; - - /** - * Returns numeric value representing duration of single measure, - * which is based on Trhythm calculation (RVALUE) - * 3/4 is 72, 4/4 is 96 (RVALUE), etc. - */ - Q_INVOKABLE int duration() const; - - /** - * returns number of up to counts depending on meter - */ - Q_INVOKABLE int countTo() const; - - void toXml(QXmlStreamWriter& xml) const; - bool fromXml(QXmlStreamReader& xml); - - /** - * Prints current meter to std out with given text - */ - Q_INVOKABLE void debug(const QString& text = QString()); - - bool operator==(const Tmeter& m) const { return meter() == m.meter(); } - bool operator!=(const Tmeter& m) const { return meter() != m.meter(); } - - /** - * Converts given @p up upper numerator and @p lo lower denominator - * into @p Tmeter::Emeter enumerator - */ - static Emeter valueToMeter(int up, int lo); - - void fillMeterGroups(QList<quint8>& durationList); - - enum EbeatUnit { - BeatQuarter = 0, - BeatEighth = 1, - BeatQuarterDot = 2, - BeatHalf = 3 - }; - - Q_ENUM(EbeatUnit) - - static EbeatUnit optimalBeat(Emeter m); - - /** - * Optimal beat unit for current meter - */ - Q_INVOKABLE Tmeter::EbeatUnit optimalBeat() const { return optimalBeat(m_meter); } - - /** - * Returns tempo multiplexer depending on beat unit: - * i.e.: for eighth note it is @p 2 as such as tempo with eighths is 2 times faster than tempo with quarters - */ - Q_INVOKABLE static qreal beatTempoFactor(Tmeter::EbeatUnit bu); - - /** - * Tempo of quarter notes (per minute), independent on beat unit - */ - static int quarterTempo(int beatTempo, Tmeter::EbeatUnit beatUnit) { - return qRound(static_cast<qreal>(beatTempo) / beatTempoFactor(beatUnit)); - } - - /** - * Tempo of quarter notes (per minute), independent on beat unit - */ - static int quarterTempo(int beatTempo, int beatUnit) { - return Tmeter::quarterTempo(beatTempo, static_cast<EbeatUnit>(beatUnit)); - } + enum Emeter { + NoMeter = 0, + Meter_2_4 = 1, + Meter_3_4 = 2, + Meter_4_4 = 4, + Meter_5_4 = 8, + Meter_6_4 = 16, + Meter_7_4 = 32, + Meter_3_8 = 64, + Meter_5_8 = 128, + Meter_6_8 = 256, + Meter_7_8 = 512, + Meter_9_8 = 1024, + Meter_12_8 = 2048 + }; + + Q_ENUM(Emeter) + + Tmeter(Emeter meter = NoMeter) + : m_meter(meter) + { + } + + Q_INVOKABLE Tmeter::Emeter meter() const { return m_meter; } + Q_INVOKABLE void setMeter(Tmeter::Emeter m) { m_meter = m; } + + /** + * Returns upper digit of time signature + */ + Q_INVOKABLE int upper() const; + + /** + * Returns lower digit of time signature + */ + Q_INVOKABLE int lower() const; + + /** + * Returns text symbol of this time signature, + * containing Scorek (or Bravura) font characters + */ + Q_INVOKABLE QString symbol() const; + + /** + * Returns numeric value representing duration of single measure, + * which is based on Trhythm calculation (RVALUE) + * 3/4 is 72, 4/4 is 96 (RVALUE), etc. + */ + Q_INVOKABLE int duration() const; + + /** + * returns number of up to counts depending on meter + */ + Q_INVOKABLE int countTo() const; + + void toXml(QXmlStreamWriter &xml) const; + bool fromXml(QXmlStreamReader &xml); + + /** + * Prints current meter to std out with given text + */ + Q_INVOKABLE void debug(const QString &text = QString()); + + bool operator==(const Tmeter &m) const { return meter() == m.meter(); } + bool operator!=(const Tmeter &m) const { return meter() != m.meter(); } + + /** + * Converts given @p up upper numerator and @p lo lower denominator + * into @p Tmeter::Emeter enumerator + */ + static Emeter valueToMeter(int up, int lo); + + void fillMeterGroups(QList<quint8> &durationList); + + enum EbeatUnit { BeatQuarter = 0, BeatEighth = 1, BeatQuarterDot = 2, BeatHalf = 3 }; + + Q_ENUM(EbeatUnit) + + static EbeatUnit optimalBeat(Emeter m); + + /** + * Optimal beat unit for current meter + */ + Q_INVOKABLE Tmeter::EbeatUnit optimalBeat() const { return optimalBeat(m_meter); } + + /** + * Returns tempo multiplexer depending on beat unit: + * i.e.: for eighth note it is @p 2 as such as tempo with eighths is 2 times faster than tempo with quarters + */ + Q_INVOKABLE static qreal beatTempoFactor(Tmeter::EbeatUnit bu); + + /** + * Tempo of quarter notes (per minute), independent on beat unit + */ + static int quarterTempo(int beatTempo, Tmeter::EbeatUnit beatUnit) { return qRound(static_cast<qreal>(beatTempo) / beatTempoFactor(beatUnit)); } + + /** + * Tempo of quarter notes (per minute), independent on beat unit + */ + static int quarterTempo(int beatTempo, int beatUnit) { return Tmeter::quarterTempo(beatTempo, static_cast<EbeatUnit>(beatUnit)); } private: - Emeter m_meter; + Emeter m_meter; }; #endif // TMETER_H diff --git a/src/libs/core/music/tnamestylefilter.cpp b/src/libs/core/music/tnamestylefilter.cpp index 56cedb43438aa148af9b2563dd02fdcda24896e3..6fca34c4831fa3573a2bd5d0bf3975967a52841d 100644 --- a/src/libs/core/music/tnamestylefilter.cpp +++ b/src/libs/core/music/tnamestylefilter.cpp @@ -18,48 +18,45 @@ #include "tnamestylefilter.h" -bool* TnameStyleFilter::m_is7th_B = nullptr; -Tnote::EnameStyle* TnameStyleFilter::m_solfegeStyle = nullptr; - +bool *TnameStyleFilter::m_is7th_B = nullptr; +Tnote::EnameStyle *TnameStyleFilter::m_solfegeStyle = nullptr; TnameStyleFilter::TnameStyleFilter() -{} - +{ +} /** * TODO * @p solfegeStyle is not used (ignored) * it can be set the same way as letter styles */ -void TnameStyleFilter::setStyleFilter(bool* is7th_B, Tnote::EnameStyle* solfegeStyle) { - m_is7th_B = is7th_B; - m_solfegeStyle = solfegeStyle; +void TnameStyleFilter::setStyleFilter(bool *is7th_B, Tnote::EnameStyle *solfegeStyle) +{ + m_is7th_B = is7th_B; + m_solfegeStyle = solfegeStyle; } - -Tnote::EnameStyle TnameStyleFilter::get(Tnote::EnameStyle style) { - if (m_is7th_B) { - if (style == Tnote::e_italiano_Si || style == Tnote::e_russian_Ci) - return style; -// return *(m_solfegeStyle); TODO ignored - - if (style == Tnote::e_deutsch_His || style == Tnote::e_nederl_Bis) { - if (*(m_is7th_B)) - return Tnote::e_nederl_Bis; - else - return Tnote::e_deutsch_His; +Tnote::EnameStyle TnameStyleFilter::get(Tnote::EnameStyle style) +{ + if (m_is7th_B) { + if (style == Tnote::e_italiano_Si || style == Tnote::e_russian_Ci) + return style; + // return *(m_solfegeStyle); TODO ignored + + if (style == Tnote::e_deutsch_His || style == Tnote::e_nederl_Bis) { + if (*(m_is7th_B)) + return Tnote::e_nederl_Bis; + else + return Tnote::e_deutsch_His; + } + + if (style == Tnote::e_english_Bb || style == Tnote::e_norsk_Hb) { + if (*(m_is7th_B)) + return Tnote::e_english_Bb; + else + return Tnote::e_norsk_Hb; + } } - if (style == Tnote::e_english_Bb|| style == Tnote::e_norsk_Hb) { - if (*(m_is7th_B)) - return Tnote::e_english_Bb; - else - return Tnote::e_norsk_Hb; - } - } - - return style; + return style; } - - - diff --git a/src/libs/core/music/tnamestylefilter.h b/src/libs/core/music/tnamestylefilter.h index 02f9679bcb49b3dd5bb614e0f66f40e608c3ca27..6741757cd06cf2455aa9a64a4b38e22da4156628 100644 --- a/src/libs/core/music/tnamestylefilter.h +++ b/src/libs/core/music/tnamestylefilter.h @@ -19,10 +19,8 @@ #ifndef TNAMESTYLEFILTER_H #define TNAMESTYLEFILTER_H - #include "tnote.h" - /** * This is very simple class, more to describe than to do. * This class converts given style according to global Nootka settings. @@ -40,35 +38,32 @@ */ class NOOTKACORE_EXPORT TnameStyleFilter { - public: TnameStyleFilter(); - /** - * Initialize filter with pointers to global settings - */ - static void setStyleFilter(bool *is7th_B, Tnote::EnameStyle *solfegeStyle); + /** + * Initialize filter with pointers to global settings + */ + static void setStyleFilter(bool *is7th_B, Tnote::EnameStyle *solfegeStyle); - /** - * Perform filtering - */ - static Tnote::EnameStyle get(Tnote::EnameStyle style); + /** + * Perform filtering + */ + static Tnote::EnameStyle get(Tnote::EnameStyle style); - /** - * Usually it is pointer to global @p is7th_B. - */ - static bool* is7th_B() { return m_is7th_B; } - - /** - * Usually it is pointer to global solfegeStyle. - */ - static Tnote::EnameStyle* solfegeStyle() { return m_solfegeStyle; } + /** + * Usually it is pointer to global @p is7th_B. + */ + static bool *is7th_B() { return m_is7th_B; } + /** + * Usually it is pointer to global solfegeStyle. + */ + static Tnote::EnameStyle *solfegeStyle() { return m_solfegeStyle; } private: - static bool *m_is7th_B; - static Tnote::EnameStyle *m_solfegeStyle; - + static bool *m_is7th_B; + static Tnote::EnameStyle *m_solfegeStyle; }; #endif // TNAMESTYLEFILTER_H diff --git a/src/libs/core/music/tnote.cpp b/src/libs/core/music/tnote.cpp index 17183b8d7e6dc6c9d0845f3e7217d68d62c4f044..b80a5d50d8f1e4609606a16c21eb94e61a25dbcf 100644 --- a/src/libs/core/music/tnote.cpp +++ b/src/libs/core/music/tnote.cpp @@ -19,71 +19,90 @@ #include "tnote.h" #include "tnamestylefilter.h" +#include <iostream> #include <sstream> #include <string> -#include <iostream> #include <unistd.h> #include <QtCore/qvariant.h> #include <QtGui/qguiapplication.h> +const std::string Letters[7] = {"C", "D", "E", "F", "G", "A", "H"}; +const std::string signsAcid[5] = { + "bb", + "b", + "", + "#", + "x", +}; -const std::string Letters [7] = { "C", "D", "E", "F", "G", "A", "H"}; -const std::string signsAcid[5] = { "bb", "b", "", "#", "x", }; - - -std::string Tnote::alterSymbol(int id) { - if (id > -1 && id < 5) - return signsAcid[id]; - return ""; +std::string Tnote::alterSymbol(int id) +{ + if (id > -1 && id < 5) + return signsAcid[id]; + return ""; } - -std::string IntToString(int num) { - std::ostringstream myStream; - myStream << num << std::flush; - return(myStream.str()); +std::string IntToString(int num) +{ + std::ostringstream myStream; + myStream << num << std::flush; + return (myStream.str()); } - -std::string CharToString(char chr) { - std::ostringstream myStream; - myStream << (int) chr << std::flush; - return(myStream.str()); +std::string CharToString(char chr) +{ + std::ostringstream myStream; + myStream << (int)chr << std::flush; + return (myStream.str()); } - -QString accidInSpan(char accid) { - QString accTxt; - switch (accid) { - case -2: accTxt = QStringLiteral("B"); break; - case -1: accTxt = QStringLiteral("b"); break; - case 1: accTxt = QStringLiteral("#"); break; - case 2: accTxt = QStringLiteral("x"); break; - default: break; - } - return QString("<span style=\"font-family: nootka;\">%1</span>").arg(accTxt); +QString accidInSpan(char accid) +{ + QString accTxt; + switch (accid) { + case -2: + accTxt = QStringLiteral("B"); + break; + case -1: + accTxt = QStringLiteral("b"); + break; + case 1: + accTxt = QStringLiteral("#"); + break; + case 2: + accTxt = QStringLiteral("x"); + break; + default: + break; + } + return QString("<span style=\"font-family: nootka;\">%1</span>").arg(accTxt); } /** * Quick map (array) to get chromatic note number - first filed is invalid */ -static qint8 chromaticMap[8] { 0, 1, 3, 5, 6, 8, 10, 12 }; - -static char setChromaticMap[12][2] { {1, 0}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {4, 0}, {4, 1}, {5, 0}, {5, 1}, {6, 0}, {6, 1}, {7, 0} }; - -const char* const fullOctaveNames[8] = { QT_TRANSLATE_NOOP("TnoteName", "Subcontra octave"), QT_TRANSLATE_NOOP("TnoteName", "Contra octave"), - QT_TRANSLATE_NOOP("TnoteName", "Great octave"), QT_TRANSLATE_NOOP("TnoteName", "Small octave"), - QT_TRANSLATE_NOOP("TnoteName", "One-line octave"), QT_TRANSLATE_NOOP("TnoteName", "Two-line octave"), - QT_TRANSLATE_NOOP("TnoteName", "Three-line octave"), QT_TRANSLATE_NOOP("TnoteName", "Four-line octave") -}; - -static const char* const shortOctaveNames[8] = { QT_TRANSLATE_NOOP("TnoteName", "Sub"), QT_TRANSLATE_NOOP("TnoteName", "Contra"), - QT_TRANSLATE_NOOP("TnoteName", "Great"), QT_TRANSLATE_NOOP("TnoteName", "Small"), - QT_TRANSLATE_NOOP("TnoteName", "1-line"), QT_TRANSLATE_NOOP("TnoteName", "2-line"), - QT_TRANSLATE_NOOP("TnoteName", "3-line"), QT_TRANSLATE_NOOP("TnoteName", "4-line") -}; - +static qint8 chromaticMap[8]{0, 1, 3, 5, 6, 8, 10, 12}; + +static char setChromaticMap[12][2]{{1, 0}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {4, 0}, {4, 1}, {5, 0}, {5, 1}, {6, 0}, {6, 1}, {7, 0}}; + +const char *const fullOctaveNames[8] = {QT_TRANSLATE_NOOP("TnoteName", "Subcontra octave"), + QT_TRANSLATE_NOOP("TnoteName", "Contra octave"), + QT_TRANSLATE_NOOP("TnoteName", "Great octave"), + QT_TRANSLATE_NOOP("TnoteName", "Small octave"), + QT_TRANSLATE_NOOP("TnoteName", "One-line octave"), + QT_TRANSLATE_NOOP("TnoteName", "Two-line octave"), + QT_TRANSLATE_NOOP("TnoteName", "Three-line octave"), + QT_TRANSLATE_NOOP("TnoteName", "Four-line octave")}; + +static const char *const shortOctaveNames[8] = {QT_TRANSLATE_NOOP("TnoteName", "Sub"), + QT_TRANSLATE_NOOP("TnoteName", "Contra"), + QT_TRANSLATE_NOOP("TnoteName", "Great"), + QT_TRANSLATE_NOOP("TnoteName", "Small"), + QT_TRANSLATE_NOOP("TnoteName", "1-line"), + QT_TRANSLATE_NOOP("TnoteName", "2-line"), + QT_TRANSLATE_NOOP("TnoteName", "3-line"), + QT_TRANSLATE_NOOP("TnoteName", "4-line")}; /*static*/ std::string Tnote::m_solmization[7] = {"Do", "Re", "Mi", "Fa", "Sol", "La", "Si"}; @@ -92,257 +111,294 @@ std::string Tnote::m_solmizationRu[7] = {"До", "Ре", "Ми", "Фа", "Сол Tnote::EnameStyle Tnote::defaultStyle = Tnote::e_norsk_Hb; bool Tnote::scientificOctaves = false; +// ############################################################################################# +// ################################ PUBLIC ##################################################### +// ############################################################################################# -//############################################################################################# -//################################ PUBLIC ##################################################### -//############################################################################################# - -void Tnote::setChromatic(short int noteNr) { - int nr = (noteNr + 143) % 12; - setNote(setChromaticMap[nr][0]); - setAlter(setChromaticMap[nr][1]); - setOctave((noteNr + 143) / 12 - 12); +void Tnote::setChromatic(short int noteNr) +{ + int nr = (noteNr + 143) % 12; + setNote(setChromaticMap[nr][0]); + setAlter(setChromaticMap[nr][1]); + setOctave((noteNr + 143) / 12 - 12); } - -short Tnote::chromatic() const { - return chromaticMap[static_cast<int>(Tnote::note())] + Tnote::octave() * 12 + Tnote::alter(); +short Tnote::chromatic() const +{ + return chromaticMap[static_cast<int>(Tnote::note())] + Tnote::octave() * 12 + Tnote::alter(); } - -Tnote Tnote::showAsNatural() const { - Tnote n(chromatic()); - n.setRhythm(rtm); - n.setOnUpperStaff(onUpperStaff()); - return n; +Tnote Tnote::showAsNatural() const +{ + Tnote n(chromatic()); + n.setRhythm(rtm); + n.setOnUpperStaff(onUpperStaff()); + return n; } - -Tnote Tnote::showWithFlat() const { - if (alter() != Tnote::e_Flat) { - Tnote outputNote = Tnote::showAsNatural(); -// Tnote::showAsNatural() always returns notes with sharps or neutral, and never returns H# or E#, but C or F. - if (outputNote.alter() == Tnote::e_Sharp) { - outputNote.setNote(outputNote.note() + 1); - outputNote.setAlter(Tnote::e_Flat); - } else if (outputNote.note() == 7 || outputNote.note() == 3) { // so only for H changed to Cb we have to increment octave, - if (outputNote.note() == 7) { - outputNote.setOctave(outputNote.octave() + 1); - outputNote.setNote(1); +Tnote Tnote::showWithFlat() const +{ + if (alter() != Tnote::e_Flat) { + Tnote outputNote = Tnote::showAsNatural(); + // Tnote::showAsNatural() always returns notes with sharps or neutral, and never returns H# or E#, but C or F. + if (outputNote.alter() == Tnote::e_Sharp) { + outputNote.setNote(outputNote.note() + 1); + outputNote.setAlter(Tnote::e_Flat); + } else if (outputNote.note() == 7 || outputNote.note() == 3) { // so only for H changed to Cb we have to increment octave, + if (outputNote.note() == 7) { + outputNote.setOctave(outputNote.octave() + 1); + outputNote.setNote(1); + } + if (outputNote.note() == 3) // and for E simply convert to Fb + outputNote.setNote(4); + outputNote.setAlter(Tnote::e_Flat); } - if (outputNote.note() == 3) //and for E simply convert to Fb - outputNote.setNote(4); - outputNote.setAlter(Tnote::e_Flat); + outputNote.setOnUpperStaff(onUpperStaff()); + return outputNote; } - outputNote.setOnUpperStaff(onUpperStaff()); - return outputNote; - } - return Tnote(note(), octave(), alter()); + return Tnote(note(), octave(), alter()); } - -Tnote Tnote::showWithSharp() const { - if (alter() != Tnote::e_Sharp) { - Tnote outputNote = Tnote::showAsNatural(); - if (outputNote.alter() == Tnote::e_Natural) { - if (outputNote.note() == 4) { - outputNote.setNote(3); - outputNote.setAlter(Tnote::e_Sharp); - } else if (outputNote.note() == 1) { - outputNote.setOctave(outputNote.octave() - 1); - outputNote.setNote(7); - outputNote.setAlter(Tnote::e_Sharp); - } +Tnote Tnote::showWithSharp() const +{ + if (alter() != Tnote::e_Sharp) { + Tnote outputNote = Tnote::showAsNatural(); + if (outputNote.alter() == Tnote::e_Natural) { + if (outputNote.note() == 4) { + outputNote.setNote(3); + outputNote.setAlter(Tnote::e_Sharp); + } else if (outputNote.note() == 1) { + outputNote.setOctave(outputNote.octave() - 1); + outputNote.setNote(7); + outputNote.setAlter(Tnote::e_Sharp); + } + } + outputNote.setOnUpperStaff(onUpperStaff()); + return outputNote; } - outputNote.setOnUpperStaff(onUpperStaff()); - return outputNote; - } - return Tnote(note(), octave(), alter()); + return Tnote(note(), octave(), alter()); } - -Tnote Tnote::showWithDoubleSharp() const { - if (alter() != Tnote::e_DoubleSharp) { - Tnote outputNote = Tnote::showAsNatural(); - if (outputNote.alter() == Tnote::e_Natural) { - if (outputNote.note() == 4) { - outputNote.setAlter(Tnote::e_Sharp); - outputNote.setNote(3); - } else if (outputNote.note() == 1) { - outputNote.setAlter(Tnote::e_Sharp); - outputNote.setNote(7); - outputNote.setOctave(outputNote.octave() - 1); - } else { - outputNote.setAlter(Tnote::e_DoubleSharp); - outputNote.setNote(note() - 1); - } +Tnote Tnote::showWithDoubleSharp() const +{ + if (alter() != Tnote::e_DoubleSharp) { + Tnote outputNote = Tnote::showAsNatural(); + if (outputNote.alter() == Tnote::e_Natural) { + if (outputNote.note() == 4) { + outputNote.setAlter(Tnote::e_Sharp); + outputNote.setNote(3); + } else if (outputNote.note() == 1) { + outputNote.setAlter(Tnote::e_Sharp); + outputNote.setNote(7); + outputNote.setOctave(outputNote.octave() - 1); + } else { + outputNote.setAlter(Tnote::e_DoubleSharp); + outputNote.setNote(note() - 1); + } + } + outputNote.setOnUpperStaff(onUpperStaff()); + return outputNote; } - outputNote.setOnUpperStaff(onUpperStaff()); - return outputNote; - } - return Tnote(note(), octave(), alter()); + return Tnote(note(), octave(), alter()); } - -Tnote Tnote::showWithDoubleFlat() const { - if (alter() != Tnote::e_DoubleFlat) { - Tnote outputNote = Tnote(note(), octave(), alter()); - if (outputNote.alter() == Tnote::e_Flat && (outputNote.note() == 3 || outputNote.note() == 7)) { - if (outputNote.note() == 3) { - outputNote.setAlter(Tnote::e_DoubleFlat); - outputNote.setNote(4); - } else { - outputNote.setAlter(Tnote::e_DoubleFlat); - outputNote.setNote(1); - outputNote.setOctave(outputNote.octave() + 1); - } - } else { - outputNote = outputNote.showAsNatural(); - if (outputNote.alter() == Tnote::e_Natural) { +Tnote Tnote::showWithDoubleFlat() const +{ + if (alter() != Tnote::e_DoubleFlat) { + Tnote outputNote = Tnote(note(), octave(), alter()); + if (outputNote.alter() == Tnote::e_Flat && (outputNote.note() == 3 || outputNote.note() == 7)) { if (outputNote.note() == 3) { - outputNote.setAlter(Tnote::e_Flat); - outputNote.setNote(4); - } else if (outputNote.note() == 7) { - outputNote.setAlter(Tnote::e_Flat); - outputNote.setNote(1); - outputNote.setOctave(outputNote.octave() + 1); - } else { - outputNote.setAlter(Tnote::e_DoubleFlat); - outputNote.setNote(outputNote.note() + 1); - } - } else if (outputNote.alter() == Tnote::e_Sharp) { - if (outputNote.note() == 2) { outputNote.setAlter(Tnote::e_DoubleFlat); outputNote.setNote(4); - } else if (outputNote.note() == 6) { + } else { outputNote.setAlter(Tnote::e_DoubleFlat); outputNote.setNote(1); outputNote.setOctave(outputNote.octave() + 1); - } else - outputNote = outputNote.showWithFlat(); - } + } + } else { + outputNote = outputNote.showAsNatural(); + if (outputNote.alter() == Tnote::e_Natural) { + if (outputNote.note() == 3) { + outputNote.setAlter(Tnote::e_Flat); + outputNote.setNote(4); + } else if (outputNote.note() == 7) { + outputNote.setAlter(Tnote::e_Flat); + outputNote.setNote(1); + outputNote.setOctave(outputNote.octave() + 1); + } else { + outputNote.setAlter(Tnote::e_DoubleFlat); + outputNote.setNote(outputNote.note() + 1); + } + } else if (outputNote.alter() == Tnote::e_Sharp) { + if (outputNote.note() == 2) { + outputNote.setAlter(Tnote::e_DoubleFlat); + outputNote.setNote(4); + } else if (outputNote.note() == 6) { + outputNote.setAlter(Tnote::e_DoubleFlat); + outputNote.setNote(1); + outputNote.setOctave(outputNote.octave() + 1); + } else + outputNote = outputNote.showWithFlat(); + } + } + outputNote.setRhythm(rtm); + outputNote.setOnUpperStaff(onUpperStaff()); + return outputNote; } - outputNote.setRhythm(rtm); - outputNote.setOnUpperStaff(onUpperStaff()); - return outputNote; - } - return Tnote(note(), octave(), alter()); + return Tnote(note(), octave(), alter()); } - -TnotesList Tnote::getTheSameNotes(bool enableDbAccids) const { - TnotesList notesList; - short cnt; // counter of notes. With double accidentals is 5 (4) without 3 (2) - notesList.push_back(Tnote(note(),octave(),alter())); - notesList[0].setOnUpperStaff(onUpperStaff()); - if (notesList[0].alter() != Tnote::e_Natural) - notesList.push_back(notesList[0].showAsNatural()); - if (notesList[0].alter() != Tnote::e_Sharp) - notesList.push_back(notesList[0].showWithSharp()); - if (notesList[0].alter() != Tnote::e_Flat) - notesList.push_back(notesList[0].showWithFlat()); - if (enableDbAccids) { - if (notesList[0].alter() != Tnote::e_DoubleSharp) - notesList.push_back(notesList[0].showWithDoubleSharp()); - if (notesList[0].alter() != Tnote::e_DoubleFlat) - notesList.push_back(notesList[0].showWithDoubleFlat()); - cnt = 4; - } else - cnt = 2; - for(int m = 0; m < cnt; m++) { - for( int n = cnt; n > m; n--) { - if ((notesList[m].note() != 0) && (notesList[n].note() != 0)) { - if (notesList[m].compareNotes(notesList[n])) - notesList[n].setNote(0); - } +TnotesList Tnote::getTheSameNotes(bool enableDbAccids) const +{ + TnotesList notesList; + short cnt; // counter of notes. With double accidentals is 5 (4) without 3 (2) + notesList.push_back(Tnote(note(), octave(), alter())); + notesList[0].setOnUpperStaff(onUpperStaff()); + if (notesList[0].alter() != Tnote::e_Natural) + notesList.push_back(notesList[0].showAsNatural()); + if (notesList[0].alter() != Tnote::e_Sharp) + notesList.push_back(notesList[0].showWithSharp()); + if (notesList[0].alter() != Tnote::e_Flat) + notesList.push_back(notesList[0].showWithFlat()); + if (enableDbAccids) { + if (notesList[0].alter() != Tnote::e_DoubleSharp) + notesList.push_back(notesList[0].showWithDoubleSharp()); + if (notesList[0].alter() != Tnote::e_DoubleFlat) + notesList.push_back(notesList[0].showWithDoubleFlat()); + cnt = 4; + } else + cnt = 2; + for (int m = 0; m < cnt; m++) { + for (int n = cnt; n > m; n--) { + if ((notesList[m].note() != 0) && (notesList[n].note() != 0)) { + if (notesList[m].compareNotes(notesList[n])) + notesList[n].setNote(0); + } + } + } + TnotesList::iterator m = notesList.end(); + while (m != notesList.begin()) { + --m; + if (!(*m).isValid()) + notesList.erase(m); } - } - TnotesList::iterator m = notesList.end(); - while(m != notesList.begin()){ - --m; - if (!(*m).isValid()) - notesList.erase(m); - } - return notesList; + return notesList; } +std::string Tnote::getName(Tnote::EnameStyle notation, bool showOctave) const +{ + std::string noteStr; + if (!isValid()) + return "none"; -std::string Tnote::getName(Tnote::EnameStyle notation, bool showOctave) const { - std::string noteStr; - if (!isValid()) - return "none"; - - switch (TnameStyleFilter::get(notation)) { - case e_italiano_Si: - noteStr = m_solmization[note() - 1] + signsAcid[alter() + 2]; - break; - case e_russian_Ci: - noteStr = m_solmizationRu[note() - 1] + signsAcid[alter() + 2]; - break; - case e_deutsch_His: - noteStr = Letters[note() - 1]; - switch (alter()) { - case e_Natural: break; - case e_DoubleSharp: noteStr = noteStr + "isis"; break; - case e_Sharp: noteStr = noteStr + "is"; break; - case e_DoubleFlat: switch (note()) { - case 3: noteStr = noteStr + "ses"; break; - case 6: noteStr = noteStr + "sas"; break; - default: noteStr = noteStr + "eses"; break; - } - break; - case e_Flat: switch (note()) { - case 3: noteStr = noteStr + "s"; break; - case 6: noteStr = noteStr + "s"; break; - case 7: noteStr = "B"; break; - default: noteStr = noteStr + "es"; break; - } + switch (TnameStyleFilter::get(notation)) { + case e_italiano_Si: + noteStr = m_solmization[note() - 1] + signsAcid[alter() + 2]; + break; + case e_russian_Ci: + noteStr = m_solmizationRu[note() - 1] + signsAcid[alter() + 2]; + break; + case e_deutsch_His: + noteStr = Letters[note() - 1]; + switch (alter()) { + case e_Natural: + break; + case e_DoubleSharp: + noteStr = noteStr + "isis"; + break; + case e_Sharp: + noteStr = noteStr + "is"; + break; + case e_DoubleFlat: + switch (note()) { + case 3: + noteStr = noteStr + "ses"; + break; + case 6: + noteStr = noteStr + "sas"; + break; + default: + noteStr = noteStr + "eses"; + break; + } + break; + case e_Flat: + switch (note()) { + case 3: + noteStr = noteStr + "s"; + break; + case 6: + noteStr = noteStr + "s"; + break; + case 7: + noteStr = "B"; + break; + default: + noteStr = noteStr + "es"; break; - } - break; + } + break; + } + break; case e_nederl_Bis: noteStr = Letters[note() - 1]; - if (note() == 7) noteStr = "B"; - switch( alter() ){ - case e_Natural: break; - case e_DoubleSharp: noteStr = noteStr + "isis"; break; - case e_Sharp: noteStr = noteStr + "is"; break; - case e_DoubleFlat: - switch (note()) { - case 3: noteStr = noteStr + "ses"; break; - case 6: noteStr = noteStr + "ses"; break; - default: noteStr = noteStr + "eses"; break; + if (note() == 7) + noteStr = "B"; + switch (alter()) { + case e_Natural: + break; + case e_DoubleSharp: + noteStr = noteStr + "isis"; + break; + case e_Sharp: + noteStr = noteStr + "is"; + break; + case e_DoubleFlat: + switch (note()) { + case 3: + noteStr = noteStr + "ses"; + break; + case 6: + noteStr = noteStr + "ses"; + break; + default: + noteStr = noteStr + "eses"; + break; } break; - case e_Flat: - switch (note()) { - case 3: noteStr = noteStr + "s"; break; - case 6: noteStr = noteStr + "s"; break; - default: noteStr = noteStr + "es"; break; + case e_Flat: + switch (note()) { + case 3: + noteStr = noteStr + "s"; + break; + case 6: + noteStr = noteStr + "s"; + break; + default: + noteStr = noteStr + "es"; + break; } break; } break; default: noteStr = Letters[note() - 1]; - if ((notation == e_english_Bb) && (note() == 7)) noteStr = "B"; - noteStr = noteStr + signsAcid[alter() + 2]; + if ((notation == e_english_Bb) && (note() == 7)) + noteStr = "B"; + noteStr = noteStr + signsAcid[alter() + 2]; break; - } - if (showOctave) - noteStr = noteStr + CharToString(octave() + (scientificOctaves ? 3 : 0)); - return noteStr; + } + if (showOctave) + noteStr = noteStr + CharToString(octave() + (scientificOctaves ? 3 : 0)); + return noteStr; } - -QString Tnote::toRichText(Tnote::EnameStyle notation, bool showOctave) const { - QString richText = toText(notation, false); - if (notation == Tnote::e_italiano_Si || - notation == Tnote::e_russian_Ci || - notation == Tnote::e_english_Bb || - notation == Tnote::e_norsk_Hb ) { +QString Tnote::toRichText(Tnote::EnameStyle notation, bool showOctave) const +{ + QString richText = toText(notation, false); + if (notation == Tnote::e_italiano_Si || notation == Tnote::e_russian_Ci || notation == Tnote::e_english_Bb || notation == Tnote::e_norsk_Hb) { if (alter()) - richText.replace(QString::fromStdString(signsAcid[alter() + 2]), QString("<sub>%1</sub>").arg(accidInSpan(alter()))); + richText.replace(QString::fromStdString(signsAcid[alter() + 2]), QString("<sub>%1</sub>").arg(accidInSpan(alter()))); } if (alter() == -2) richText.replace(QLatin1String("B"), QLatin1String("!")); // store capital B otherwise toLower() make it lower @@ -350,138 +406,140 @@ QString Tnote::toRichText(Tnote::EnameStyle notation, bool showOctave) const { if (alter() == -2) richText.replace(QLatin1String("!"), QLatin1String("B")); // bring back capital B if (showOctave) { - if (scientificOctaves) { - QString l1 = richText.mid(0, 1).toUpper(); - richText.replace(0, 1, l1); - richText += QString("<sub>%1</sub>").arg(static_cast<int>(octave() + 3)); - } else { - if (octave() < 0) { //first letter capitalize + if (scientificOctaves) { QString l1 = richText.mid(0, 1).toUpper(); richText.replace(0, 1, l1); - if (octave() < -1) - richText += QString("<sub>%1</sub>").arg(static_cast<int>(octave() * (-1) - 1)); - } - if (octave() > 0) - richText += QString("<sup>%1</sup>").arg(static_cast<int>(octave())); - } + richText += QString("<sub>%1</sub>").arg(static_cast<int>(octave() + 3)); + } else { + if (octave() < 0) { // first letter capitalize + QString l1 = richText.mid(0, 1).toUpper(); + richText.replace(0, 1, l1); + if (octave() < -1) + richText += QString("<sub>%1</sub>").arg(static_cast<int>(octave() * (-1) - 1)); + } + if (octave() > 0) + richText += QString("<sup>%1</sup>").arg(static_cast<int>(octave())); + } } return richText; } - -QString Tnote::styledName(bool showOctave) const { - QString name; - if (isValid()) { - EnameStyle notation = defaultStyle; - if (notation == Tnote::e_italiano_Si || notation == Tnote::e_russian_Ci || - notation == Tnote::e_english_Bb || notation == Tnote::e_norsk_Hb ) { - name = Tnote(note(), octave(), 0).toText(notation, false).toLower(); - if (alter()) - name += QString(QChar(379 + alter())); // 377 (0x179) is glyph number of double flat (-2) - } else // e_deutsch_His & e_nederl_Bis (full name) - name = toText(notation, false).toLower(); - if (showOctave) { - if (scientificOctaves) { - QString firstLetter = name.mid(0, 1).toUpper(); - name.replace(0, 1, firstLetter); - name += QString(QChar(435 + octave())); - } else { - if (octave() > 0) - name += QString(QChar(390 + octave())); // 391 is fist glyph of sup script digit - else if (octave() < 0) { - QString firstLetter = name.mid(0, 1).toUpper(); - name.replace(0, 1, firstLetter); - if (octave() < -1) // 397 is fist glyph of sub script digit, - name += QString(QChar(395 - octave())); // and -1 octave is just with capital letter but without digit, only -2 has 1 digit and etc - } - } +QString Tnote::styledName(bool showOctave) const +{ + QString name; + if (isValid()) { + EnameStyle notation = defaultStyle; + if (notation == Tnote::e_italiano_Si || notation == Tnote::e_russian_Ci || notation == Tnote::e_english_Bb || notation == Tnote::e_norsk_Hb) { + name = Tnote(note(), octave(), 0).toText(notation, false).toLower(); + if (alter()) + name += QString(QChar(379 + alter())); // 377 (0x179) is glyph number of double flat (-2) + } else // e_deutsch_His & e_nederl_Bis (full name) + name = toText(notation, false).toLower(); + if (showOctave) { + if (scientificOctaves) { + QString firstLetter = name.mid(0, 1).toUpper(); + name.replace(0, 1, firstLetter); + name += QString(QChar(435 + octave())); + } else { + if (octave() > 0) + name += QString(QChar(390 + octave())); // 391 is fist glyph of sup script digit + else if (octave() < 0) { + QString firstLetter = name.mid(0, 1).toUpper(); + name.replace(0, 1, firstLetter); + if (octave() < -1) // 397 is fist glyph of sub script digit, + name += QString(QChar(395 - octave())); // and -1 octave is just with capital letter but without digit, only -2 has 1 digit and etc + } + } + } } - } - return name; + return name; } - -void Tnote::toXml(QXmlStreamWriter& xml, const QString& tag, const QString& prefix, - const QString& attr, const QString& val) const { - if (!tag.isEmpty()) { - xml.writeStartElement(tag); - if (!attr.isEmpty()) - xml.writeAttribute(attr, val); - } - if (note() !=0) { // write <pitch> context only if note is valid - Tnote bareNote = Tnote(note(), octave(), 0); - xml.writeTextElement(prefix + QLatin1String("step"), bareNote.toText(Tnote::e_english_Bb, false)); - if (alter()) - xml.writeTextElement(prefix + QLatin1String("alter"), QString::number(static_cast<int>(alter()))); - xml.writeTextElement(prefix + QLatin1String("octave"), QString::number(static_cast<int>(octave() + 3))); - } - if (!tag.isEmpty()) - xml.writeEndElement(); // pitch +void Tnote::toXml(QXmlStreamWriter &xml, const QString &tag, const QString &prefix, const QString &attr, const QString &val) const +{ + if (!tag.isEmpty()) { + xml.writeStartElement(tag); + if (!attr.isEmpty()) + xml.writeAttribute(attr, val); + } + if (note() != 0) { // write <pitch> context only if note is valid + Tnote bareNote = Tnote(note(), octave(), 0); + xml.writeTextElement(prefix + QLatin1String("step"), bareNote.toText(Tnote::e_english_Bb, false)); + if (alter()) + xml.writeTextElement(prefix + QLatin1String("alter"), QString::number(static_cast<int>(alter()))); + xml.writeTextElement(prefix + QLatin1String("octave"), QString::number(static_cast<int>(octave() + 3))); + } + if (!tag.isEmpty()) + xml.writeEndElement(); // pitch } - -void Tnote::fromXml(QXmlStreamReader& xml, const QString& prefix) { -// if (xml.name() == "pitch") { - setNote(0); setOctave(0); setAlter(0); // reset this note +void Tnote::fromXml(QXmlStreamReader &xml, const QString &prefix) +{ + // if (xml.name() == "pitch") { + setNote(0); + setOctave(0); + setAlter(0); // reset this note while (xml.readNextStartElement()) { - if (xml.name() == (prefix + QLatin1String("step"))) { - QString step = xml.readElementText().toUpper(); - for (char i = 1; i < 8; i++) { - Tnote n(i, 0, 0); - if (n.toText(Tnote::e_english_Bb, false) == step) { - setNote(i); - break; - } - } - } else if (xml.name() == (prefix + QLatin1String("octave"))) - setOctave(static_cast<char>(xml.readElementText().toInt() - 3)); - else if (xml.name() == (prefix + QLatin1String("alter"))) - setAlter(static_cast<char>(xml.readElementText().toInt())); - else - xml.skipCurrentElement(); + if (xml.name() == (prefix + QLatin1String("step"))) { + QString step = xml.readElementText().toUpper(); + for (char i = 1; i < 8; i++) { + Tnote n(i, 0, 0); + if (n.toText(Tnote::e_english_Bb, false) == step) { + setNote(i); + break; + } + } + } else if (xml.name() == (prefix + QLatin1String("octave"))) + setOctave(static_cast<char>(xml.readElementText().toInt() - 3)); + else if (xml.name() == (prefix + QLatin1String("alter"))) + setAlter(static_cast<char>(xml.readElementText().toInt())); + else + xml.skipCurrentElement(); } -// } + // } } - -void Tnote::transpose(int interval) { - if (isValid() && interval != 0) - setChromatic(chromatic() + static_cast<short>(interval)); +void Tnote::transpose(int interval) +{ + if (isValid() && interval != 0) + setChromatic(chromatic() + static_cast<short>(interval)); } - -QString Tnote::shortOctaveName(int o) { - return o > -4 && o < 5 ? QGuiApplication::translate("TnoteName", shortOctaveNames[o + 3]) : QString(); +QString Tnote::shortOctaveName(int o) +{ + return o > -4 && o < 5 ? QGuiApplication::translate("TnoteName", shortOctaveNames[o + 3]) : QString(); } - -QString Tnote::fullOctaveName(int o) { - return o > -4 && o < 5 ? QGuiApplication::translate("TnoteName", fullOctaveNames[o + 3]) : QString(); +QString Tnote::fullOctaveName(int o) +{ + return o > -4 && o < 5 ? QGuiApplication::translate("TnoteName", fullOctaveNames[o + 3]) : QString(); } - -bool getNoteFromStream(QDataStream &in, Tnote &n) { +bool getNoteFromStream(QDataStream &in, Tnote &n) +{ bool ok = true; qint8 nn, oo, aa; in >> nn >> oo >> aa; if (nn < 1 || nn > 8 || aa < -2 || aa > 2) { - nn = 1; aa = 0; oo = 0; + nn = 1; + aa = 0; + oo = 0; ok = false; } n = Tnote(nn, oo, aa); return ok; } - -QDataStream &operator << (QDataStream &out, const Tnote &n) { - out << static_cast<qint8>(n.note()) << static_cast<qint8>(n.octave()) << static_cast<qint8>(n.alter()); - return out; +QDataStream &operator<<(QDataStream &out, const Tnote &n) +{ + out << static_cast<qint8>(n.note()) << static_cast<qint8>(n.octave()) << static_cast<qint8>(n.alter()); + return out; } - -QDataStream &operator >> (QDataStream &in, Tnote &n) { - qint8 nn, oo, aa; - in >> nn >> oo >> aa; - n = Tnote(nn, oo, aa); - return in; +QDataStream &operator>>(QDataStream &in, Tnote &n) +{ + qint8 nn, oo, aa; + in >> nn >> oo >> aa; + n = Tnote(nn, oo, aa); + return in; } diff --git a/src/libs/core/music/tnote.h b/src/libs/core/music/tnote.h index 3baa4b2f434916df708a8096af8e731ed8bf7f19..ccd6a2ed58bccb43da54b744fbb9c3871d79e57f 100644 --- a/src/libs/core/music/tnote.h +++ b/src/libs/core/music/tnote.h @@ -19,15 +19,13 @@ #ifndef TNOTE_H #define TNOTE_H - -#include <nootkacoreglobal.h> #include "trhythm.h" +#include <nootkacoreglobal.h> -#include <string> -#include <vector> #include <QtCore/qdatastream.h> #include <QtCore/qxmlstream.h> - +#include <string> +#include <vector> class Tnote; @@ -36,7 +34,6 @@ typedef std::vector<Tnote> TnotesList; #define ALTER_BIT_MASK (7) #define ON_UPPER_BIT_MASK (128) - /** * This class provides the descriptions of musical. * It helps to convert the letter notation (C D E ...) to solfege (Do Re Mi). @@ -48,329 +45,331 @@ typedef std::vector<Tnote> TnotesList; */ class NOOTKACORE_EXPORT Tnote { - public: + /** + * Enumeration type describes the styles of notation + */ + enum EnameStyle { + e_norsk_Hb = 0, /**< for letters with signs f.e. C# Cx or Cb !! THIS IS DEFAULT !! */ + e_deutsch_His = 1, /**< for letters with names f.e. Cis Cisis or Ces H and B (H with flat) */ + e_italiano_Si = 2, /**< for classical Do Re Mi Fa Sol La Si */ + e_english_Bb = 3, /**<like @p e_norsk_Hb but with B and Bb (B flat) */ + e_nederl_Bis = 4, /**< like @p e_deutsch_His but with B ens Bes */ + e_russian_Ci = 5 /**< classical but in Russian: До Ре Ми Фа Соль Ð›Ñ Ð¡Ð¸ */ + }; + + /** + * @p Ealter enumeration type describes all signs which can be before note in score. + * It can be: @li e_None = 3 @li e_Sharp = 1 @li e_DoubleSharp=2 + * @li e_Flat= -1 @li e_DoubleFlat= -2 @li e_Natural=0 + */ + enum Ealter : char { e_Natural = 0, e_Sharp = 1, e_DoubleSharp = 2, e_Flat = -1, e_DoubleFlat = -2, e_None = 3 }; + + /** + * note (step) is a number in "diatonic notation" (see constructor). + */ + inline char note() const { return p_note; } + inline void setNote(char n) { p_note = n; } + + /** + * Octave number is @p 0 for "small octave", @p -1 for "Great" @p 1 for "one-line". + */ + inline char octave() const { return p_octave; } + inline void setOctave(char o) { p_octave = o; } + + /** + * @p accidental means raising or dropping a note, so it ca be: + * @li 2 for double sharp (x) + * @li 1 for sharp (#) + * @li 0 for natural + * @li -1 for flat (b) + * @li -2 for double flat (bb) + */ + inline char alter() const { return (p_alterValue & ALTER_BIT_MASK) - 3; } + + /** + * @p a HAS TO BE in range [-3, 3] (even if value -3 makes no sense) + * otherwise it may overwrite @p onUpperStaff() state + */ + inline void setAlter(char a) + { + p_alterValue &= ~ALTER_BIT_MASK; + p_alterValue |= a + 3; + } + + inline bool onUpperStaff() const { return !(p_alterValue & ON_UPPER_BIT_MASK); } + inline void setOnUpperStaff(bool onUp) + { + if (onUp) + p_alterValue &= ~128; + else + p_alterValue |= 128; + } - /** - * Enumeration type describes the styles of notation - */ - enum EnameStyle { - e_norsk_Hb = 0, /**< for letters with signs f.e. C# Cx or Cb !! THIS IS DEFAULT !! */ - e_deutsch_His = 1, /**< for letters with names f.e. Cis Cisis or Ces H and B (H with flat) */ - e_italiano_Si = 2, /**< for classical Do Re Mi Fa Sol La Si */ - e_english_Bb = 3, /**<like @p e_norsk_Hb but with B and Bb (B flat) */ - e_nederl_Bis = 4, /**< like @p e_deutsch_His but with B ens Bes */ - e_russian_Ci = 5 /**< classical but in Russian: До Ре Ми Фа Соль Ð›Ñ Ð¡Ð¸ */ - }; - - /** - * @p Ealter enumeration type describes all signs which can be before note in score. - * It can be: @li e_None = 3 @li e_Sharp = 1 @li e_DoubleSharp=2 - * @li e_Flat= -1 @li e_DoubleFlat= -2 @li e_Natural=0 - */ - enum Ealter : char { - e_Natural = 0, e_Sharp = 1, e_DoubleSharp = 2, e_Flat = -1, e_DoubleFlat = -2, e_None = 3 - }; - - /** - * note (step) is a number in "diatonic notation" (see constructor). - */ - inline char note() const { return p_note; } - inline void setNote(char n) { p_note = n; } - - /** - * Octave number is @p 0 for "small octave", @p -1 for "Great" @p 1 for "one-line". - */ - inline char octave() const { return p_octave; } - inline void setOctave(char o) { p_octave = o; } - - /** - * @p accidental means raising or dropping a note, so it ca be: - * @li 2 for double sharp (x) - * @li 1 for sharp (#) - * @li 0 for natural - * @li -1 for flat (b) - * @li -2 for double flat (bb) - */ - inline char alter() const { return (p_alterValue & ALTER_BIT_MASK) - 3; } - - /** - * @p a HAS TO BE in range [-3, 3] (even if value -3 makes no sense) - * otherwise it may overwrite @p onUpperStaff() state - */ - inline void setAlter(char a) { p_alterValue &= ~ALTER_BIT_MASK; p_alterValue |= a + 3; } - - inline bool onUpperStaff() const { return !(p_alterValue & ON_UPPER_BIT_MASK); } - inline void setOnUpperStaff(bool onUp) { - if (onUp) - p_alterValue &= ~128; - else - p_alterValue |= 128; - } - - /** - * returns std string representing letter of accidental (alter) symbol - */ - static std::string alterSymbol(int id); - - /** - * Construct object of Tnote from number of note, number of octave - * and optionally accidental and rhythm (none by default). - * The note number is: - * @li "1" for C - * @li "2" for D1 - * @li ....... - * @li 7 for B (H in Deutsh) - * If accidental is not defined, the note is natural. - */ - Tnote(char diatonNote, char oct, char accid = 0, const Trhythm& r = Trhythm(Trhythm::NoRhythm)) : - rtm(r), - p_note(diatonNote), - p_octave(oct) + /** + * returns std string representing letter of accidental (alter) symbol + */ + static std::string alterSymbol(int id); + + /** + * Construct object of Tnote from number of note, number of octave + * and optionally accidental and rhythm (none by default). + * The note number is: + * @li "1" for C + * @li "2" for D1 + * @li ....... + * @li 7 for B (H in Deutsh) + * If accidental is not defined, the note is natural. + */ + Tnote(char diatonNote, char oct, char accid = 0, const Trhythm &r = Trhythm(Trhythm::NoRhythm)) + : rtm(r) + , p_note(diatonNote) + , p_octave(oct) + { + setAlter(accid); + } + + /** + * The simple constructor, creates the note instance with 0 note and no rhythm. + * It makes no sense in musical notation. It's needed for vectors. + */ + Tnote() + : rtm(Trhythm(Trhythm::NoRhythm)) + { + } + + /** + * Construct object of Tnote from number, that represents: + * @li "1" for C1 in first octave + * @li "2" for Cis + * @li ....... + * @li 13 for C2 in next octave + * @li -12 for C in little octave etc.... + * The sharp accidental is default. If other are needed, use @p showWithFlat(). + * Sets rhythm to @p r + */ + Tnote(short chromaticNrOfNote, const Trhythm &r = Trhythm(Trhythm::NoRhythm)) + : rtm(r) + { + setChromatic(chromaticNrOfNote); + } + + /** + * Constructs @class Tnote from other Tnote but with different rhythm @p r + */ + Tnote(const Tnote &other, const Trhythm &r) + : rtm(r) + , p_note(other.note()) + , p_octave(other.octave()) + , p_alterValue(other.p_alterValue) { - setAlter(accid); } - /** - * The simple constructor, creates the note instance with 0 note and no rhythm. - * It makes no sense in musical notation. It's needed for vectors. - */ - Tnote() : rtm(Trhythm(Trhythm::NoRhythm)) {} - - /** - * Construct object of Tnote from number, that represents: - * @li "1" for C1 in first octave - * @li "2" for Cis - * @li ....... - * @li 13 for C2 in next octave - * @li -12 for C in little octave etc.... - * The sharp accidental is default. If other are needed, use @p showWithFlat(). - * Sets rhythm to @p r - */ - Tnote(short chromaticNrOfNote, const Trhythm& r = Trhythm(Trhythm::NoRhythm)) : - rtm(r) - { - setChromatic(chromaticNrOfNote); - } - - /** - * Constructs @class Tnote from other Tnote but with different rhythm @p r - */ - Tnote(const Tnote& other, const Trhythm& r) : - rtm(r), - p_note(other.note()), - p_octave(other.octave()), - p_alterValue(other.p_alterValue) - {} - - - Trhythm rtm; /**< Easy access to rhythm object */ - Trhythm::Erhythm rhythm() const { return rtm.rhythm(); } - - /** - * Sets rhythm parameters, Resets all previous values! - */ - void setRhythm(Trhythm::Erhythm r, bool rest = false, bool dot = false, bool triplet = false) { - rtm.setRhythm(r, rest, dot, triplet); - } - - void setRhythm(const Trhythm& r) { rtm.setRhythm(r); } - - /** - * Converts given value into rhythm - */ - void setRhythm(quint16 durationValue) { rtm.setRhythm(durationValue); } - - /** - * Changes rhythmic value only, state of dot, triplet, beams remains unchanged - */ - void setRhythmValue(Trhythm::Erhythm nVal) { rtm.setRhythmValue(nVal); } - - /** - * It converts std::string into rhythm value. Doesn't change state of triplet, dot or beam. - */ - void setRhythmValue(const std::string& nVal) { rtm.setRhythmValue(nVal); } - - /** - * Rhythm value cast to int: i.e. quarter is 4, half is 2 and so on - */ - int weight() const { return rtm.weight(); } - - /** - * Whole note is 96, half is 48, quarter is 24 and with dot is 36. Single eight triplet is 8. - * Base value is defined in @p RVALUE macro - */ - int duration() const { return rtm.duration(); } - bool isRest() const { return rtm.isRest(); } - void setRest(bool rest) { rtm.setRest(rest); } - bool hasDot() const { return rtm.hasDot(); } - void setDot(bool dot) { rtm.setDot(dot); } - bool isTriplet() const { return rtm.isTriplet(); } - void setTriplet(bool tri) { rtm.setTriplet(tri); } - - /* - * Returns @p TRUE when note is valid. There are used 'undefined' notes with 0 - they are invalid. - */ - bool isValid() const { return (note() > 0 && note() < 8); } - - /** - * Static value determines default name style for a note - */ - static EnameStyle defaultStyle; - - /** - * Determines whether to use International Pitch Notation, - * when octaves are numbered from 0 (-3 here, sub contra octave). - * This is global static switch, a change influences any further note name. - * By default it is @p FALSE - */ - static bool scientificOctaves; - - bool operator==(const Tnote& N2) const { - return (note() == N2.note() && octave() == N2.octave() && p_alterValue == N2.p_alterValue && rtm == N2.rtm); - } - - bool operator!=(const Tnote& N2) const { - return ( note() != N2.note() || octave() != N2.octave() || p_alterValue != N2.p_alterValue || rtm != N2.rtm); - } - - /** - * Splits current note on two given rhythmic values - */ - Tnote split(const Trhythm& r1, const Trhythm& r2) { - setRhythm(r1); - return Tnote(note(), octave(), p_alterValue, r2); - } - - /** - * @return List of Tnote objects, which are the same (in sound sense), - * like note represents by class. (C# = Db, or Cx = D = Ebb) - * @param enableDbAccids if @p TRUE - checks substitutes with double accidentals - */ - TnotesList getTheSameNotes (bool enableDbAccids) const; - - /** Return this note converted into double-sharp or the same note if not possible. */ - Tnote showWithDoubleSharp() const; - - /** Return this note converted into sharp or the same note if not possible. */ - Tnote showWithSharp() const; - - /** It returns Tnote object with no accidentals, if it's possible, or with sharps if not. */ - Tnote showAsNatural() const; - - /** Return this note converted into flat or the same note if not possible. */ - Tnote showWithFlat() const; - - /** Return this note converted into double-flat or the same note if not possible. */ - Tnote showWithDoubleFlat() const; - - /** - * This method compares actual note, with otherNote @p otherNote. - * @p ignoreOctave, if @p TRUE - the octave values are ignored, - * and method compares only number of note and accidental. - * - * In contrary to == operator, it also ignores @p onUpperStaff() comparison - */ - bool compareNotes(const Tnote& otherNote, bool ignoreOctave = false) const { - return note() == otherNote.note() && alter() == otherNote.alter() && (ignoreOctave || octave() == otherNote.octave()); - } - - std::string getName(EnameStyle notation = e_norsk_Hb, bool showOctave = 1) const; - - /** Returns note name converted to QString */ - QString toText (EnameStyle notation, bool showOctave = true) const { - return QString::fromUtf8(getName(notation, showOctave).data()); - } - - /** Returns note name converted to @p QString */ - QString toText (bool showOctave = true) const { return toText(defaultStyle, showOctave); } - - /** DEPRECATED - * Returns note name formatted to HTML - * NOTE: Since QML port, it is note recommended, prefer @p styledName() instead - */ - QString toRichText(EnameStyle notation, bool showOctave = true) const; - - /** DEPRECATED - * Returns note name formatted to HTML in default name style sets by @p defaultStyle. - * NOTE: Since QML port, it is note recommended, prefer @p styledName() instead - */ - QString toRichText(bool showOctave = true) const { return toRichText(defaultStyle, showOctave); } - - /** - * Returns name of the current note with glyphs of Scorek font. - * It doesn't use any tags for better QML performance. - */ - QString styledName(bool showOctave = true)const; - - short chromatic() const; /**< Returns chromatic number of note f.e. C1 is 60 */ - void setChromatic(short noteNr); /**< Determines note, octave and accidental from chromatic value. */ - - /** - * Adds given @p tag or 'pitch' key to XML stream compatible with MusicXML format with current note - * Following elements can be prefixed with @p prefix (it is used i.e. to tuning in MusicXML) - * If @p attr and its @p val is set the attribute is added - * <pitch attr="val"> - * <prefix-step>G</prefix-step> - * <prefix-octave>2</prefix-octave> - * <prefix-alter>-1</prefix-alter> - * </pitch> - */ - void toXml(QXmlStreamWriter& xml, const QString& tag = QStringLiteral("pitch"), const QString& prefix = QString(), - const QString& attr = QString(), const QString& val = QString()) const; - - /** - * Reads this note from XML stream. - * It looks every note element prefixed with @p prefix. - */ - void fromXml(QXmlStreamReader& xml, const QString& prefix = QString()); - - - /** - * Returns this note as midi number. - */ - unsigned int toMidi() const { return chromatic() + 47; } - - /** - * Sets this note from midi note number. - */ - void fromMidi(unsigned int midiNote) { setChromatic(midiNote - 47); } - - /** - * Shifts this note (if valid) about given number of semitones (interval) - * Negative values shift down (drops), positive ones shift up (rise) - */ - void transpose(int interval); - - /** - * Simple rises this note one octave up - */ - inline void riseOctaveUp() { if (isValid()) p_octave++; } - - static QString shortOctaveName(int o); - static QString fullOctaveName(int o); + Trhythm rtm; /**< Easy access to rhythm object */ + Trhythm::Erhythm rhythm() const { return rtm.rhythm(); } + + /** + * Sets rhythm parameters, Resets all previous values! + */ + void setRhythm(Trhythm::Erhythm r, bool rest = false, bool dot = false, bool triplet = false) { rtm.setRhythm(r, rest, dot, triplet); } + + void setRhythm(const Trhythm &r) { rtm.setRhythm(r); } + + /** + * Converts given value into rhythm + */ + void setRhythm(quint16 durationValue) { rtm.setRhythm(durationValue); } + + /** + * Changes rhythmic value only, state of dot, triplet, beams remains unchanged + */ + void setRhythmValue(Trhythm::Erhythm nVal) { rtm.setRhythmValue(nVal); } + + /** + * It converts std::string into rhythm value. Doesn't change state of triplet, dot or beam. + */ + void setRhythmValue(const std::string &nVal) { rtm.setRhythmValue(nVal); } + + /** + * Rhythm value cast to int: i.e. quarter is 4, half is 2 and so on + */ + int weight() const { return rtm.weight(); } + + /** + * Whole note is 96, half is 48, quarter is 24 and with dot is 36. Single eight triplet is 8. + * Base value is defined in @p RVALUE macro + */ + int duration() const { return rtm.duration(); } + bool isRest() const { return rtm.isRest(); } + void setRest(bool rest) { rtm.setRest(rest); } + bool hasDot() const { return rtm.hasDot(); } + void setDot(bool dot) { rtm.setDot(dot); } + bool isTriplet() const { return rtm.isTriplet(); } + void setTriplet(bool tri) { rtm.setTriplet(tri); } + + /* + * Returns @p TRUE when note is valid. There are used 'undefined' notes with 0 - they are invalid. + */ + bool isValid() const { return (note() > 0 && note() < 8); } + + /** + * Static value determines default name style for a note + */ + static EnameStyle defaultStyle; + + /** + * Determines whether to use International Pitch Notation, + * when octaves are numbered from 0 (-3 here, sub contra octave). + * This is global static switch, a change influences any further note name. + * By default it is @p FALSE + */ + static bool scientificOctaves; + + bool operator==(const Tnote &N2) const { return (note() == N2.note() && octave() == N2.octave() && p_alterValue == N2.p_alterValue && rtm == N2.rtm); } + + bool operator!=(const Tnote &N2) const { return (note() != N2.note() || octave() != N2.octave() || p_alterValue != N2.p_alterValue || rtm != N2.rtm); } + + /** + * Splits current note on two given rhythmic values + */ + Tnote split(const Trhythm &r1, const Trhythm &r2) + { + setRhythm(r1); + return Tnote(note(), octave(), p_alterValue, r2); + } + + /** + * @return List of Tnote objects, which are the same (in sound sense), + * like note represents by class. (C# = Db, or Cx = D = Ebb) + * @param enableDbAccids if @p TRUE - checks substitutes with double accidentals + */ + TnotesList getTheSameNotes(bool enableDbAccids) const; + + /** Return this note converted into double-sharp or the same note if not possible. */ + Tnote showWithDoubleSharp() const; + + /** Return this note converted into sharp or the same note if not possible. */ + Tnote showWithSharp() const; + + /** It returns Tnote object with no accidentals, if it's possible, or with sharps if not. */ + Tnote showAsNatural() const; + + /** Return this note converted into flat or the same note if not possible. */ + Tnote showWithFlat() const; + + /** Return this note converted into double-flat or the same note if not possible. */ + Tnote showWithDoubleFlat() const; + + /** + * This method compares actual note, with otherNote @p otherNote. + * @p ignoreOctave, if @p TRUE - the octave values are ignored, + * and method compares only number of note and accidental. + * + * In contrary to == operator, it also ignores @p onUpperStaff() comparison + */ + bool compareNotes(const Tnote &otherNote, bool ignoreOctave = false) const + { + return note() == otherNote.note() && alter() == otherNote.alter() && (ignoreOctave || octave() == otherNote.octave()); + } + + std::string getName(EnameStyle notation = e_norsk_Hb, bool showOctave = 1) const; + + /** Returns note name converted to QString */ + QString toText(EnameStyle notation, bool showOctave = true) const { return QString::fromUtf8(getName(notation, showOctave).data()); } + + /** Returns note name converted to @p QString */ + QString toText(bool showOctave = true) const { return toText(defaultStyle, showOctave); } + + /** DEPRECATED + * Returns note name formatted to HTML + * NOTE: Since QML port, it is note recommended, prefer @p styledName() instead + */ + QString toRichText(EnameStyle notation, bool showOctave = true) const; + + /** DEPRECATED + * Returns note name formatted to HTML in default name style sets by @p defaultStyle. + * NOTE: Since QML port, it is note recommended, prefer @p styledName() instead + */ + QString toRichText(bool showOctave = true) const { return toRichText(defaultStyle, showOctave); } + + /** + * Returns name of the current note with glyphs of Scorek font. + * It doesn't use any tags for better QML performance. + */ + QString styledName(bool showOctave = true) const; + + short chromatic() const; /**< Returns chromatic number of note f.e. C1 is 60 */ + void setChromatic(short noteNr); /**< Determines note, octave and accidental from chromatic value. */ + + /** + * Adds given @p tag or 'pitch' key to XML stream compatible with MusicXML format with current note + * Following elements can be prefixed with @p prefix (it is used i.e. to tuning in MusicXML) + * If @p attr and its @p val is set the attribute is added + * <pitch attr="val"> + * <prefix-step>G</prefix-step> + * <prefix-octave>2</prefix-octave> + * <prefix-alter>-1</prefix-alter> + * </pitch> + */ + void toXml(QXmlStreamWriter &xml, + const QString &tag = QStringLiteral("pitch"), + const QString &prefix = QString(), + const QString &attr = QString(), + const QString &val = QString()) const; + + /** + * Reads this note from XML stream. + * It looks every note element prefixed with @p prefix. + */ + void fromXml(QXmlStreamReader &xml, const QString &prefix = QString()); + + /** + * Returns this note as midi number. + */ + unsigned int toMidi() const { return chromatic() + 47; } + + /** + * Sets this note from midi note number. + */ + void fromMidi(unsigned int midiNote) { setChromatic(midiNote - 47); } + + /** + * Shifts this note (if valid) about given number of semitones (interval) + * Negative values shift down (drops), positive ones shift up (rise) + */ + void transpose(int interval); + + /** + * Simple rises this note one octave up + */ + inline void riseOctaveUp() + { + if (isValid()) + p_octave++; + } + + static QString shortOctaveName(int o); + static QString fullOctaveName(int o); protected: - char p_note = 0; - char p_octave = 0; - quint8 p_alterValue = 3; /**< Used also to store upper/lower staff placement. */ + char p_note = 0; + char p_octave = 0; + quint8 p_alterValue = 3; /**< Used also to store upper/lower staff placement. */ private: - static std::string m_solmization[7]; - static std::string m_solmizationRu[7]; - + static std::string m_solmization[7]; + static std::string m_solmizationRu[7]; }; Q_DECLARE_METATYPE(Tnote) - /** - * This function is substitute of >> operator for @p Tnote. - * It checks is Tnote valid, and return Boolean about it. - */ +/** + * This function is substitute of >> operator for @p Tnote. + * It checks is Tnote valid, and return Boolean about it. + */ NOOTKACORE_EXPORT bool getNoteFromStream(QDataStream &in, Tnote &n); -NOOTKACORE_EXPORT QDataStream &operator<< (QDataStream &out, const Tnote &n); -NOOTKACORE_EXPORT QDataStream &operator>> (QDataStream &in, Tnote &n); - +NOOTKACORE_EXPORT QDataStream &operator<<(QDataStream &out, const Tnote &n); +NOOTKACORE_EXPORT QDataStream &operator>>(QDataStream &in, Tnote &n); #endif diff --git a/src/libs/core/music/tnotestruct.cpp b/src/libs/core/music/tnotestruct.cpp index f738d1c695e0a06eaf7bc3d800ec791d9fc22aee..f511926884aad173d664c731019860943a685251 100644 --- a/src/libs/core/music/tnotestruct.cpp +++ b/src/libs/core/music/tnotestruct.cpp @@ -18,64 +18,64 @@ #include "tnotestruct.h" - -void TnoteStruct::init(int _index, int chunkNr, qreal floatPitch) { - startChunk = chunkNr; - endChunk = chunkNr; - pitchF = floatPitch; - index = _index; - maxVol = 0.0f; - minVol = 1.0f; - maxPCMvol = 0.0f; - m_totalAver = 0.0; - m_shortAver = 0.0; - m_pList.clear(); - m_pList << floatPitch; - bestPitch = 0.0; - idChangedAt.clear(); - idChangedAt << 0; +void TnoteStruct::init(int _index, int chunkNr, qreal floatPitch) +{ + startChunk = chunkNr; + endChunk = chunkNr; + pitchF = floatPitch; + index = _index; + maxVol = 0.0f; + minVol = 1.0f; + maxPCMvol = 0.0f; + m_totalAver = 0.0; + m_shortAver = 0.0; + m_pList.clear(); + m_pList << floatPitch; + bestPitch = 0.0; + idChangedAt.clear(); + idChangedAt << 0; } - -void TnoteStruct::sumarize(qreal chunkTime) { - freq = pitchToFreq(bestPitch); - duration = numChunks() * chunkTime; - if (!m_pList.isEmpty()) - pitchF = m_pList.size() > 3 ? m_pList[3] : m_pList.last(); +void TnoteStruct::sumarize(qreal chunkTime) +{ + freq = pitchToFreq(bestPitch); + duration = numChunks() * chunkTime; + if (!m_pList.isEmpty()) + pitchF = m_pList.size() > 3 ? m_pList[3] : m_pList.last(); } - -void TnoteStruct::update(int chunkNr, qreal floatPitch, float vol) { - if (floatPitch > 1.0) { - m_pList << floatPitch; - pitchF = m_pList.size() > 3 ? m_pList[3] : m_pList.last(); - if (m_pList.size() == 2 || m_pList.size() == 3) - basePitch = qRound(pitchF); // it is better to take base pitch not from first chunks - } - endChunk = chunkNr; - maxVol = qMax<float>(maxVol, vol); - if (numChunks() > 3) // skip first 3 chunks - Tartini may detected a note with low volume - minVol = qMin<float>(minVol, vol); - if (qAbs(floatPitch - static_cast<qreal>(basePitch)) < qAbs(bestPitch - static_cast<qreal>(basePitch))) - bestPitch = floatPitch; +void TnoteStruct::update(int chunkNr, qreal floatPitch, float vol) +{ + if (floatPitch > 1.0) { + m_pList << floatPitch; + pitchF = m_pList.size() > 3 ? m_pList[3] : m_pList.last(); + if (m_pList.size() == 2 || m_pList.size() == 3) + basePitch = qRound(pitchF); // it is better to take base pitch not from first chunks + } + endChunk = chunkNr; + maxVol = qMax<float>(maxVol, vol); + if (numChunks() > 3) // skip first 3 chunks - Tartini may detected a note with low volume + minVol = qMin<float>(minVol, vol); + if (qAbs(floatPitch - static_cast<qreal>(basePitch)) < qAbs(bestPitch - static_cast<qreal>(basePitch))) + bestPitch = floatPitch; } - -qreal TnoteStruct::getAverage(unsigned int start, unsigned int stop) { - qreal sum = 0.0; - int cnt = 0; - for (int i = qMin<int>(start - 1, m_pList.size() - 1); i < qMin<int>(m_pList.size(), stop); ++i) { - sum += m_pList[i]; - cnt++; - } - return sum / cnt; +qreal TnoteStruct::getAverage(unsigned int start, unsigned int stop) +{ + qreal sum = 0.0; + int cnt = 0; + for (int i = qMin<int>(start - 1, m_pList.size() - 1); i < qMin<int>(m_pList.size(), stop); ++i) { + sum += m_pList[i]; + cnt++; + } + return sum / cnt; } - -QString TnoteStruct::debug() { - QString pp = QStringLiteral(" [ "); - for (qreal p : m_pList) pp += QString("%1, ").arg(p); - pp += QStringLiteral(" ]"); - return QString::number(pitchF) + QLatin1String(", chunks: ") + QString::number(m_pList.size()) + pp - + QString(" %1").arg(bestPitch); +QString TnoteStruct::debug() +{ + QString pp = QStringLiteral(" [ "); + for (qreal p : m_pList) + pp += QString("%1, ").arg(p); + pp += QStringLiteral(" ]"); + return QString::number(pitchF) + QLatin1String(", chunks: ") + QString::number(m_pList.size()) + pp + QString(" %1").arg(bestPitch); } diff --git a/src/libs/core/music/tnotestruct.h b/src/libs/core/music/tnotestruct.h index 3480098c55b55b8afbd794c6f8d1e3ea389cb465..1df8bcfd791c7e6c4c4213906e6a239ca872d142 100644 --- a/src/libs/core/music/tnotestruct.h +++ b/src/libs/core/music/tnotestruct.h @@ -19,11 +19,9 @@ #ifndef TNOTESTRUCT_H #define TNOTESTRUCT_H - -#include <nootkacoreglobal.h> -#include <cmath> #include "tnote.h" - +#include <cmath> +#include <nootkacoreglobal.h> /** * Structure that stores pitch and its parameters as such as frequency and duration in [s]. @@ -36,122 +34,136 @@ */ class NOOTKACORE_EXPORT TnoteStruct { - public: - TnoteStruct(const Tnote& p, qreal pF, qreal f = 0.0, qreal dur = 0.0) : pitch(p), pitchF(pF), freq(f), duration(dur) {} - - /** - * Default constructor - */ - TnoteStruct() {} - - - /** - * Initializes and cleans @p TnoteStruct values - */ - void init(int _index, int chunkNr, qreal floatPitch); - - /** - * Updates values. Calculates minimal and maximal volume already occurred - */ - void update(int chunkNr, qreal floatPitch, float vol); - - /** - * Summarizes a note. Sets duration in seconds [s]. - * Given parameter @p chunkTime is duration of single chunk in seconds. - * It sets frequency of a note from @p bestPitch value. - */ - void sumarize(qreal chunkTime); - - /** - * Checks is float value of a note pitch different than its root pitch in range of given threshold. - * Returns @p TRUE if @p pitchF value is into threshold range or @p FALSE when not - * This way intonation accuracy is checked. - */ - bool inTune(float threshold) { - if (qAbs(pitchF - (float)qRound(pitchF)) >= threshold) - return false; - else - return true; - } - - /** - * Sets values of note structure, or sets them to NULL if no values defined. - */ - void set(qreal midiPitch = 0.0, qreal f = 0.0, qreal dur = 0.0) { - midiPitch ? pitch = Tnote(qRound(midiPitch) - 47) : pitch = Tnote(); pitchF = midiPitch; freq = f; duration = dur; - } - - int index; /**< Note index in entire channel */ - Tnote pitch; /**< Note pitch like C, D, E or C3, D# */ - qreal pitchF = 0.0; /**< Chromatic note number in MIDI scale, C1 = 60 */ - qreal bestPitch; /**< Closest value to rounded (perfect) pitch among all occurred pitches. */ - int basePitch; /**< Midi value (rounded to integer) of a pitch */ - qreal freq = 0.0; /**< Frequency of a note */ - qreal duration = 0.0; /**< Duration of a note */ - int startChunk; /**< Chunk in which note was noticed */ - int endChunk; /**< Last chunk with this note */ - float maxVol; /**< Loudest volume occurred */ - float minVol; /**< Quietest volume occurred */ - float maxPCMvol; /**< Maximal Raw PCM volume during whole note */ - QVector<int> idChangedAt; /**< List of positions in @p pitches() when note index changed */ - - void indexChanged() { idChangedAt << m_pList.size() - 1; } - - /** - * Note duration in chunks - */ - int numChunks() { return endChunk - startChunk + 1; } - - /** - * Average pitch of all chunk pitches - */ - qreal totalAverage() { if (m_totalAver == 0.0) getTotalAverage(); return m_totalAver; } - - /** - * Average pitch of 3 to 6 chunks - */ - qreal shortAverage() { if (m_shortAver == 0.0) getShortAverage(); return m_shortAver; } - - /** - * Average frequency of total average pitch - */ - qreal averageFreq() { return pitchToFreq(totalAverage()); } - - /** - * Average frequency of short average pitch - */ - qreal averageShortFreq() { return pitchToFreq(shortAverage()); } - - /** - * Pointer to list @p QVector of pitches - */ - QVector<qreal>* pitches() { return &m_pList; } - - /** - * Static method that converts given pitch to a frequency (in Hz) - */ - static qreal pitchToFreq(qreal midiPitch) { return std::pow(10.0, (midiPitch + 36.3763165622959152488) / 39.8631371386483481); } - - /** - * Returns average pitch in given chunks range. First chunk is 1. - */ - qreal getAverage(unsigned int start, unsigned int stop); - - QString debug(); + TnoteStruct(const Tnote &p, qreal pF, qreal f = 0.0, qreal dur = 0.0) + : pitch(p) + , pitchF(pF) + , freq(f) + , duration(dur) + { + } + + /** + * Default constructor + */ + TnoteStruct() { } + + /** + * Initializes and cleans @p TnoteStruct values + */ + void init(int _index, int chunkNr, qreal floatPitch); + + /** + * Updates values. Calculates minimal and maximal volume already occurred + */ + void update(int chunkNr, qreal floatPitch, float vol); + + /** + * Summarizes a note. Sets duration in seconds [s]. + * Given parameter @p chunkTime is duration of single chunk in seconds. + * It sets frequency of a note from @p bestPitch value. + */ + void sumarize(qreal chunkTime); + + /** + * Checks is float value of a note pitch different than its root pitch in range of given threshold. + * Returns @p TRUE if @p pitchF value is into threshold range or @p FALSE when not + * This way intonation accuracy is checked. + */ + bool inTune(float threshold) + { + if (qAbs(pitchF - (float)qRound(pitchF)) >= threshold) + return false; + else + return true; + } + + /** + * Sets values of note structure, or sets them to NULL if no values defined. + */ + void set(qreal midiPitch = 0.0, qreal f = 0.0, qreal dur = 0.0) + { + midiPitch ? pitch = Tnote(qRound(midiPitch) - 47) : pitch = Tnote(); + pitchF = midiPitch; + freq = f; + duration = dur; + } + + int index; /**< Note index in entire channel */ + Tnote pitch; /**< Note pitch like C, D, E or C3, D# */ + qreal pitchF = 0.0; /**< Chromatic note number in MIDI scale, C1 = 60 */ + qreal bestPitch; /**< Closest value to rounded (perfect) pitch among all occurred pitches. */ + int basePitch; /**< Midi value (rounded to integer) of a pitch */ + qreal freq = 0.0; /**< Frequency of a note */ + qreal duration = 0.0; /**< Duration of a note */ + int startChunk; /**< Chunk in which note was noticed */ + int endChunk; /**< Last chunk with this note */ + float maxVol; /**< Loudest volume occurred */ + float minVol; /**< Quietest volume occurred */ + float maxPCMvol; /**< Maximal Raw PCM volume during whole note */ + QVector<int> idChangedAt; /**< List of positions in @p pitches() when note index changed */ + + void indexChanged() { idChangedAt << m_pList.size() - 1; } + + /** + * Note duration in chunks + */ + int numChunks() { return endChunk - startChunk + 1; } + + /** + * Average pitch of all chunk pitches + */ + qreal totalAverage() + { + if (m_totalAver == 0.0) + getTotalAverage(); + return m_totalAver; + } + + /** + * Average pitch of 3 to 6 chunks + */ + qreal shortAverage() + { + if (m_shortAver == 0.0) + getShortAverage(); + return m_shortAver; + } + + /** + * Average frequency of total average pitch + */ + qreal averageFreq() { return pitchToFreq(totalAverage()); } + + /** + * Average frequency of short average pitch + */ + qreal averageShortFreq() { return pitchToFreq(shortAverage()); } + + /** + * Pointer to list @p QVector of pitches + */ + QVector<qreal> *pitches() { return &m_pList; } + + /** + * Static method that converts given pitch to a frequency (in Hz) + */ + static qreal pitchToFreq(qreal midiPitch) { return std::pow(10.0, (midiPitch + 36.3763165622959152488) / 39.8631371386483481); } + + /** + * Returns average pitch in given chunks range. First chunk is 1. + */ + qreal getAverage(unsigned int start, unsigned int stop); + + QString debug(); private: - QVector<qreal> m_pList; - qreal m_totalAver, m_shortAver; - - void getTotalAverage() { - m_totalAver = getAverage(1, m_pList.size()); - } + QVector<qreal> m_pList; + qreal m_totalAver, m_shortAver; - void getShortAverage() { - m_shortAver = getAverage(3, 6); - } + void getTotalAverage() { m_totalAver = getAverage(1, m_pList.size()); } + void getShortAverage() { m_shortAver = getAverage(3, 6); } }; #endif // TNOTESTRUCT_H diff --git a/src/libs/core/music/trhythm.cpp b/src/libs/core/music/trhythm.cpp index 950c0baf59887e74d81404007400ee0adadb81aa..ab97185b652a045a1a48cc2fc0bc875b8b59f66d 100644 --- a/src/libs/core/music/trhythm.cpp +++ b/src/libs/core/music/trhythm.cpp @@ -19,7 +19,6 @@ #include "trhythm.h" #include <QtCore/qdebug.h> - /** * Converts @p Trhythm into value: * - rhythmic value number: first 3 bytes [0-7] @@ -27,8 +26,9 @@ * - or is triplet: byte 8 [128] * It is used to store rhythm info in single byte for duration array */ -inline quint8 rHash(const Trhythm& r) { - return r.rhythm() + (r.hasDot() ? 64 : 0) + (r.isTriplet() ? 128 : 0); +inline quint8 rHash(const Trhythm &r) +{ + return r.rhythm() + (r.hasDot() ? 64 : 0) + (r.isTriplet() ? 128 : 0); } /*-----static-------*/ @@ -36,240 +36,240 @@ inline quint8 rHash(const Trhythm& r) { * Array that keeps hash values for every numeric duration [0-96] * or null if no such duration */ -static quint8 rArray[RVALUE + 1]; -const std::string Trhythm::rhythmStrings[6] = { "", "whole", "half", "quarter", "eighth", "16th" }; - -void Trhythm::initialize() { - for (int d = 0; d < RVALUE + 1; ++d) - rArray[d] = 0; - - for (int d = 1; d < 6 ; ++d) { - Trhythm r(static_cast<Erhythm>(d)); - rArray[r.duration()] = rHash(r); - if (d > 1) { // skip whole with dot (144 is too big) - r.setDot(true); - rArray[r.duration()] = rHash(r); - r.setDot(false); +static quint8 rArray[RVALUE + 1]; +const std::string Trhythm::rhythmStrings[6] = {"", "whole", "half", "quarter", "eighth", "16th"}; + +void Trhythm::initialize() +{ + for (int d = 0; d < RVALUE + 1; ++d) + rArray[d] = 0; + + for (int d = 1; d < 6; ++d) { + Trhythm r(static_cast<Erhythm>(d)); + rArray[r.duration()] = rHash(r); + if (d > 1) { // skip whole with dot (144 is too big) + r.setDot(true); + rArray[r.duration()] = rHash(r); + r.setDot(false); + } + r.setTriplet(true); + rArray[r.duration()] = rHash(r); } - r.setTriplet(true); - rArray[r.duration()] = rHash(r); - } -// for (quint8 r = 0; r <= RVALUE; ++r) { -// Trhythm rm(r); -// if (rm.rhythm() != Trhythm::e_none) -// qDebug() << r << rm.string(); -// } + // for (quint8 r = 0; r <= RVALUE; ++r) { + // Trhythm rm(r); + // if (rm.rhythm() != Trhythm::e_none) + // qDebug() << r << rm.string(); + // } } /*------------------*/ - -void Trhythm::setRhythmValue(const std::string& nVal) { - for (int i = 0; i < 6; ++i) { - if (nVal == rhythmStrings[i]) { - m_r = static_cast<Erhythm>(i); - return; +void Trhythm::setRhythmValue(const std::string &nVal) +{ + for (int i = 0; i < 6; ++i) { + if (nVal == rhythmStrings[i]) { + m_r = static_cast<Erhythm>(i); + return; + } } - } } - -QString Trhythm::xmlType() const { - return QString::fromStdString(rhythmStrings[m_r]); +QString Trhythm::xmlType() const +{ + return QString::fromStdString(rhythmStrings[m_r]); } - -void Trhythm::setRhythm(quint16 durationValue) { - m_prefs = 0; - m_r = NoRhythm; - if (durationValue <= 96) { - m_r = static_cast<Erhythm>(rArray[durationValue] & 7); // bit mask 11100000 to extract rhythm value - if (m_r != NoRhythm) { - if (rArray[durationValue] & 64) - m_prefs = e_dot; - else if (rArray[durationValue] & 128) - m_prefs = e_triplet; - } - } else { - if (durationValue == 144) { // "manually catch whole note with dot" - m_prefs = e_dot; - m_r = Whole; - } - } +void Trhythm::setRhythm(quint16 durationValue) +{ + m_prefs = 0; + m_r = NoRhythm; + if (durationValue <= 96) { + m_r = static_cast<Erhythm>(rArray[durationValue] & 7); // bit mask 11100000 to extract rhythm value + if (m_r != NoRhythm) { + if (rArray[durationValue] & 64) + m_prefs = e_dot; + else if (rArray[durationValue] & 128) + m_prefs = e_triplet; + } + } else { + if (durationValue == 144) { // "manually catch whole note with dot" + m_prefs = e_dot; + m_r = Whole; + } + } } - /** * In most cases it returns only single element list because subtracting can be resolved with only one rhythm value. */ -void Trhythm::sub(const Trhythm& r, TrhythmList& remained) const { - if (r.rhythm() == NoRhythm) { - remained << *this; - qDebug() << "[Trhythm] subtracting null rhythm! IS IT REALLY NECESSARY?"; - } else { - if (r.isTriplet() != isTriplet()) { // TODO: It has to be solved by changing main note - qDebug() << "[Trhythm] Subtracting triplets and no triplets unsupported"; - return; - } - - int baseDur = duration(); - int subDur = r.duration(); - - if (subDur > baseDur) { - qDebug() << "[Trhythm] Subtracting rhythm" << r.duration() << "is greater than" << duration(); - return; - } - if (baseDur - subDur == 0) { // Return empty (null) rhythm when rhythms are the same - remained << Trhythm(NoRhythm); - return; - } - Trhythm newR(baseDur - subDur, isRest()); - if (newR.rhythm() != NoRhythm) { // In most cases subtracting returns single rhythm - remained << newR; - return; - } - - if (r.isTriplet() || isTriplet()) // There is no support for subtracting triplets into multiple notes - return; - if (baseDur == 4) // 16th triplet - nothing to subtract from - return; - - // For the rest cases list will contain two Trhythm elements - if (baseDur == 36 && subDur == 6) // quarter with dot (4.) minus 16th = 4 and 16th - remained << Trhythm(Quarter, isRest()) << Trhythm(Eighth, isRest(), true); - else if (baseDur == 48) { // subtracting form half note - remained << Trhythm(Quarter, isRest()); - if (subDur == 6) // 2 - 16th = 4 and 8. - remained << Trhythm(Eighth, isRest(), true); - else if (subDur == 18) // 2 - 8. = 4 and 16th - remained << Trhythm(Sixteenth, isRest()); - } else if (baseDur == 72) { // subtracting from half with dot - remained << Trhythm(Whole, isRest()); // whole is always first - if (baseDur == 6) // 2. - 16th = 2 and 8. - remained << Trhythm(Eighth, isRest(), true); - else if (baseDur == 12) // 2. - 8 = 2 and 8 - remained << Trhythm(Eighth, isRest()); - else if (baseDur == 18) // 2. - 8. = 2 and 16th - remained << Trhythm(Sixteenth, isRest()); - } else if (baseDur == 96) { // subtracting from whole note - remained << Trhythm(Whole, isRest(), true); // whole wit dot is always first - if (subDur == 6) // 1 - 16 = 2. and 8. - remained << Trhythm(Eighth, isRest(), true); - else if (baseDur == 12) // 1 - 8 = 2. and 8 - remained << Trhythm(Eighth, isRest()); - else if (baseDur == 18) // 1 - 8. = 2. and 16th - remained << Trhythm(Sixteenth, isRest()); - else if (baseDur == 36) { // 1 - 4. = 2 and 16th - remained[0].setDot(false); // revert a dot set above - remained << Trhythm(Sixteenth, isRest()); - } - } else if (baseDur == 144) { // subtracting from whole and dot - if (subDur <= 48) { - Trhythm half(Half, isRest()); - half.sub(r, remained); - remained.prepend(Trhythm(Whole, isRest())); - } - } - } +void Trhythm::sub(const Trhythm &r, TrhythmList &remained) const +{ + if (r.rhythm() == NoRhythm) { + remained << *this; + qDebug() << "[Trhythm] subtracting null rhythm! IS IT REALLY NECESSARY?"; + } else { + if (r.isTriplet() != isTriplet()) { // TODO: It has to be solved by changing main note + qDebug() << "[Trhythm] Subtracting triplets and no triplets unsupported"; + return; + } + + int baseDur = duration(); + int subDur = r.duration(); + + if (subDur > baseDur) { + qDebug() << "[Trhythm] Subtracting rhythm" << r.duration() << "is greater than" << duration(); + return; + } + if (baseDur - subDur == 0) { // Return empty (null) rhythm when rhythms are the same + remained << Trhythm(NoRhythm); + return; + } + Trhythm newR(baseDur - subDur, isRest()); + if (newR.rhythm() != NoRhythm) { // In most cases subtracting returns single rhythm + remained << newR; + return; + } + + if (r.isTriplet() || isTriplet()) // There is no support for subtracting triplets into multiple notes + return; + if (baseDur == 4) // 16th triplet - nothing to subtract from + return; + + // For the rest cases list will contain two Trhythm elements + if (baseDur == 36 && subDur == 6) // quarter with dot (4.) minus 16th = 4 and 16th + remained << Trhythm(Quarter, isRest()) << Trhythm(Eighth, isRest(), true); + else if (baseDur == 48) { // subtracting form half note + remained << Trhythm(Quarter, isRest()); + if (subDur == 6) // 2 - 16th = 4 and 8. + remained << Trhythm(Eighth, isRest(), true); + else if (subDur == 18) // 2 - 8. = 4 and 16th + remained << Trhythm(Sixteenth, isRest()); + } else if (baseDur == 72) { // subtracting from half with dot + remained << Trhythm(Whole, isRest()); // whole is always first + if (baseDur == 6) // 2. - 16th = 2 and 8. + remained << Trhythm(Eighth, isRest(), true); + else if (baseDur == 12) // 2. - 8 = 2 and 8 + remained << Trhythm(Eighth, isRest()); + else if (baseDur == 18) // 2. - 8. = 2 and 16th + remained << Trhythm(Sixteenth, isRest()); + } else if (baseDur == 96) { // subtracting from whole note + remained << Trhythm(Whole, isRest(), true); // whole wit dot is always first + if (subDur == 6) // 1 - 16 = 2. and 8. + remained << Trhythm(Eighth, isRest(), true); + else if (baseDur == 12) // 1 - 8 = 2. and 8 + remained << Trhythm(Eighth, isRest()); + else if (baseDur == 18) // 1 - 8. = 2. and 16th + remained << Trhythm(Sixteenth, isRest()); + else if (baseDur == 36) { // 1 - 4. = 2 and 16th + remained[0].setDot(false); // revert a dot set above + remained << Trhythm(Sixteenth, isRest()); + } + } else if (baseDur == 144) { // subtracting from whole and dot + if (subDur <= 48) { + Trhythm half(Half, isRest()); + half.sub(r, remained); + remained.prepend(Trhythm(Whole, isRest())); + } + } + } } - -void Trhythm::split(TrhythmList& twoRhythms) const { - if (rhythm() == NoRhythm || rhythm() == Sixteenth) - return; // nothing to split - - if (hasDot()) { - twoRhythms << Trhythm(rhythm(), isRest(), false) // no triplet for sure - << Trhythm(static_cast<Erhythm>(rhythm() + 1), isRest()); - // Also when there is a dot, for sure it is not lowest possible rhythm, so we may add 1 to rhythm value to obtain its half - } else { - Trhythm half(static_cast<Erhythm>(rhythm() + 1), isRest(), false, isTriplet()); - twoRhythms << half << half; - } - if (!isRest() && twoRhythms.size() == 2) { - twoRhythms.first().setStemDown(stemDown()); - twoRhythms.last().setStemDown(stemDown()); - } +void Trhythm::split(TrhythmList &twoRhythms) const +{ + if (rhythm() == NoRhythm || rhythm() == Sixteenth) + return; // nothing to split + + if (hasDot()) { + twoRhythms << Trhythm(rhythm(), isRest(), false) // no triplet for sure + << Trhythm(static_cast<Erhythm>(rhythm() + 1), isRest()); + // Also when there is a dot, for sure it is not lowest possible rhythm, so we may add 1 to rhythm value to obtain its half + } else { + Trhythm half(static_cast<Erhythm>(rhythm() + 1), isRest(), false, isTriplet()); + twoRhythms << half << half; + } + if (!isRest() && twoRhythms.size() == 2) { + twoRhythms.first().setStemDown(stemDown()); + twoRhythms.last().setStemDown(stemDown()); + } } - -void Trhythm::resolve(int problemDur, TrhythmList& solvList) { - int smallestDur = 0; - for (int i = 1; i < 6; ++i) { // looking for smallest rhythm to divide given duration @p dur - smallestDur = Trhythm(static_cast<Trhythm::Erhythm>(i)).duration(); // no triplets here - if (smallestDur < problemDur) { - if ((problemDur / smallestDur) * smallestDur == problemDur) - break; - } - } - if (smallestDur) { - int chunksNr = problemDur / smallestDur; // number of smallest rhythm values in duration @p dur - int step = 0; - solvList << Trhythm() << Trhythm(); - do { // find when rhythms of two divided notes of duration are valid - step++; - solvList[0].setRhythm((chunksNr - step) * smallestDur); - solvList[1].setRhythm(smallestDur * step); - } while (step < chunksNr - 1 && (!solvList[0].isValid() || !solvList[1].isValid())); - } +void Trhythm::resolve(int problemDur, TrhythmList &solvList) +{ + int smallestDur = 0; + for (int i = 1; i < 6; ++i) { // looking for smallest rhythm to divide given duration @p dur + smallestDur = Trhythm(static_cast<Trhythm::Erhythm>(i)).duration(); // no triplets here + if (smallestDur < problemDur) { + if ((problemDur / smallestDur) * smallestDur == problemDur) + break; + } + } + if (smallestDur) { + int chunksNr = problemDur / smallestDur; // number of smallest rhythm values in duration @p dur + int step = 0; + solvList << Trhythm() << Trhythm(); + do { // find when rhythms of two divided notes of duration are valid + step++; + solvList[0].setRhythm((chunksNr - step) * smallestDur); + solvList[1].setRhythm(smallestDur * step); + } while (step < chunksNr - 1 && (!solvList[0].isValid() || !solvList[1].isValid())); + } } - -TrhythmList Trhythm::resolve(int problemDur, int* unsolvedDur) { - int workDur = problemDur; - TrhythmList rList; - for (int d = 0; d < DUR_COUNT; ++d) { - while (workDur > durOrder[d]) { - rList << Trhythm(durOrder[d]); - workDur -= durOrder[d]; +TrhythmList Trhythm::resolve(int problemDur, int *unsolvedDur) +{ + int workDur = problemDur; + TrhythmList rList; + for (int d = 0; d < DUR_COUNT; ++d) { + while (workDur > durOrder[d]) { + rList << Trhythm(durOrder[d]); + workDur -= durOrder[d]; + } + if (workDur == durOrder[d]) { + rList << Trhythm(durOrder[d]); + workDur -= durOrder[d]; + break; + } // else try next loop with smaller rhythmic value } - if (workDur == durOrder[d]) { - rList << Trhythm(durOrder[d]); - workDur -= durOrder[d]; - break; - } // else try next loop with smaller rhythmic value - } - if (workDur > 0) { - qDebug() << "[Trhythm] Can not resolve duration of" << problemDur << ". Remains" << workDur; - if (unsolvedDur) - *unsolvedDur = workDur; - } - return rList; + if (workDur > 0) { + qDebug() << "[Trhythm] Can not resolve duration of" << problemDur << ". Remains" << workDur; + if (unsolvedDur) + *unsolvedDur = workDur; + } + return rList; } - -QString Trhythm::string() const { - QString ret = QString::number(weight()); - if (isRest()) - ret.prepend(QStringLiteral("R")); - if (hasDot()) - ret.append(QStringLiteral(".")); - else if (isTriplet()) - ret.append(QStringLiteral("^3")); - return ret; +QString Trhythm::string() const +{ + QString ret = QString::number(weight()); + if (isRest()) + ret.prepend(QStringLiteral("R")); + if (hasDot()) + ret.append(QStringLiteral(".")); + else if (isTriplet()) + ret.append(QStringLiteral("^3")); + return ret; } - -void Trhythm::debug(const char* text) const { - if (m_r == NoRhythm) - qDebug() << text << "no rhythm"; - else { - qDebug() << text << xmlType() << "| rest" << isRest() << "| dot" << hasDot() << "| triplet" << isTriplet() << "| duration" << duration() - << "| beam" << beam() << "| tie" << tie() << "| stem" << (stemDown() ? "down" : "up") - << "|" << (m_prefs % 8) << m_prefs; - } +void Trhythm::debug(const char *text) const +{ + if (m_r == NoRhythm) + qDebug() << text << "no rhythm"; + else { + qDebug() << text << xmlType() << "| rest" << isRest() << "| dot" << hasDot() << "| triplet" << isTriplet() << "| duration" << duration() << "| beam" + << beam() << "| tie" << tie() << "| stem" << (stemDown() ? "down" : "up") << "|" << (m_prefs % 8) << m_prefs; + } } - -QDataStream& operator << (QDataStream& out, const Trhythm& r) { - out << static_cast<quint8>(r.rhythm()) << static_cast<quint8>(r.parameters()); - return out; +QDataStream &operator<<(QDataStream &out, const Trhythm &r) +{ + out << static_cast<quint8>(r.rhythm()) << static_cast<quint8>(r.parameters()); + return out; } - -QDataStream& operator >>(QDataStream& in, Trhythm& r) { - quint8 rr, p; - in >> rr >> p; - r.setRhythm(static_cast<Trhythm::Erhythm>(rr)); - r.setParameters(p); - return in; +QDataStream &operator>>(QDataStream &in, Trhythm &r) +{ + quint8 rr, p; + in >> rr >> p; + r.setRhythm(static_cast<Trhythm::Erhythm>(rr)); + r.setParameters(p); + return in; } diff --git a/src/libs/core/music/trhythm.h b/src/libs/core/music/trhythm.h index 06eb6aa0423c24a34370036b9a128d665971ef20..77b263bc641271cf62619a7af9ad942c9cbfffff 100644 --- a/src/libs/core/music/trhythm.h +++ b/src/libs/core/music/trhythm.h @@ -19,48 +19,44 @@ #ifndef TRHYTHM_H #define TRHYTHM_H - #include "nootkacoreglobal.h" -#include <QtCore/qobject.h> #include <QtCore/qdatastream.h> #include <QtCore/qmath.h> +#include <QtCore/qobject.h> - - /** - * Almost powers of 2, used to quickly mapping @p Erhythm enumerator into weight of rhythm value - */ +/** + * Almost powers of 2, used to quickly mapping @p Erhythm enumerator into weight of rhythm value + */ static const quint8 rtm2weightArr[6] = {0, 1, 2, 4, 8, 16}; - - /** - * Base value to calculate proportional values of rhythm duration: - * 96 - whole note - * 6 - sixteen note - * 4 - sixteen triplet - */ +/** + * Base value to calculate proportional values of rhythm duration: + * 96 - whole note + * 6 - sixteen note + * 4 - sixteen triplet + */ #define RVALUE (96) - /** - * Array with duration values - */ +/** + * Array with duration values + */ static const quint8 durArray[6][3] = { -// | bare note | dot | triplet | - { 0, 0, 0 }, // none - { RVALUE, (RVALUE * 3) / 2, (RVALUE * 2) / 3}, // whole note (96, 144, 64) - { RVALUE / 2, (RVALUE * 3) / 4, RVALUE / 3 }, // half note (48, 72, 32) - { RVALUE / 4, (RVALUE * 3) / 8, RVALUE / 6 }, // quarter note (24, 36, 16) - { RVALUE / 8, (RVALUE * 3) / 16, RVALUE / 12 }, // eighth note (12, 18, 8) - { RVALUE / 16,(RVALUE * 3) / 32, RVALUE / 24 } // sixteenth note (6, 9, 4) + // | bare note | dot | triplet | + {0, 0, 0}, // none + {RVALUE, (RVALUE * 3) / 2, (RVALUE * 2) / 3}, // whole note (96, 144, 64) + {RVALUE / 2, (RVALUE * 3) / 4, RVALUE / 3}, // half note (48, 72, 32) + {RVALUE / 4, (RVALUE * 3) / 8, RVALUE / 6}, // quarter note (24, 36, 16) + {RVALUE / 8, (RVALUE * 3) / 16, RVALUE / 12}, // eighth note (12, 18, 8) + {RVALUE / 16, (RVALUE * 3) / 32, RVALUE / 24} // sixteenth note (6, 9, 4) }; -#define DUR_COUNT (10) // total number of single notes duration (TODO: no triplets yet) -static const quint8 durOrder[DUR_COUNT] = { 144, 96, 72, 48, 36, 24, 18, 12, 9, 6 }; +#define DUR_COUNT (10) // total number of single notes duration (TODO: no triplets yet) +static const quint8 durOrder[DUR_COUNT] = {144, 96, 72, 48, 36, 24, 18, 12, 9, 6}; class Trhythm; typedef QList<Trhythm> TrhythmList; - /** * This class describes musical note value (relative duration) * but has limitations for Nootka purposes. @@ -70,232 +66,230 @@ typedef QList<Trhythm> TrhythmList; */ class NOOTKACORE_EXPORT Trhythm { - - Q_GADGET + Q_GADGET public: + static const std::string rhythmStrings[6]; + /** + * Initialize class @p Trhythm with values of duration. + * Without this, @p setRhythm(int) will not work. + */ + static void initialize(); + + /** + * Describes note duration + */ + enum Erhythm : quint8 { NoRhythm = 0, Whole = 1, Half = 2, Quarter = 3, Eighth = 4, Sixteenth = 5 }; + Q_ENUM(Erhythm) + + /** + * Additional note preferences + */ + enum Eprefs : quint8 { e_rest = 1, e_dot = 2, e_triplet = 4, e_stemDown = 8 }; + + /** + * It covers 16&32 bits of @p m_prefs value. It is mapped into @enum Ebeam then + */ + enum Ebeam : quint8 { e_noBeam = 0, e_beamStart = 16, e_beamCont = 32, e_beamEnd = 48 }; + Q_ENUM(Ebeam) + + enum Etie : quint8 { e_noTie = 0, e_tieStart = 64, e_tieCont = 128, e_tieEnd = 192 }; + Q_ENUM(Etie) + + /** + * DEFAULT CONSTRUCTOR: by default it is quarter (without dot) + */ + Trhythm(Erhythm nVal = Quarter, bool rest = false, bool dot = false, bool triplet = false) { setRhythm(nVal, rest, dot, triplet); } + + /** + * Creates rhythm from given duration value + */ + Trhythm(int rhythmDuration, bool rest = false) + { + setRhythm(rhythmDuration); + setRest(rest); + } + + /** + * Sets rhythm parameters, Resets all previous values! + */ + void setRhythm(Erhythm nVal, bool rest = false, bool dot = false, bool triplet = false) + { + m_r = nVal; + m_prefs = (rest ? 1 : 0) + (dot ? 2 : (triplet ? 4 : 0)); + } + + Erhythm rhythm() const { return m_r; } + + /** + * Changes rhythmic value only, state of dot, triplet, beams remains unchanged + */ + void setRhythmValue(Erhythm nVal) { m_r = nVal; } + + /** + * It converts std::string into rhythm value. Doesn't change state of triplet, dot or beam. + */ + void setRhythmValue(const std::string &nVal); - static const std::string rhythmStrings[6]; - /** - * Initialize class @p Trhythm with values of duration. - * Without this, @p setRhythm(int) will not work. - */ - static void initialize(); - - /** - * Describes note duration - */ - enum Erhythm : quint8 { - NoRhythm = 0, Whole = 1, Half = 2, Quarter = 3, Eighth = 4, Sixteenth = 5 - }; - Q_ENUM(Erhythm) - - /** - * Additional note preferences - */ - enum Eprefs : quint8 { - e_rest = 1, e_dot = 2, e_triplet = 4, e_stemDown = 8 - }; - - /** - * It covers 16&32 bits of @p m_prefs value. It is mapped into @enum Ebeam then - */ - enum Ebeam : quint8 { - e_noBeam = 0, e_beamStart = 16, e_beamCont = 32, e_beamEnd = 48 - }; - Q_ENUM(Ebeam) - - enum Etie : quint8 { - e_noTie = 0, e_tieStart = 64, e_tieCont = 128, e_tieEnd = 192 - }; - Q_ENUM(Etie) - - /** - * DEFAULT CONSTRUCTOR: by default it is quarter (without dot) - */ - Trhythm(Erhythm nVal = Quarter, bool rest = false, bool dot = false, bool triplet = false) - { - setRhythm(nVal, rest, dot, triplet); - } - - /** - * Creates rhythm from given duration value - */ - Trhythm(int rhythmDuration, bool rest = false) - { - setRhythm(rhythmDuration); - setRest(rest); - } - - /** - * Sets rhythm parameters, Resets all previous values! - */ - void setRhythm(Erhythm nVal, bool rest = false, bool dot = false, bool triplet = false) { - m_r = nVal; - m_prefs = (rest ? 1 : 0) + (dot ? 2 : (triplet ? 4 : 0)); - } - - Erhythm rhythm() const { return m_r; } - - /** - * Changes rhythmic value only, state of dot, triplet, beams remains unchanged - */ - void setRhythmValue(Erhythm nVal) { m_r = nVal; } - - /** - * It converts std::string into rhythm value. Doesn't change state of triplet, dot or beam. - */ - void setRhythmValue(const std::string& nVal); - - /** - * Makes quick copy of another @p Trhythm instance. - */ - void setRhythm(const Trhythm& r) { m_r = r.rhythm(); m_prefs = r.parameters(); } - - /** - * Converts given value into rhythm - */ - void setRhythm(quint16 durationValue); - - /** - * Rhythm value: i.e. sixteen is 16, quarter is 4, half is 2 and so on - */ - quint8 weight() const { return rtm2weightArr[m_r]; } - - /** - * Rhythm is valid when it is different than @p Trhythm::NoRhythm - */ - bool isValid() const { return m_r != NoRhythm; } - - /** - * Whole note is 96, half is 48, quarter is 24 and with dot is 36. Single eight triplet is 8. - * Base value is defined in @p RVALUE macro - */ - int duration() const { - return durArray[m_r][hasDot() ? 1 : (isTriplet() ? 2 : 0)]; - } - - bool isRest() const { return m_prefs & e_rest; } - void setRest(bool rest) { - if (rest) - m_prefs |= e_rest; - else - m_prefs &= ~e_rest; - } - - bool hasDot() const { return m_prefs & e_dot; } - - /** - * Allows to set dot only if no triplet - */ - void setDot(bool dot) { - if (dot) { - if (!isTriplet()) - m_prefs |= e_dot; - } else - m_prefs &= ~e_dot; - } - - bool isTriplet() const { return m_prefs & e_triplet; } - - /** - * Allows to set triplet only if no dot - */ - void setTriplet(bool tri) { - if (tri) { - if (!hasDot()) - m_prefs |= e_triplet; - } else - m_prefs &= ~e_triplet; - } - - void setBeam(Ebeam b) { - m_prefs &= ~e_beamEnd; // reset previous beam to none (~e_beamEnd is reverted bin of 48 to set those bits to 0) - m_prefs |= b; - } - Ebeam beam() const { return Ebeam(m_prefs & e_beamEnd); } - - void setTie(Etie t) { - m_prefs &= ~e_tieEnd; - m_prefs |= t; - } - Etie tie() const { return Etie(m_prefs & e_tieEnd); } - - void setStemDown(bool stemD) { - if (stemD) - m_prefs |= e_stemDown; - else - m_prefs &= ~e_stemDown; - } - bool stemDown() const { return m_prefs & e_stemDown; } - - /** - * Subtract @p r rhythm from current rhythm and writes returned rhythm(s) to given list @p remained. - * The list is necessary due to some of subtractions are impossible to be expressed in single @p Trhythm value. - * I.e. half - 16th is quarter and eight with dot. - * It doesn't change current rhythm. - * @p remained list may be empty (unchanged) when subtraction is impossible to perform - */ - void sub(const Trhythm& r, TrhythmList& remained) const; - - /** - * Splits current rhythm into two rhythm values and writes result into @p twoRhythms list - * When current rhythm is without dot it divides it into two equal parts: - * - i.e. quarter will be two eighths - * but notes with dots splits on main part and dot part: - * - i.e. 8. will be 8 and sixteenth - * It doesn't change current rhythm - */ - void split(TrhythmList& twoRhythms) const; - - /** - * Tries to solve given @p problemDur which is not possible to express in single @p Trhythm instance - * with a few rhythms put into @p solvList - */ - static void resolve(int problemDur, TrhythmList& solvList); - - /** - * Static method returning @p TrhythmList with rhythmic values covering given @p problemDur. - * If @p unsolvedDur pointer is set, rest of @p problemDur which can't be expressed with correct rhythmic value is put into it. - */ - static TrhythmList resolve(int problemDur, int* unsolvedDur = nullptr); - - /** - * Returns string with formatted rhythm value i.e. 4 or 8. or 16^3 - */ - QString string() const; - - QString xmlType() const; - - /** - * Prints current rhythm parameters to std out with given text - */ - void debug(const char* text = 0) const; - - bool operator==(const Trhythm& r) const { - return m_r == r.rhythm() && ((m_prefs % 8) == (r.parameters() % 8)); // compare only first three bits of m_prefs (rest, dot and triplet) - } - - bool operator!=(const Trhythm& r) const { - return m_r != r.rhythm() || ((m_prefs % 8) != (r.parameters() % 8)); // compare only first three bits of m_prefs (rest, dot and triplet) - } - - friend QDataStream& operator << (QDataStream &out, const Trhythm &r); - friend QDataStream& operator >> (QDataStream &in, Trhythm &r); + /** + * Makes quick copy of another @p Trhythm instance. + */ + void setRhythm(const Trhythm &r) + { + m_r = r.rhythm(); + m_prefs = r.parameters(); + } + + /** + * Converts given value into rhythm + */ + void setRhythm(quint16 durationValue); + + /** + * Rhythm value: i.e. sixteen is 16, quarter is 4, half is 2 and so on + */ + quint8 weight() const { return rtm2weightArr[m_r]; } + + /** + * Rhythm is valid when it is different than @p Trhythm::NoRhythm + */ + bool isValid() const { return m_r != NoRhythm; } + + /** + * Whole note is 96, half is 48, quarter is 24 and with dot is 36. Single eight triplet is 8. + * Base value is defined in @p RVALUE macro + */ + int duration() const { return durArray[m_r][hasDot() ? 1 : (isTriplet() ? 2 : 0)]; } + + bool isRest() const { return m_prefs & e_rest; } + void setRest(bool rest) + { + if (rest) + m_prefs |= e_rest; + else + m_prefs &= ~e_rest; + } + + bool hasDot() const { return m_prefs & e_dot; } + + /** + * Allows to set dot only if no triplet + */ + void setDot(bool dot) + { + if (dot) { + if (!isTriplet()) + m_prefs |= e_dot; + } else + m_prefs &= ~e_dot; + } + + bool isTriplet() const { return m_prefs & e_triplet; } + + /** + * Allows to set triplet only if no dot + */ + void setTriplet(bool tri) + { + if (tri) { + if (!hasDot()) + m_prefs |= e_triplet; + } else + m_prefs &= ~e_triplet; + } + + void setBeam(Ebeam b) + { + m_prefs &= ~e_beamEnd; // reset previous beam to none (~e_beamEnd is reverted bin of 48 to set those bits to 0) + m_prefs |= b; + } + Ebeam beam() const { return Ebeam(m_prefs & e_beamEnd); } + + void setTie(Etie t) + { + m_prefs &= ~e_tieEnd; + m_prefs |= t; + } + Etie tie() const { return Etie(m_prefs & e_tieEnd); } + + void setStemDown(bool stemD) + { + if (stemD) + m_prefs |= e_stemDown; + else + m_prefs &= ~e_stemDown; + } + bool stemDown() const { return m_prefs & e_stemDown; } + + /** + * Subtract @p r rhythm from current rhythm and writes returned rhythm(s) to given list @p remained. + * The list is necessary due to some of subtractions are impossible to be expressed in single @p Trhythm value. + * I.e. half - 16th is quarter and eight with dot. + * It doesn't change current rhythm. + * @p remained list may be empty (unchanged) when subtraction is impossible to perform + */ + void sub(const Trhythm &r, TrhythmList &remained) const; + + /** + * Splits current rhythm into two rhythm values and writes result into @p twoRhythms list + * When current rhythm is without dot it divides it into two equal parts: + * - i.e. quarter will be two eighths + * but notes with dots splits on main part and dot part: + * - i.e. 8. will be 8 and sixteenth + * It doesn't change current rhythm + */ + void split(TrhythmList &twoRhythms) const; + + /** + * Tries to solve given @p problemDur which is not possible to express in single @p Trhythm instance + * with a few rhythms put into @p solvList + */ + static void resolve(int problemDur, TrhythmList &solvList); + + /** + * Static method returning @p TrhythmList with rhythmic values covering given @p problemDur. + * If @p unsolvedDur pointer is set, rest of @p problemDur which can't be expressed with correct rhythmic value is put into it. + */ + static TrhythmList resolve(int problemDur, int *unsolvedDur = nullptr); + + /** + * Returns string with formatted rhythm value i.e. 4 or 8. or 16^3 + */ + QString string() const; + + QString xmlType() const; + + /** + * Prints current rhythm parameters to std out with given text + */ + void debug(const char *text = 0) const; + + bool operator==(const Trhythm &r) const + { + return m_r == r.rhythm() && ((m_prefs % 8) == (r.parameters() % 8)); // compare only first three bits of m_prefs (rest, dot and triplet) + } + + bool operator!=(const Trhythm &r) const + { + return m_r != r.rhythm() || ((m_prefs % 8) != (r.parameters() % 8)); // compare only first three bits of m_prefs (rest, dot and triplet) + } + + friend QDataStream &operator<<(QDataStream &out, const Trhythm &r); + friend QDataStream &operator>>(QDataStream &in, Trhythm &r); protected: - /** - * For copy purposes - */ - quint8 parameters() const { return m_prefs; } - void setParameters(quint8 p) { m_prefs = p; } + /** + * For copy purposes + */ + quint8 parameters() const { return m_prefs; } + void setParameters(quint8 p) { m_prefs = p; } private: - Erhythm m_r; - quint8 m_prefs = 0; + Erhythm m_r; + quint8 m_prefs = 0; }; -NOOTKACORE_EXPORT QDataStream& operator << (QDataStream &out, const Trhythm &r); -NOOTKACORE_EXPORT QDataStream& operator >> (QDataStream &in, Trhythm &r); +NOOTKACORE_EXPORT QDataStream &operator<<(QDataStream &out, const Trhythm &r); +NOOTKACORE_EXPORT QDataStream &operator>>(QDataStream &in, Trhythm &r); #endif // TRHYTHM_H diff --git a/src/libs/core/music/trtmgroup.cpp b/src/libs/core/music/trtmgroup.cpp index 02d15edd7056d5f396754e4d613b459a00880433..3706180b245ed0cffcedc755df5f1c7018c18637 100644 --- a/src/libs/core/music/trtmgroup.cpp +++ b/src/libs/core/music/trtmgroup.cpp @@ -18,143 +18,288 @@ #include "trtmgroup.h" - /** Shortcuts of C++ statements to be more readable in code */ -#define whole (QStringLiteral("\ue1d2")) -#define half (QStringLiteral("\ue1d3")) -#define quarter (QStringLiteral("\ue1f1")) -#define eight (QStringLiteral("\ue1d7")) -#define note8 (QStringLiteral("\ue1f3")) -#define note16 (QStringLiteral("\ue1f5")) -#define beam8 (QStringLiteral("\ue1f8")) -#define beam16 (QStringLiteral("\ue1fa")) -#define dot (QStringLiteral("\ue1fc")) -#define space (QStringLiteral(" ")) - -#define R4 Trhythm(Trhythm::Quarter) -#define R8 Trhythm(Trhythm::Eighth) -#define R8dot Trhythm(Trhythm::Eighth, false, true) -#define R16 Trhythm(Trhythm::Sixteenth) - - -QString TrtmGroup::text() const { - QString t; - switch (m_gr) { - case Gr_1: t = whole; break; - case Gr_2: t = half; break; - case Gr_4: t = quarter; break; - case Gr_8_8: t = quarter + beam8 + note8; break; - case Gr_8_16_16: t = quarter + beam8 + note8 + beam16 + note16; break; - case Gr_16_16_16_16: t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16; break; - case Gr_16_16_8: t = quarter + beam16 + note16 + beam8 + note8; break; - case Gr_16_8_16: t = quarter + beam16 + note8 + beam8 + note16 ; break; - - case Gr_8_8_8: t = quarter + beam8 + note8 + beam8 + note8; break; - case Gr_4_8: t = quarter + space + space + eight; break; - case Gr_8_4: t = eight + space + quarter; break; - case Gr_8_8_16_16: t = quarter + beam8 + note8 + beam8 + note8 + beam16 + note16; break; - case Gr_8_16_16_8: t = quarter + beam8 + note8 + beam16 + note16 + beam8 + note8; break; - case Gr_16_16_8_8: t = quarter + beam16 + note16 + beam8 + note8 + beam8 + note8; break; - case Gr_8_16_16_16_16: t = t = quarter + beam8 + note8 + beam16 + note16 + beam16 + note16 + beam16 + note16; break; - case Gr_16_16_16_16_8: t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam8 + note8; break; - case Gr_16_16_8_16_16: t = quarter + beam16 + note16 + beam8 + note8 + beam8 + note8 + beam16 + note16; break; - case Gr_16_16_16_16_16_16: t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam16 + note16; break; - - case Gr_16_8_8_16: t = quarter + beam16 + note8 + beam8 + note8 + beam8 + note16; break; - case Gr_16_8_16_8: t = quarter + beam16 + note8 + beam8 + beam8 + note16 + beam8 + note8; break; - case Gr_16_16_16_8_16: t = quarter + beam16 + note16 + beam16 + note16 + beam8 + note8 + beam8 + note16; break; - case Gr_16_8_16_16_16: t = quarter + beam16 + note8 + beam8 + note8 + beam16 + note16 + beam16 + note16; break; - - case Gr_1dot: t = whole + space + dot; break; - case Gr_2dot: t = half + space + dot; break; - case Gr_4dot: t = quarter + dot; break; - case Gr_8dot_16: t = quarter + beam8 + dot + beam8 + note16; break; - case Gr_16_8dot: t = quarter + beam16 + note8 + dot; break; - - case Gr_8dot_8dot: t = quarter + beam8 + dot + beam8 + note8 + dot; break; - case Gr_8dot_16_8: t = quarter + beam8 + dot + beam8 + note16 + beam8 + note8; break; - case Gr_8dot_8_16: t = quarter + beam8 + dot + beam8 + note8 + beam8 + note16; break; - case Gr_16_8dot_8: t = quarter + beam16 + note8 + beam8 + dot + beam8 + note8; break; - case Gr_8_8dot_16: t = quarter + beam8 + note8 + beam8 + dot + beam8 + note16; break; - case Gr_8dot_16_16_16: t = quarter + beam8 + dot + beam8 + note8 + beam16 + note16 + beam16 + note16; break; - case Gr_16_8dot_16_16: t = quarter + beam16 + note8 + beam8 + dot + beam8 + note8 + beam16 + note16; break; - case Gr_16_16_8dot_16: t = quarter + beam16 + note16 + beam8 + note8 + beam8 + dot + beam8 + note16; break; - case Gr_16_16_16_8dot: t = quarter + beam16 + note16 + beam16 + note16 + beam8 + note8 + dot; break; - default: break; - } - return t; -} +#define whole (QStringLiteral("\ue1d2")) +#define half (QStringLiteral("\ue1d3")) +#define quarter (QStringLiteral("\ue1f1")) +#define eight (QStringLiteral("\ue1d7")) +#define note8 (QStringLiteral("\ue1f3")) +#define note16 (QStringLiteral("\ue1f5")) +#define beam8 (QStringLiteral("\ue1f8")) +#define beam16 (QStringLiteral("\ue1fa")) +#define dot (QStringLiteral("\ue1fc")) +#define space (QStringLiteral(" ")) +#define R4 Trhythm(Trhythm::Quarter) +#define R8 Trhythm(Trhythm::Eighth) +#define R8dot Trhythm(Trhythm::Eighth, false, true) +#define R16 Trhythm(Trhythm::Sixteenth) -TrhythmList TrtmGroup::rhythm() const { - TrhythmList rl; - switch (m_gr) { - case Gr_1: rl << Trhythm(Trhythm::Whole); break; - case Gr_2: rl << Trhythm(Trhythm::Half); break; - case Gr_4: rl << R4; break; - case Gr_8_8: rl << R8 << R8; break; - case Gr_8_16_16: rl << R8 << R16 << R16; break; - case Gr_16_16_16_16: rl << R16 << R16 << R16 << R16; break; - case Gr_16_16_8: rl << R16 << R16 << R8; break; - case Gr_16_8_16: rl << R16 << R8 << R16; break; - - case Gr_8_8_8: rl << R8 << R8 << R8; break; - case Gr_4_8: rl << R4 << R8; break; - case Gr_8_4: rl << R8 << R4; break; - case Gr_8_8_16_16: rl << R8 << R8 << R16 << R16; break; - case Gr_8_16_16_8: rl << R8 << R16 << R16 << R8; break; - case Gr_16_16_8_8: rl << R16 << R16 << R8 << R8; break; - case Gr_8_16_16_16_16: rl << R8 << R16 << R16 << R16 << R16; break; - case Gr_16_16_16_16_8: rl << R16 << R16 << R16 << R16 << R8 ; break; - case Gr_16_16_8_16_16: rl << R16 << R16 << R8 << R16 << R16; break; - case Gr_16_16_16_16_16_16: rl << R16 << R16 << R16 << R16 << R16 << R16; break; - - case Gr_16_8_8_16: rl << R16 << R8 << R8 << R16; break; - case Gr_16_8_16_8: rl << R16 << R8 << R16 << R8; break; - case Gr_16_16_16_8_16: rl << R16 << R16 << R16 << R8 << R16; break; - case Gr_16_8_16_16_16: rl << R16 << R8 << R16 << R16 << R16; break; - - case Gr_1dot: rl << Trhythm(Trhythm::Whole, false, true); break; - case Gr_2dot: rl << Trhythm(Trhythm::Half, false, true); break; - case Gr_4dot: rl << Trhythm(Trhythm::Quarter, false, true); break; - - case Gr_8dot_16: rl << R8dot << R16; break; - case Gr_16_8dot: rl << R16 << R8dot; break; -// // Triple metre (duration: 4. -> one quarter and eight) ==== DOTS - case Gr_8dot_8dot: rl << R8dot << R8dot; break; - case Gr_8dot_16_8: rl << R8dot << R16 << R8; break; - case Gr_8dot_8_16: rl << R8dot << R8 << R16; break; - case Gr_16_8dot_8: rl << R16 << R8dot << R8; break; - case Gr_8_8dot_16: rl << R8 << R8dot << R16; break; - case Gr_8dot_16_16_16: rl << R8dot << R16 << R16 << R16; break; - case Gr_16_8dot_16_16: rl << R16 << R8dot << R16 << R16; break; - case Gr_16_16_8dot_16: rl << R16 << R16 << R8dot << R16; break; - case Gr_16_16_16_8dot: rl << R16 << R16 << R16 << R8dot; break; - default: break; - } - return rl; -} +QString TrtmGroup::text() const +{ + QString t; + switch (m_gr) { + case Gr_1: + t = whole; + break; + case Gr_2: + t = half; + break; + case Gr_4: + t = quarter; + break; + case Gr_8_8: + t = quarter + beam8 + note8; + break; + case Gr_8_16_16: + t = quarter + beam8 + note8 + beam16 + note16; + break; + case Gr_16_16_16_16: + t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16; + break; + case Gr_16_16_8: + t = quarter + beam16 + note16 + beam8 + note8; + break; + case Gr_16_8_16: + t = quarter + beam16 + note8 + beam8 + note16; + break; + + case Gr_8_8_8: + t = quarter + beam8 + note8 + beam8 + note8; + break; + case Gr_4_8: + t = quarter + space + space + eight; + break; + case Gr_8_4: + t = eight + space + quarter; + break; + case Gr_8_8_16_16: + t = quarter + beam8 + note8 + beam8 + note8 + beam16 + note16; + break; + case Gr_8_16_16_8: + t = quarter + beam8 + note8 + beam16 + note16 + beam8 + note8; + break; + case Gr_16_16_8_8: + t = quarter + beam16 + note16 + beam8 + note8 + beam8 + note8; + break; + case Gr_8_16_16_16_16: + t = t = quarter + beam8 + note8 + beam16 + note16 + beam16 + note16 + beam16 + note16; + break; + case Gr_16_16_16_16_8: + t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam8 + note8; + break; + case Gr_16_16_8_16_16: + t = quarter + beam16 + note16 + beam8 + note8 + beam8 + note8 + beam16 + note16; + break; + case Gr_16_16_16_16_16_16: + t = quarter + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam16 + note16 + beam16 + note16; + break; + + case Gr_16_8_8_16: + t = quarter + beam16 + note8 + beam8 + note8 + beam8 + note16; + break; + case Gr_16_8_16_8: + t = quarter + beam16 + note8 + beam8 + beam8 + note16 + beam8 + note8; + break; + case Gr_16_16_16_8_16: + t = quarter + beam16 + note16 + beam16 + note16 + beam8 + note8 + beam8 + note16; + break; + case Gr_16_8_16_16_16: + t = quarter + beam16 + note8 + beam8 + note8 + beam16 + note16 + beam16 + note16; + break; + case Gr_1dot: + t = whole + space + dot; + break; + case Gr_2dot: + t = half + space + dot; + break; + case Gr_4dot: + t = quarter + dot; + break; + case Gr_8dot_16: + t = quarter + beam8 + dot + beam8 + note16; + break; + case Gr_16_8dot: + t = quarter + beam16 + note8 + dot; + break; -bool TrtmGroup::isSpecial() const { - return (m_gr != Gr_undefined && m_gr < Gr_4) || m_gr == Gr_1dot || m_gr == Gr_2dot; + case Gr_8dot_8dot: + t = quarter + beam8 + dot + beam8 + note8 + dot; + break; + case Gr_8dot_16_8: + t = quarter + beam8 + dot + beam8 + note16 + beam8 + note8; + break; + case Gr_8dot_8_16: + t = quarter + beam8 + dot + beam8 + note8 + beam8 + note16; + break; + case Gr_16_8dot_8: + t = quarter + beam16 + note8 + beam8 + dot + beam8 + note8; + break; + case Gr_8_8dot_16: + t = quarter + beam8 + note8 + beam8 + dot + beam8 + note16; + break; + case Gr_8dot_16_16_16: + t = quarter + beam8 + dot + beam8 + note8 + beam16 + note16 + beam16 + note16; + break; + case Gr_16_8dot_16_16: + t = quarter + beam16 + note8 + beam8 + dot + beam8 + note8 + beam16 + note16; + break; + case Gr_16_16_8dot_16: + t = quarter + beam16 + note16 + beam8 + note8 + beam8 + dot + beam8 + note16; + break; + case Gr_16_16_16_8dot: + t = quarter + beam16 + note16 + beam16 + note16 + beam8 + note8 + dot; + break; + default: + break; + } + return t; } +TrhythmList TrtmGroup::rhythm() const +{ + TrhythmList rl; + switch (m_gr) { + case Gr_1: + rl << Trhythm(Trhythm::Whole); + break; + case Gr_2: + rl << Trhythm(Trhythm::Half); + break; + case Gr_4: + rl << R4; + break; + case Gr_8_8: + rl << R8 << R8; + break; + case Gr_8_16_16: + rl << R8 << R16 << R16; + break; + case Gr_16_16_16_16: + rl << R16 << R16 << R16 << R16; + break; + case Gr_16_16_8: + rl << R16 << R16 << R8; + break; + case Gr_16_8_16: + rl << R16 << R8 << R16; + break; -bool TrtmGroup::isDuple() const { - return (m_gr > Gr_2 && m_gr <= Gr_16_8_16) || m_gr == Gr_8dot_16 || m_gr == Gr_16_8dot; + case Gr_8_8_8: + rl << R8 << R8 << R8; + break; + case Gr_4_8: + rl << R4 << R8; + break; + case Gr_8_4: + rl << R8 << R4; + break; + case Gr_8_8_16_16: + rl << R8 << R8 << R16 << R16; + break; + case Gr_8_16_16_8: + rl << R8 << R16 << R16 << R8; + break; + case Gr_16_16_8_8: + rl << R16 << R16 << R8 << R8; + break; + case Gr_8_16_16_16_16: + rl << R8 << R16 << R16 << R16 << R16; + break; + case Gr_16_16_16_16_8: + rl << R16 << R16 << R16 << R16 << R8; + break; + case Gr_16_16_8_16_16: + rl << R16 << R16 << R8 << R16 << R16; + break; + case Gr_16_16_16_16_16_16: + rl << R16 << R16 << R16 << R16 << R16 << R16; + break; + + case Gr_16_8_8_16: + rl << R16 << R8 << R8 << R16; + break; + case Gr_16_8_16_8: + rl << R16 << R8 << R16 << R8; + break; + case Gr_16_16_16_8_16: + rl << R16 << R16 << R16 << R8 << R16; + break; + case Gr_16_8_16_16_16: + rl << R16 << R8 << R16 << R16 << R16; + break; + + case Gr_1dot: + rl << Trhythm(Trhythm::Whole, false, true); + break; + case Gr_2dot: + rl << Trhythm(Trhythm::Half, false, true); + break; + case Gr_4dot: + rl << Trhythm(Trhythm::Quarter, false, true); + break; + + case Gr_8dot_16: + rl << R8dot << R16; + break; + case Gr_16_8dot: + rl << R16 << R8dot; + break; + // // Triple metre (duration: 4. -> one quarter and eight) ==== DOTS + case Gr_8dot_8dot: + rl << R8dot << R8dot; + break; + case Gr_8dot_16_8: + rl << R8dot << R16 << R8; + break; + case Gr_8dot_8_16: + rl << R8dot << R8 << R16; + break; + case Gr_16_8dot_8: + rl << R16 << R8dot << R8; + break; + case Gr_8_8dot_16: + rl << R8 << R8dot << R16; + break; + case Gr_8dot_16_16_16: + rl << R8dot << R16 << R16 << R16; + break; + case Gr_16_8dot_16_16: + rl << R16 << R8dot << R16 << R16; + break; + case Gr_16_16_8dot_16: + rl << R16 << R16 << R8dot << R16; + break; + case Gr_16_16_16_8dot: + rl << R16 << R16 << R16 << R8dot; + break; + default: + break; + } + return rl; } +bool TrtmGroup::isSpecial() const +{ + return (m_gr != Gr_undefined && m_gr < Gr_4) || m_gr == Gr_1dot || m_gr == Gr_2dot; +} -bool TrtmGroup::isTriple() const { - return (m_gr >= Gr_8_8_8 && m_gr <= Gr_16_8_16_16_16) || m_gr == Gr_4dot || (m_gr >= Gr_8dot_8dot && m_gr <= Gr_16_16_16_8dot); +bool TrtmGroup::isDuple() const +{ + return (m_gr > Gr_2 && m_gr <= Gr_16_8_16) || m_gr == Gr_8dot_16 || m_gr == Gr_16_8dot; } +bool TrtmGroup::isTriple() const +{ + return (m_gr >= Gr_8_8_8 && m_gr <= Gr_16_8_16_16_16) || m_gr == Gr_4dot || (m_gr >= Gr_8dot_8dot && m_gr <= Gr_16_16_16_8dot); +} -int TrtmGroup::duration() const { - int dur = 0; - TrhythmList rl = rhythm(); - for (int r = 0; r < rl.count(); ++r) - dur += rl[r].duration(); - return dur; +int TrtmGroup::duration() const +{ + int dur = 0; + TrhythmList rl = rhythm(); + for (int r = 0; r < rl.count(); ++r) + dur += rl[r].duration(); + return dur; } diff --git a/src/libs/core/music/trtmgroup.h b/src/libs/core/music/trtmgroup.h index 5454d19232c62025eb544104229f753d737fe4d2..495c8913976f53808aadf0f5530f0bb1d7c610ef 100644 --- a/src/libs/core/music/trtmgroup.h +++ b/src/libs/core/music/trtmgroup.h @@ -19,108 +19,106 @@ #ifndef TRTMGROUP_H #define TRTMGROUP_H - -#include <nootkacoreglobal.h> #include "trhythm.h" #include <QtCore/qobject.h> +#include <nootkacoreglobal.h> - -#define BASIC_GR_COUNT (22) /** Number of enumerators representing basic rhythmic groups (without dots) */ - +#define BASIC_GR_COUNT (22) /** Number of enumerators representing basic rhythmic groups (without dots) */ /** * @class TrtmGroup describes rhythmic group of notes - * It defines rhythmic groups in @p ErtmGroup enumerator + * It defines rhythmic groups in @p ErtmGroup enumerator */ class NOOTKACORE_EXPORT TrtmGroup { - - Q_GADGET + Q_GADGET public: - enum ErtmGroup : quint8 { - Gr_undefined = 0, - // Special - Gr_1, - Gr_2, - // Duple metre (duration: 4 -> one quarter) ==== SIMPLE - Gr_4, - Gr_8_8, - Gr_8_16_16, - Gr_16_16_16_16, - Gr_16_16_8, - Gr_16_8_16, - // Triple metre (duration: 4. -> one quarter and eight) ==== SIMPLE - Gr_8_8_8, - Gr_4_8, - Gr_8_4, - Gr_8_8_16_16, - Gr_8_16_16_8, - Gr_16_16_8_8, - Gr_8_16_16_16_16, - Gr_16_16_16_16_8, - Gr_16_16_8_16_16, - Gr_16_16_16_16_16_16, - // Triple metre (duration: 4. -> one quarter and eight) ==== ADVANCED - Gr_16_8_8_16, - Gr_16_8_16_8, - Gr_16_16_16_8_16, - Gr_16_8_16_16_16, - // Special - Gr_1dot, - Gr_2dot, - // Triple or special - Gr_4dot, - // Duple metre (duration: 4 -> one quarter) ==== DOTS - Gr_8dot_16, - Gr_16_8dot, - // Triple metre (duration: 4. -> one quarter and eight) ==== DOTS - Gr_8dot_8dot, - Gr_8dot_16_8, - Gr_8dot_8_16, - Gr_16_8dot_8, - Gr_8_8dot_16, - Gr_8dot_16_16_16, - Gr_16_8dot_16_16, - Gr_16_16_8dot_16, - Gr_16_16_16_8dot - }; - Q_ENUM(ErtmGroup) - - - TrtmGroup() {} - TrtmGroup(ErtmGroup rGr) : m_gr(rGr) {} - - ErtmGroup group() const { return m_gr; } - void setGroup(ErtmGroup g) { m_gr = g; } - - /** - * Text that represents rhythm of this group in Scorek (Bravura) font - */ - QString text() const; - - /** - * Rhythm of this group in @p Trhythm values - */ - TrhythmList rhythm() const; - - /** - * Special rhythmic group means a group longer than duple (quarter) or triple (quarter and dot) - * There are: whole, half and whole with dot and half with dot. - * @p Gr_1, @p Gr_2, @p Gr_1dot, @p Gr_2dot - */ - bool isSpecial() const; - bool isDuple() const; - bool isTriple() const; - - /** - * Duration of the group. - * Use this method wisely - it is expensive! - */ - int duration() const; + enum ErtmGroup : quint8 { + Gr_undefined = 0, + // Special + Gr_1, + Gr_2, + // Duple metre (duration: 4 -> one quarter) ==== SIMPLE + Gr_4, + Gr_8_8, + Gr_8_16_16, + Gr_16_16_16_16, + Gr_16_16_8, + Gr_16_8_16, + // Triple metre (duration: 4. -> one quarter and eight) ==== SIMPLE + Gr_8_8_8, + Gr_4_8, + Gr_8_4, + Gr_8_8_16_16, + Gr_8_16_16_8, + Gr_16_16_8_8, + Gr_8_16_16_16_16, + Gr_16_16_16_16_8, + Gr_16_16_8_16_16, + Gr_16_16_16_16_16_16, + // Triple metre (duration: 4. -> one quarter and eight) ==== ADVANCED + Gr_16_8_8_16, + Gr_16_8_16_8, + Gr_16_16_16_8_16, + Gr_16_8_16_16_16, + // Special + Gr_1dot, + Gr_2dot, + // Triple or special + Gr_4dot, + // Duple metre (duration: 4 -> one quarter) ==== DOTS + Gr_8dot_16, + Gr_16_8dot, + // Triple metre (duration: 4. -> one quarter and eight) ==== DOTS + Gr_8dot_8dot, + Gr_8dot_16_8, + Gr_8dot_8_16, + Gr_16_8dot_8, + Gr_8_8dot_16, + Gr_8dot_16_16_16, + Gr_16_8dot_16_16, + Gr_16_16_8dot_16, + Gr_16_16_16_8dot + }; + Q_ENUM(ErtmGroup) + + TrtmGroup() { } + TrtmGroup(ErtmGroup rGr) + : m_gr(rGr) + { + } + + ErtmGroup group() const { return m_gr; } + void setGroup(ErtmGroup g) { m_gr = g; } + + /** + * Text that represents rhythm of this group in Scorek (Bravura) font + */ + QString text() const; + + /** + * Rhythm of this group in @p Trhythm values + */ + TrhythmList rhythm() const; + + /** + * Special rhythmic group means a group longer than duple (quarter) or triple (quarter and dot) + * There are: whole, half and whole with dot and half with dot. + * @p Gr_1, @p Gr_2, @p Gr_1dot, @p Gr_2dot + */ + bool isSpecial() const; + bool isDuple() const; + bool isTriple() const; + + /** + * Duration of the group. + * Use this method wisely - it is expensive! + */ + int duration() const; private: - ErtmGroup m_gr = Gr_undefined; + ErtmGroup m_gr = Gr_undefined; }; #endif // TRTMGROUP_H diff --git a/src/libs/core/music/ttechnical.cpp b/src/libs/core/music/ttechnical.cpp index f5e9297dac3994e90e6694dfc525651366b00a24..a2bd14cff8d23e35ee3b85319ddd7cb8c8c4f412 100644 --- a/src/libs/core/music/ttechnical.cpp +++ b/src/libs/core/music/ttechnical.cpp @@ -16,90 +16,88 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "ttechnical.h" #include <QtCore/qdebug.h> - Ttechnical::Ttechnical(quint32 fromData) { - setData(fromData); + setData(fromData); } - /** * Upper staff id default state so it is for 0 value */ -void Ttechnical::setOnUpperStaff(bool onUpper) { - if (onUpper) - m_otherData &= ~ON_UPPER; - else - m_otherData |= ON_UPPER; +void Ttechnical::setOnUpperStaff(bool onUpper) +{ + if (onUpper) + m_otherData &= ~ON_UPPER; + else + m_otherData |= ON_UPPER; } - -void Ttechnical::setBowing(Ttechnical::EbowDirection b) { - m_otherData &= ~BOW_DIRECTION; // reset it first - m_otherData |= b; +void Ttechnical::setBowing(Ttechnical::EbowDirection b) +{ + m_otherData &= ~BOW_DIRECTION; // reset it first + m_otherData |= b; } - -void Ttechnical::setFinger(int fingerNr) { - m_otherData &= ~FINGERING; - if (fingerNr >= -1 && fingerNr < 6) - m_otherData |= (static_cast<quint16>(fingerNr) + 1) << 3; - else - qDebug() << "[TnoteData] wrong finger number to store" << fingerNr << " --> Ignoring."; +void Ttechnical::setFinger(int fingerNr) +{ + m_otherData &= ~FINGERING; + if (fingerNr >= -1 && fingerNr < 6) + m_otherData |= (static_cast<quint16>(fingerNr) + 1) << 3; + else + qDebug() << "[TnoteData] wrong finger number to store" << fingerNr << " --> Ignoring."; } - -quint32 Ttechnical::data() const { - quint32 d = m_otherData; - d <<= 8; - d += m_fingerPos.data(); - return d; +quint32 Ttechnical::data() const +{ + quint32 d = m_otherData; + d <<= 8; + d += m_fingerPos.data(); + return d; } - -void Ttechnical::setData(quint32 d) { - m_fingerPos.setData(static_cast<quint8>(d)); - m_otherData = static_cast<quint16>(d >> 8); +void Ttechnical::setData(quint32 d) +{ + m_fingerPos.setData(static_cast<quint8>(d)); + m_otherData = static_cast<quint16>(d >> 8); } - -void Ttechnical::fromXml(QXmlStreamReader& xml) { - int s = 0, f = 50; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("string")) - s = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("fret")) - f = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("down-bow")) { - xml.skipCurrentElement(); - setBowing(BowDown); - } else if (xml.name() == QLatin1String("up-bow")) { - xml.skipCurrentElement(); - setBowing(BowUp); - } else - xml.skipCurrentElement(); - } - if (s > 0 && s < 7) - m_fingerPos.setPos(s, f == 50 ? 0 : f); // set fret to 0 if its value was not set - else - m_fingerPos.setData(NO_TECHNICALS); // invalid +void Ttechnical::fromXml(QXmlStreamReader &xml) +{ + int s = 0, f = 50; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("string")) + s = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("fret")) + f = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("down-bow")) { + xml.skipCurrentElement(); + setBowing(BowDown); + } else if (xml.name() == QLatin1String("up-bow")) { + xml.skipCurrentElement(); + setBowing(BowUp); + } else + xml.skipCurrentElement(); + } + if (s > 0 && s < 7) + m_fingerPos.setPos(s, f == 50 ? 0 : f); // set fret to 0 if its value was not set + else + m_fingerPos.setData(NO_TECHNICALS); // invalid } - -void Ttechnical::toXml(QXmlStreamWriter& xml, const QString& tag) const { - if (!tag.isEmpty()) - xml.writeStartElement(tag); - if (m_fingerPos.isValid()) { - xml.writeTextElement(QLatin1String("string"), QString("%1").arg(m_fingerPos.str())); - xml.writeTextElement(QLatin1String("fret"), QString("%1").arg(m_fingerPos.fret())); - } - if (bowing()) - xml.writeEmptyElement(bowing() == BowDown ? QLatin1String("down-bow") : QLatin1String("up-bow")); - if (!tag.isEmpty()) - xml.writeEndElement(); // tag +void Ttechnical::toXml(QXmlStreamWriter &xml, const QString &tag) const +{ + if (!tag.isEmpty()) + xml.writeStartElement(tag); + if (m_fingerPos.isValid()) { + xml.writeTextElement(QLatin1String("string"), QString("%1").arg(m_fingerPos.str())); + xml.writeTextElement(QLatin1String("fret"), QString("%1").arg(m_fingerPos.fret())); + } + if (bowing()) + xml.writeEmptyElement(bowing() == BowDown ? QLatin1String("down-bow") : QLatin1String("up-bow")); + if (!tag.isEmpty()) + xml.writeEndElement(); // tag } diff --git a/src/libs/core/music/ttechnical.h b/src/libs/core/music/ttechnical.h index 0b23d5699c87d209d4eff79b514cb50b01d8d404..01c8aab0365b5f783cd0b0fc1de06871a6cd4075 100644 --- a/src/libs/core/music/ttechnical.h +++ b/src/libs/core/music/ttechnical.h @@ -19,19 +19,16 @@ #ifndef TTECHNICAL_H #define TTECHNICAL_H - #include "nootkacoreglobal.h" #include "tfingerpos.h" #include <QtCore/qobject.h> - #define ON_UPPER (1) // first bit #define BOW_DIRECTION (6) // 2nd and 3rd bits #define FINGERING (56) // 4th to 6th bits #define NO_TECHNICALS (255) // 255 means that there is no any technical information - /** * @p Ttechnical extends information about note in the score. * It has guitar position in @p TfingerPos @@ -46,55 +43,58 @@ */ class NOOTKACORE_EXPORT Ttechnical { - - Q_GADGET + Q_GADGET public: - Ttechnical() {} - Ttechnical(quint32 fromData); - - /** - * Returns @p TRUE when all extra note parameters are unset - */ - bool isEmpty() const { return !m_fingerPos.isValid() && m_otherData == 0; } - - /** - * Resets all extra note parameters to null - */ - void reset() { m_fingerPos.setData(NO_TECHNICALS); m_otherData = 0; } - - TfingerPos& fingerPos() { return m_fingerPos; } - void setFingerPos(TfingerPos fp) { m_fingerPos.setData(fp.data()); } - - bool onUpperStaff() const { return !(m_otherData & ON_UPPER); } - void setOnUpperStaff(bool onUpper); - - enum EbowDirection { - BowUndefined = 0, - BowDown = 2, /**< For bandoneon it is bellow opening */ - BowUp = 4 /**< For bandoneon it is bellow closing */ - }; - Q_ENUM(EbowDirection) - - EbowDirection bowing() const { return static_cast<EbowDirection>(m_otherData & BOW_DIRECTION); } - void setBowing(EbowDirection b); - - /** - * Finger number [0 - 5]. - * -1 is returned when undefined - */ - int finger() const { return ((m_otherData & FINGERING) >> 3) - 1; } - void setFinger(int fingerNr); - - quint32 data() const; - void setData(quint32 d); - - void toXml (QXmlStreamWriter& xml, const QString& tag = QLatin1String("technical")) const; - void fromXml(QXmlStreamReader& xml); + Ttechnical() { } + Ttechnical(quint32 fromData); + + /** + * Returns @p TRUE when all extra note parameters are unset + */ + bool isEmpty() const { return !m_fingerPos.isValid() && m_otherData == 0; } + + /** + * Resets all extra note parameters to null + */ + void reset() + { + m_fingerPos.setData(NO_TECHNICALS); + m_otherData = 0; + } + + TfingerPos &fingerPos() { return m_fingerPos; } + void setFingerPos(TfingerPos fp) { m_fingerPos.setData(fp.data()); } + + bool onUpperStaff() const { return !(m_otherData & ON_UPPER); } + void setOnUpperStaff(bool onUpper); + + enum EbowDirection { + BowUndefined = 0, + BowDown = 2, /**< For bandoneon it is bellow opening */ + BowUp = 4 /**< For bandoneon it is bellow closing */ + }; + Q_ENUM(EbowDirection) + + EbowDirection bowing() const { return static_cast<EbowDirection>(m_otherData & BOW_DIRECTION); } + void setBowing(EbowDirection b); + + /** + * Finger number [0 - 5]. + * -1 is returned when undefined + */ + int finger() const { return ((m_otherData & FINGERING) >> 3) - 1; } + void setFinger(int fingerNr); + + quint32 data() const; + void setData(quint32 d); + + void toXml(QXmlStreamWriter &xml, const QString &tag = QLatin1String("technical")) const; + void fromXml(QXmlStreamReader &xml); private: - TfingerPos m_fingerPos; - quint16 m_otherData = 0; + TfingerPos m_fingerPos; + quint16 m_otherData = 0; }; #endif // TTECHNICAL_H diff --git a/src/libs/core/music/ttune.cpp b/src/libs/core/music/ttune.cpp index 32cd22ce58bca5c7b03daf7b30e93968c7a18035..9a43c66ad640675f868073e40644a792c9fcbd27 100644 --- a/src/libs/core/music/ttune.cpp +++ b/src/libs/core/music/ttune.cpp @@ -18,11 +18,10 @@ #include "ttune.h" -#include <QtCore/qvariant.h> #include <QtCore/qdebug.h> +#include <QtCore/qvariant.h> #include <QtGui/qguiapplication.h> - /*static*/ Ttune Ttune::stdTune = Ttune(); @@ -32,133 +31,209 @@ Ttune Ttune::bassTunes[4]; Ttune Ttune::ukuleleGCEA = Ttune(); Ttune Ttune::ukuleleRaised = Ttune(); - -void Ttune::prepareDefinedTunes() { - stdTune = Ttune(QGuiApplication::translate("Ttune", "Standard: E A D G B E "), Tnote(3, 1, 0), Tnote(7, 0, 0), - Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0), Tnote(3, -1, 0), Standard_EADGBE); - - tunes[0] = Ttune(QGuiApplication::translate("Ttune", "Dropped D: D A D G B E "), Tnote(3, 1, 0), Tnote(7, 0, 0), - Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0), Dropped_D_DADGBE); - tunes[1] = Ttune(QGuiApplication::translate("Ttune", "Dummy Lute: D A D F# B E "), Tnote(3, 1, 0), Tnote(7, 0, 0), - Tnote(4, 0, 1), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0), DummyLute_DADFshBE); - tunes[2] = Ttune(QGuiApplication::translate("Ttune", "Open: D A D G A D "), Tnote(2, 1, 0), Tnote(6, 0, 0), - Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0), Open_DADGAD); - tunes[3] = Ttune(QGuiApplication::translate("Ttune", "Kouyanbaba: D A D A D F "), Tnote(4, 1, 0), Tnote(2, 1, 0), - Tnote(6, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0), Kouyanbaba_DADADF); - - bassTunes[0] = Ttune(QGuiApplication::translate("Ttune", "Standard bass: E A D G"), Tnote(5, 0, 0), Tnote(2, 0, 0), - Tnote(6, -1, 0), Tnote(3, -1, 0), Tnote(0, 0, 0), Tnote(0, 0, 0), Bass4_EADG); - bassTunes[1] = Ttune(QGuiApplication::translate("Ttune", "Bass in fifths: C G D A"), Tnote(6, 0, 0), Tnote(2, 0, 0), - Tnote(5, -1, 0), Tnote(1, -1, 0), Tnote(0, 0, 0), Tnote(0, 0, 0), Bass4_5ths_CGDA); - bassTunes[2] = Ttune(QGuiApplication::translate("Ttune", "5-str. bass: B E A D G"), Tnote(5, 0, 0), Tnote(2, 0, 0), - Tnote(6, -1, 0), Tnote(3, -1, 0), Tnote(7, -2, 0), Tnote(0, 0, 0), Bass5_BEADG); - bassTunes[3] = Ttune(QGuiApplication::translate("Ttune", "6-str. bass: B E A D G C"), Tnote(1, 1, 0), Tnote(5, 0, 0), Tnote(2, 0, 0), - Tnote(6, -1, 0), Tnote(3, -1, 0), Tnote(7, -2, 0), Bass6_BEADGC); - - ukuleleGCEA = Ttune(QGuiApplication::translate("Ttune", "Standard C: G C E A"), Tnote(6, 1, 0), Tnote(3, 1, 0), - Tnote(1, 1, 0), Tnote(5, 1, 0), Tnote(0, 0, 0), Tnote(0, 0, 0), Ukulele_GCEA); - ukuleleRaised = Ttune(QGuiApplication::translate("Ttune", "Raised D (Canadian): A D F# B"), Tnote(7, 1, 0), Tnote(4, 1, 1), - Tnote(2, 1, 0), Tnote(6, 1, 0), Tnote(0, 0, 0), Tnote(0, 0, 0), Ukulele_Raised); +void Ttune::prepareDefinedTunes() +{ + stdTune = Ttune(QGuiApplication::translate("Ttune", "Standard: E A D G B E "), + Tnote(3, 1, 0), + Tnote(7, 0, 0), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(3, -1, 0), + Standard_EADGBE); + + tunes[0] = Ttune(QGuiApplication::translate("Ttune", "Dropped D: D A D G B E "), + Tnote(3, 1, 0), + Tnote(7, 0, 0), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(2, -1, 0), + Dropped_D_DADGBE); + tunes[1] = Ttune(QGuiApplication::translate("Ttune", "Dummy Lute: D A D F# B E "), + Tnote(3, 1, 0), + Tnote(7, 0, 0), + Tnote(4, 0, 1), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(2, -1, 0), + DummyLute_DADFshBE); + tunes[2] = Ttune(QGuiApplication::translate("Ttune", "Open: D A D G A D "), + Tnote(2, 1, 0), + Tnote(6, 0, 0), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(2, -1, 0), + Open_DADGAD); + tunes[3] = Ttune(QGuiApplication::translate("Ttune", "Kouyanbaba: D A D A D F "), + Tnote(4, 1, 0), + Tnote(2, 1, 0), + Tnote(6, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(2, -1, 0), + Kouyanbaba_DADADF); + + bassTunes[0] = Ttune(QGuiApplication::translate("Ttune", "Standard bass: E A D G"), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(3, -1, 0), + Tnote(0, 0, 0), + Tnote(0, 0, 0), + Bass4_EADG); + bassTunes[1] = Ttune(QGuiApplication::translate("Ttune", "Bass in fifths: C G D A"), + Tnote(6, 0, 0), + Tnote(2, 0, 0), + Tnote(5, -1, 0), + Tnote(1, -1, 0), + Tnote(0, 0, 0), + Tnote(0, 0, 0), + Bass4_5ths_CGDA); + bassTunes[2] = Ttune(QGuiApplication::translate("Ttune", "5-str. bass: B E A D G"), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(3, -1, 0), + Tnote(7, -2, 0), + Tnote(0, 0, 0), + Bass5_BEADG); + bassTunes[3] = Ttune(QGuiApplication::translate("Ttune", "6-str. bass: B E A D G C"), + Tnote(1, 1, 0), + Tnote(5, 0, 0), + Tnote(2, 0, 0), + Tnote(6, -1, 0), + Tnote(3, -1, 0), + Tnote(7, -2, 0), + Bass6_BEADGC); + + ukuleleGCEA = Ttune(QGuiApplication::translate("Ttune", "Standard C: G C E A"), + Tnote(6, 1, 0), + Tnote(3, 1, 0), + Tnote(1, 1, 0), + Tnote(5, 1, 0), + Tnote(0, 0, 0), + Tnote(0, 0, 0), + Ukulele_GCEA); + ukuleleRaised = Ttune(QGuiApplication::translate("Ttune", "Raised D (Canadian): A D F# B"), + Tnote(7, 1, 0), + Tnote(4, 1, 1), + Tnote(2, 1, 0), + Tnote(6, 1, 0), + Tnote(0, 0, 0), + Tnote(0, 0, 0), + Ukulele_Raised); } - -//################################################################################################## -Ttune::Ttune(const QString& tuneName, const Tnote& S1, const Tnote& S2, const Tnote& S3, - const Tnote& S4, const Tnote& S5, const Tnote& S6, Etunings tunType) : - name(tuneName), - p_tuning(tunType), - m_strNumber(0) +// ################################################################################################## +Ttune::Ttune(const QString &tuneName, const Tnote &S1, const Tnote &S2, const Tnote &S3, const Tnote &S4, const Tnote &S5, const Tnote &S6, Etunings tunType) + : name(tuneName) + , p_tuning(tunType) + , m_strNumber(0) { - stringsArray[0] = S1; - stringsArray[1] = S2; - stringsArray[2] = S3; - stringsArray[3] = S4; - stringsArray[4] = S5; - stringsArray[5] = S6; - - determineStringsNumber(); + stringsArray[0] = S1; + stringsArray[1] = S2; + stringsArray[2] = S3; + stringsArray[3] = S4; + stringsArray[4] = S5; + stringsArray[5] = S6; + + determineStringsNumber(); } - -void Ttune::determineStringsNumber() { - Tnote tmpStrings[6]; - quint8 strCnt = 0; - for (int i = 0; i < 6; i++) { - if (stringsArray[i].note() != 0) { // put all defined notes to tmpStrings - tmpStrings[strCnt] = stringsArray[i]; - strCnt++; +void Ttune::determineStringsNumber() +{ + Tnote tmpStrings[6]; + quint8 strCnt = 0; + for (int i = 0; i < 6; i++) { + if (stringsArray[i].note() != 0) { // put all defined notes to tmpStrings + tmpStrings[strCnt] = stringsArray[i]; + strCnt++; + } } - } - if (strCnt < 6) // if not all were defined fill the rest of tmpStrings with empty notes - for (int i = strCnt; i < 6; i++) - tmpStrings[i] = Tnote(0, 0, 0); - for (int i = 0; i < 6; i++) // copy all to main stringsArray - stringsArray[i] = tmpStrings[i]; - m_strNumber = strCnt; // define number of strings + if (strCnt < 6) // if not all were defined fill the rest of tmpStrings with empty notes + for (int i = strCnt; i < 6; i++) + tmpStrings[i] = Tnote(0, 0, 0); + for (int i = 0; i < 6; i++) // copy all to main stringsArray + stringsArray[i] = tmpStrings[i]; + m_strNumber = strCnt; // define number of strings } - -void Ttune::copy(Ttune& t) { - name = t.name; - for (int i = 0; i < 6; ++i) - stringsArray[i] = t.str(i + 1); - p_tuning = t.type(); - determineStringsNumber(); +void Ttune::copy(Ttune &t) +{ + name = t.name; + for (int i = 0; i < 6; ++i) + stringsArray[i] = t.str(i + 1); + p_tuning = t.type(); + determineStringsNumber(); } - -QString Ttune::definedName(Ttune::Etunings t) { - switch (t) { - case Standard_EADGBE: return stdTune.name; - case Dropped_D_DADGBE: return tunes[0].name; - case DummyLute_DADFshBE: return tunes[1].name; - case Open_DADGAD: return tunes[2].name; - case Kouyanbaba_DADADF: return tunes[3].name; - case Bass4_EADG: return bassTunes[0].name; - case Bass4_5ths_CGDA: return bassTunes[1].name; - case Bass5_BEADG: return bassTunes[2].name; - case Bass6_BEADGC: return bassTunes[3].name; - case Ukulele_GCEA: return ukuleleGCEA.name; - case Ukulele_Raised: return ukuleleRaised.name; - default: return QString(); - } +QString Ttune::definedName(Ttune::Etunings t) +{ + switch (t) { + case Standard_EADGBE: + return stdTune.name; + case Dropped_D_DADGBE: + return tunes[0].name; + case DummyLute_DADFshBE: + return tunes[1].name; + case Open_DADGAD: + return tunes[2].name; + case Kouyanbaba_DADADF: + return tunes[3].name; + case Bass4_EADG: + return bassTunes[0].name; + case Bass4_5ths_CGDA: + return bassTunes[1].name; + case Bass5_BEADG: + return bassTunes[2].name; + case Bass6_BEADGC: + return bassTunes[3].name; + case Ukulele_GCEA: + return ukuleleGCEA.name; + case Ukulele_Raised: + return ukuleleRaised.name; + default: + return QString(); + } } - -void Ttune::riseOctaveUp() { - for (int i = 0; i < 6; ++i) - stringsArray[i].riseOctaveUp(); - p_tuning = findTuning(*this); +void Ttune::riseOctaveUp() +{ + for (int i = 0; i < 6; ++i) + stringsArray[i].riseOctaveUp(); + p_tuning = findTuning(*this); } - -Ttune::Etunings Ttune::findTuning(const Ttune& t) { - if (t.stringNr() == 0) - return Ttune::NoTuning; - else if (t.stringNr() < 3) - return Ttune::Scale; - else { - if (t == Ttune::stdTune) - return Ttune::Standard_EADGBE; +Ttune::Etunings Ttune::findTuning(const Ttune &t) +{ + if (t.stringNr() == 0) + return Ttune::NoTuning; + else if (t.stringNr() < 3) + return Ttune::Scale; else { - for (int i = 0; i < 4; ++i) { - if (t == Ttune::tunes[i]) - return Ttune::tunes[i].type(); - else if (t == Ttune::bassTunes[i]) - return Ttune::bassTunes[i].type(); + if (t == Ttune::stdTune) + return Ttune::Standard_EADGBE; + else { + for (int i = 0; i < 4; ++i) { + if (t == Ttune::tunes[i]) + return Ttune::tunes[i].type(); + else if (t == Ttune::bassTunes[i]) + return Ttune::bassTunes[i].type(); + } } + if (t == Ttune::ukuleleGCEA) + return Ttune::Ukulele_GCEA; + else if (t == Ttune::ukuleleRaised) + return Ukulele_Raised; } - if (t == Ttune::ukuleleGCEA) - return Ttune::Ukulele_GCEA; - else if (t == Ttune::ukuleleRaised) - return Ukulele_Raised; - } - return Ttune::Custom; + return Ttune::Custom; } -//################################################################################################## -//###################### I/O OPERATORS ############################################################# -//################################################################################################## +// ################################################################################################## +// ###################### I/O OPERATORS ############################################################# +// ################################################################################################## /** * Defined tuning have following identifiers: @@ -171,140 +246,141 @@ Ttune::Etunings Ttune::findTuning(const Ttune& t) { * FIXME: We skip Ttune::SCALE type and write it as such as Ttune::Custom * It doesn't harm, so far */ -void Ttune::toXml(QXmlStreamWriter& xml, bool isExam) { - int id = -1; // user defined tuning - if (isExam) { // determine is tuning built-in or user defined - xml.writeStartElement(QStringLiteral("tuning")); - id = static_cast<int>(findTuning(*this)); - if (id < -1) - id = -1; - xml.writeAttribute(QStringLiteral("id"), QVariant(id).toString()); - } else - xml.writeStartElement(QStringLiteral("staff-details")); - - if (id == -1) { // write tuning data - if (isExam) - xml.writeTextElement(QStringLiteral("name"), name); // skip tuning name for musicXML (save it for exam only) - xml.writeTextElement(QStringLiteral("staff-lines"), QVariant(stringNr()).toString()); - for (int i = 0; i < stringNr(); ++i) { - stringsArray[i].toXml(xml, QStringLiteral("staff-tuning"), QStringLiteral("tuning-"), QStringLiteral("line"), QVariant(i + 1).toString()); +void Ttune::toXml(QXmlStreamWriter &xml, bool isExam) +{ + int id = -1; // user defined tuning + if (isExam) { // determine is tuning built-in or user defined + xml.writeStartElement(QStringLiteral("tuning")); + id = static_cast<int>(findTuning(*this)); + if (id < -1) + id = -1; + xml.writeAttribute(QStringLiteral("id"), QVariant(id).toString()); + } else + xml.writeStartElement(QStringLiteral("staff-details")); + + if (id == -1) { // write tuning data + if (isExam) + xml.writeTextElement(QStringLiteral("name"), name); // skip tuning name for musicXML (save it for exam only) + xml.writeTextElement(QStringLiteral("staff-lines"), QVariant(stringNr()).toString()); + for (int i = 0; i < stringNr(); ++i) { + stringsArray[i].toXml(xml, QStringLiteral("staff-tuning"), QStringLiteral("tuning-"), QStringLiteral("line"), QVariant(i + 1).toString()); + } } - } - xml.writeEndElement(); // <tuning> or <staff-details> + xml.writeEndElement(); // <tuning> or <staff-details> } - -bool Ttune::fromXml(QXmlStreamReader& xml, bool isExam) { - bool ok = true; - int id = -1; - if (isExam) { - id = xml.attributes().value(QStringLiteral("id")).toInt(); - if (id < -1 || (id > 4 && id < 100) || (id > 103 && id != 110)) { - qDebug() << "[Ttune] Tuning had wrong 'id'. Standard tuning will be used"; - ok = false; - } - switch (static_cast<Etunings>(id)) { - case Custom: break; - case Dropped_D_DADGBE: - case DummyLute_DADFshBE: - case Open_DADGAD: - case Kouyanbaba_DADADF: - copy(tunes[id]); break; - case Bass4_EADG: - case Bass4_5ths_CGDA: - case Bass5_BEADG: - case Bass6_BEADGC: - copy(bassTunes[id - 100]); break; - case Ukulele_GCEA: - copy(ukuleleGCEA); break; - case Ukulele_Raised: - copy(ukuleleRaised); break; - default: - copy(stdTune); break; +bool Ttune::fromXml(QXmlStreamReader &xml, bool isExam) +{ + bool ok = true; + int id = -1; + if (isExam) { + id = xml.attributes().value(QStringLiteral("id")).toInt(); + if (id < -1 || (id > 4 && id < 100) || (id > 103 && id != 110)) { + qDebug() << "[Ttune] Tuning had wrong 'id'. Standard tuning will be used"; + ok = false; + } + switch (static_cast<Etunings>(id)) { + case Custom: + break; + case Dropped_D_DADGBE: + case DummyLute_DADFshBE: + case Open_DADGAD: + case Kouyanbaba_DADADF: + copy(tunes[id]); + break; + case Bass4_EADG: + case Bass4_5ths_CGDA: + case Bass5_BEADG: + case Bass6_BEADGC: + copy(bassTunes[id - 100]); + break; + case Ukulele_GCEA: + copy(ukuleleGCEA); + break; + case Ukulele_Raised: + copy(ukuleleRaised); + break; + default: + copy(stdTune); + break; + } } - } - if (id == -1) { - int sNrGet = 6, sNrCount = 0; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("name")) // exam only - name = xml.readElementText(); - else if (xml.name() == QLatin1String("staff-lines")) - sNrGet = QVariant(xml.readElementText()).toInt(); - else if (xml.name() == QLatin1String("staff-tuning")) { - sNrCount++; - int line = xml.attributes().value(QStringLiteral("line")).toInt(); - if (line > 0 && line <= sNrGet && line <= 6) { - stringsArray[line - 1].fromXml(xml, QStringLiteral("tuning-")); - if (!stringsArray[line - 1].isValid()) { - qDebug() << "[Ttune] Wrong note describing a string" << line; - ok = false; + if (id == -1) { + int sNrGet = 6, sNrCount = 0; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("name")) // exam only + name = xml.readElementText(); + else if (xml.name() == QLatin1String("staff-lines")) + sNrGet = QVariant(xml.readElementText()).toInt(); + else if (xml.name() == QLatin1String("staff-tuning")) { + sNrCount++; + int line = xml.attributes().value(QStringLiteral("line")).toInt(); + if (line > 0 && line <= sNrGet && line <= 6) { + stringsArray[line - 1].fromXml(xml, QStringLiteral("tuning-")); + if (!stringsArray[line - 1].isValid()) { + qDebug() << "[Ttune] Wrong note describing a string" << line; + ok = false; + } + } else { + qDebug() << "[Ttune] Wrong string (line) number"; + ok = false; } - } else { - qDebug() << "[Ttune] Wrong string (line) number"; - ok = false; - } - } else - xml.skipCurrentElement(); - } - if (sNrGet != sNrCount) { - qDebug() << "[Ttune] String numbers not match" << sNrGet << sNrCount; - ok = false; - } - if (!ok) - copy(stdTune); - else - determineStringsNumber(); - } else - xml.skipCurrentElement(); // we are not reading from <tuning> tag content so it has to be skipped - return ok; + } else + xml.skipCurrentElement(); + } + if (sNrGet != sNrCount) { + qDebug() << "[Ttune] String numbers not match" << sNrGet << sNrCount; + ok = false; + } + if (!ok) + copy(stdTune); + else + determineStringsNumber(); + } else + xml.skipCurrentElement(); // we are not reading from <tuning> tag content so it has to be skipped + return ok; } - -QDataStream &operator<< (QDataStream &out, const Ttune &t) { - out << t.name; - for (int i = 0; i < 6; i++) - out << t.stringsArray[i]; - return out; +QDataStream &operator<<(QDataStream &out, const Ttune &t) +{ + out << t.name; + for (int i = 0; i < 6; i++) + out << t.stringsArray[i]; + return out; } - -QDataStream &operator>> (QDataStream &in, Ttune &t) { - in >> t.name; - for (int i=0; i < 6; i++) - in >> t.stringsArray[i]; - t.determineStringsNumber(); - // determine tuning type - if (t.stringNr() == 0) - t.p_tuning = Ttune::NoTuning; - else if (t.stringNr() < 3) - t.p_tuning = Ttune::Scale; - else { - if (t == Ttune::stdTune) - t.p_tuning = Ttune::Standard_EADGBE; - else if (t == Ttune::ukuleleGCEA) - t.p_tuning = Ttune::Ukulele_GCEA; - else if (t == Ttune::ukuleleRaised) - t.p_tuning = Ttune::Ukulele_Raised; - else { - for (int i = 0; i < 4; ++i) { - if (t == Ttune::tunes[i]) { - t.p_tuning = Ttune::tunes[i].type(); - break; - } else if (t == Ttune::bassTunes[i]) { - t.p_tuning = Ttune::bassTunes[i].type(); - break; +QDataStream &operator>>(QDataStream &in, Ttune &t) +{ + in >> t.name; + for (int i = 0; i < 6; i++) + in >> t.stringsArray[i]; + t.determineStringsNumber(); + // determine tuning type + if (t.stringNr() == 0) + t.p_tuning = Ttune::NoTuning; + else if (t.stringNr() < 3) + t.p_tuning = Ttune::Scale; + else { + if (t == Ttune::stdTune) + t.p_tuning = Ttune::Standard_EADGBE; + else if (t == Ttune::ukuleleGCEA) + t.p_tuning = Ttune::Ukulele_GCEA; + else if (t == Ttune::ukuleleRaised) + t.p_tuning = Ttune::Ukulele_Raised; + else { + for (int i = 0; i < 4; ++i) { + if (t == Ttune::tunes[i]) { + t.p_tuning = Ttune::tunes[i].type(); + break; + } else if (t == Ttune::bassTunes[i]) { + t.p_tuning = Ttune::bassTunes[i].type(); + break; + } } - } - if (t.type() == Ttune::NoTuning) - t.p_tuning = Ttune::Custom; - } - } + if (t.type() == Ttune::NoTuning) + t.p_tuning = Ttune::Custom; + } + } - return in; + return in; } - - - - - - diff --git a/src/libs/core/music/ttune.h b/src/libs/core/music/ttune.h index ceb0e6a30b10b72e486ea51b99d6a60fe31713f2..bb718232c9374d3c12175cfdee9da9e6af1046a3 100644 --- a/src/libs/core/music/ttune.h +++ b/src/libs/core/music/ttune.h @@ -19,172 +19,174 @@ #ifndef TTUNE_H #define TTUNE_H -#include <nootkacoreglobal.h> #include "tnote.h" #include <QtCore/qmetatype.h> -#include <QtCore/qxmlstream.h> #include <QtCore/qobject.h> - +#include <QtCore/qxmlstream.h> +#include <nootkacoreglobal.h> /** * It stores six notes of Tnote objects, which can be set * in constructor and get by overloaded [] operator. -*/ + */ class NOOTKACORE_EXPORT Ttune { + Q_GADGET - Q_GADGET - - Q_PROPERTY(QString name READ tuningName) - Q_PROPERTY(int stringNumber READ stringNr) + Q_PROPERTY(QString name READ tuningName) + Q_PROPERTY(int stringNumber READ stringNr) public: - - enum Etunings : qint8 { - NoTuning = -100, /**< Undefined - initial state. */ - Scale = -2, /**< Instrument scale - only first two strings points lower and higher notes */ - Custom = -1, /**< User defined tuning */ - Standard_EADGBE = 0, - Dropped_D_DADGBE = 1, - DummyLute_DADFshBE = 2, - Open_DADGAD = 3, - Kouyanbaba_DADADF = 4, - - Bass4_EADG = 100, - Bass4_5ths_CGDA = 101, - Bass5_BEADG = 102, - Bass6_BEADGC = 103, - - Ukulele_GCEA = 110, - Ukulele_Raised = 111 - }; - Q_ENUM(Etunings) - - /** - * @p tuneName is the name, @p S(1-6) are notes. - * Empty notes (Tnote()) can control strings number - * when empty - it is moved to the end of a array and stringNr() is less. - * This way only a number of string [from 1 to 6] is supported. - */ - Ttune(const QString& tuneName = QString(), const Tnote& S1 = Tnote() , const Tnote& S2 = Tnote(), - const Tnote& S3 = Tnote(), const Tnote& S4 = Tnote(), const Tnote& S5 = Tnote(), const Tnote& S6 = Tnote(), Etunings tunType = NoTuning); - - - QString name; /**< It is a name of the tune */ - - QString tuningName() const { return name; } - - /** - * Number of strings for current tune/guitar - */ - Q_INVOKABLE quint8 stringNr() const { return m_strNumber; } - - /** - * When tune has less than 3 strings and "scale" as a name it represents a scale of an instrument - * and it is not guitar. - */ - bool isGuitar() const { return m_strNumber > 2; } - - Etunings type() const { return p_tuning; } - - /** - * Substitute of [] operator - returns note of given string. - */ - Q_INVOKABLE Tnote str(quint8 stringNr) const { return stringsArray[stringNr - 1]; } - - /** - * Chromatic number of a given @p stringNr string - */ - Q_INVOKABLE int strChromatic(int stringNr) const { return static_cast<int>(str(stringNr).chromatic()); } - - static Ttune stdTune; /**< standard EADGBE tuning template */ - static Ttune tunes[4]; /**< templates for guitar tuning */ - - /** - * Templates for bass guitar tuning - * As long as bass guitar transposes one octave down - * keep in mind that those string notes are not transposed. - */ - static Ttune bassTunes[4]; - - static Ttune ukuleleGCEA; - static Ttune ukuleleRaised; - - /** - * Copies given tuning to this one. - */ - void copy(Ttune& t); - - static QString definedName(Etunings t); - - /** - * This method simply rises tuning one octave up. - * The ONLY ONE PURPOSE of this method is to convert/fix - * bass dropped down clef of old Nootka to ordinary bass clef. - * It automatically detects tuning type @p Etunings - */ - void riseOctaveUp(); - - /** - * Returns tuning type form given tuning @p t - */ - static Etunings findTuning(const Ttune& t); - - /** - * Makes translations in defined tunes. Has to be invoked once per app launch - */ - static void prepareDefinedTunes(); - - friend QDataStream &operator<< (QDataStream &out, const Ttune &t); - friend QDataStream &operator>> (QDataStream &in, Ttune &t); - - /** - * Method responses for converting tuning to XML structure.. - * When @p isExam is @p TRUE it is wrapped in <tuning id="0"> tag. - * Attribute @p id determining kind of tune. - * Only when tuning is different than all defined tuning suitable XML elements are written. - * When @p isExam is @p FALSE <staff-details> is a main key and all elements are saved. - */ - void toXml(QXmlStreamWriter& xml, bool isExam = true); - bool fromXml(QXmlStreamReader& xml, bool isExam = true); - - /** - * Overloaded operator [] allows to use statement - * @li Ttune @p your_variable[number_of_a_string] - * @p stringNr is real string number (1 to 6) - */ - Tnote &operator[] (quint8 stringNr) { return stringsArray[stringNr - 1]; } - bool operator== (Ttune& T2) const { - return ( stringsArray[0] == T2[1] && stringsArray[1] == T2[2] && stringsArray[2] == T2[3] && - stringsArray[3] == T2[4] && stringsArray[4] == T2[5] && stringsArray[5] == T2[6] ); - } - bool operator!= (Ttune& T2) const { - return ( stringsArray[0] != T2[1] || stringsArray[1] != T2[2] || stringsArray[2] != T2[3] || - stringsArray[3] != T2[4] || stringsArray[4] != T2[5] || stringsArray[5] != T2[6] ); - } + enum Etunings : qint8 { + NoTuning = -100, /**< Undefined - initial state. */ + Scale = -2, /**< Instrument scale - only first two strings points lower and higher notes */ + Custom = -1, /**< User defined tuning */ + Standard_EADGBE = 0, + Dropped_D_DADGBE = 1, + DummyLute_DADFshBE = 2, + Open_DADGAD = 3, + Kouyanbaba_DADADF = 4, + + Bass4_EADG = 100, + Bass4_5ths_CGDA = 101, + Bass5_BEADG = 102, + Bass6_BEADGC = 103, + + Ukulele_GCEA = 110, + Ukulele_Raised = 111 + }; + Q_ENUM(Etunings) + + /** + * @p tuneName is the name, @p S(1-6) are notes. + * Empty notes (Tnote()) can control strings number + * when empty - it is moved to the end of a array and stringNr() is less. + * This way only a number of string [from 1 to 6] is supported. + */ + Ttune(const QString &tuneName = QString(), + const Tnote &S1 = Tnote(), + const Tnote &S2 = Tnote(), + const Tnote &S3 = Tnote(), + const Tnote &S4 = Tnote(), + const Tnote &S5 = Tnote(), + const Tnote &S6 = Tnote(), + Etunings tunType = NoTuning); + + QString name; /**< It is a name of the tune */ + + QString tuningName() const { return name; } + + /** + * Number of strings for current tune/guitar + */ + Q_INVOKABLE quint8 stringNr() const { return m_strNumber; } + + /** + * When tune has less than 3 strings and "scale" as a name it represents a scale of an instrument + * and it is not guitar. + */ + bool isGuitar() const { return m_strNumber > 2; } + + Etunings type() const { return p_tuning; } + + /** + * Substitute of [] operator - returns note of given string. + */ + Q_INVOKABLE Tnote str(quint8 stringNr) const { return stringsArray[stringNr - 1]; } + + /** + * Chromatic number of a given @p stringNr string + */ + Q_INVOKABLE int strChromatic(int stringNr) const { return static_cast<int>(str(stringNr).chromatic()); } + + static Ttune stdTune; /**< standard EADGBE tuning template */ + static Ttune tunes[4]; /**< templates for guitar tuning */ + + /** + * Templates for bass guitar tuning + * As long as bass guitar transposes one octave down + * keep in mind that those string notes are not transposed. + */ + static Ttune bassTunes[4]; + + static Ttune ukuleleGCEA; + static Ttune ukuleleRaised; + + /** + * Copies given tuning to this one. + */ + void copy(Ttune &t); + + static QString definedName(Etunings t); + + /** + * This method simply rises tuning one octave up. + * The ONLY ONE PURPOSE of this method is to convert/fix + * bass dropped down clef of old Nootka to ordinary bass clef. + * It automatically detects tuning type @p Etunings + */ + void riseOctaveUp(); + + /** + * Returns tuning type form given tuning @p t + */ + static Etunings findTuning(const Ttune &t); + + /** + * Makes translations in defined tunes. Has to be invoked once per app launch + */ + static void prepareDefinedTunes(); + + friend QDataStream &operator<<(QDataStream &out, const Ttune &t); + friend QDataStream &operator>>(QDataStream &in, Ttune &t); + + /** + * Method responses for converting tuning to XML structure.. + * When @p isExam is @p TRUE it is wrapped in <tuning id="0"> tag. + * Attribute @p id determining kind of tune. + * Only when tuning is different than all defined tuning suitable XML elements are written. + * When @p isExam is @p FALSE <staff-details> is a main key and all elements are saved. + */ + void toXml(QXmlStreamWriter &xml, bool isExam = true); + bool fromXml(QXmlStreamReader &xml, bool isExam = true); + + /** + * Overloaded operator [] allows to use statement + * @li Ttune @p your_variable[number_of_a_string] + * @p stringNr is real string number (1 to 6) + */ + Tnote &operator[](quint8 stringNr) { return stringsArray[stringNr - 1]; } + bool operator==(Ttune &T2) const + { + return (stringsArray[0] == T2[1] && stringsArray[1] == T2[2] && stringsArray[2] == T2[3] && stringsArray[3] == T2[4] && stringsArray[4] == T2[5] + && stringsArray[5] == T2[6]); + } + bool operator!=(Ttune &T2) const + { + return (stringsArray[0] != T2[1] || stringsArray[1] != T2[2] || stringsArray[2] != T2[3] || stringsArray[3] != T2[4] || stringsArray[4] != T2[5] + || stringsArray[5] != T2[6]); + } protected: - Tnote stringsArray[6]; /**< Array of Tnote that represent six strings */ + Tnote stringsArray[6]; /**< Array of Tnote that represent six strings */ - /** - * This method is called by constructor and operator. - * It calculates number of strings by selecting string with defined notes - * and moving empty ones to the end of stringsArray. - * THIS IS ONLY WAY TO MANAGE STRINGS NUMBER. - */ - void determineStringsNumber(); + /** + * This method is called by constructor and operator. + * It calculates number of strings by selecting string with defined notes + * and moving empty ones to the end of stringsArray. + * THIS IS ONLY WAY TO MANAGE STRINGS NUMBER. + */ + void determineStringsNumber(); - Etunings p_tuning; + Etunings p_tuning; private: - quint8 m_strNumber; - + quint8 m_strNumber; }; Q_DECLARE_METATYPE(Ttune) - -NOOTKACORE_EXPORT QDataStream &operator<< (QDataStream &out, const Ttune &t); -NOOTKACORE_EXPORT QDataStream &operator>> (QDataStream &in, Ttune &t); +NOOTKACORE_EXPORT QDataStream &operator<<(QDataStream &out, const Ttune &t); +NOOTKACORE_EXPORT QDataStream &operator>>(QDataStream &in, Ttune &t); #endif diff --git a/src/libs/core/music/ttuneobject.cpp b/src/libs/core/music/ttuneobject.cpp index fc062bf3d0c43ff556a335679432f22c67d161f3..d8ba02e5830741fe777deff79e94dcf29221ee9b 100644 --- a/src/libs/core/music/ttuneobject.cpp +++ b/src/libs/core/music/ttuneobject.cpp @@ -20,54 +20,54 @@ #include <QtCore/qdebug.h> - -TtuneObject::TtuneObject(QObject* parent) : - QObject(parent), - m_tuning(nullptr) +TtuneObject::TtuneObject(QObject *parent) + : QObject(parent) + , m_tuning(nullptr) { } - -TtuneObject::~TtuneObject() {} - - -void TtuneObject::setTune(Ttune* t) { - m_tuning = t; - emit scordatureChanged(); +TtuneObject::~TtuneObject() +{ } - -QString TtuneObject::name() const { - return m_tuning->name; +void TtuneObject::setTune(Ttune *t) +{ + m_tuning = t; + emit scordatureChanged(); } - -QString TtuneObject::stringName(int realStrNr) const { - if (realStrNr > 0 && realStrNr <= m_tuning->stringNr()) { - auto strName = m_tuning->str(realStrNr).styledName(false); - return strName.replace(0, 1, strName[0].toUpper()); - } else - return QString(); +QString TtuneObject::name() const +{ + return m_tuning->name; } - -bool TtuneObject::otherThanStd(int realStrNr) const { - return realStrNr <= m_tuning->stringNr() && !m_tuning->str(realStrNr).compareNotes(Ttune::stdTune.str(realStrNr)); +QString TtuneObject::stringName(int realStrNr) const +{ + if (realStrNr > 0 && realStrNr <= m_tuning->stringNr()) { + auto strName = m_tuning->str(realStrNr).styledName(false); + return strName.replace(0, 1, strName[0].toUpper()); + } else + return QString(); } - -bool TtuneObject::scordature() const { - if (!m_tuning) - return false; - return m_tuning->type() == Ttune::Custom || (m_tuning->type() > Ttune::Standard_EADGBE && m_tuning->type() < Ttune::Bass4_EADG); +bool TtuneObject::otherThanStd(int realStrNr) const +{ + return realStrNr <= m_tuning->stringNr() && !m_tuning->str(realStrNr).compareNotes(Ttune::stdTune.str(realStrNr)); } +bool TtuneObject::scordature() const +{ + if (!m_tuning) + return false; + return m_tuning->type() == Ttune::Custom || (m_tuning->type() > Ttune::Standard_EADGBE && m_tuning->type() < Ttune::Bass4_EADG); +} -int TtuneObject::changedStrings() const { - int ch = 0; - for (int s = 1; s <= static_cast<int>(m_tuning->stringNr()); ++s) { - if (otherThanStd(s)) - ch++; - } - return ch; +int TtuneObject::changedStrings() const +{ + int ch = 0; + for (int s = 1; s <= static_cast<int>(m_tuning->stringNr()); ++s) { + if (otherThanStd(s)) + ch++; + } + return ch; } diff --git a/src/libs/core/music/ttuneobject.h b/src/libs/core/music/ttuneobject.h index a5c6c3e8ac5cbae44d742baad42c7ef0b2a0de4e..06a66a922b43b364bc3a7da2e6ef9996b3992802 100644 --- a/src/libs/core/music/ttuneobject.h +++ b/src/libs/core/music/ttuneobject.h @@ -19,12 +19,10 @@ #ifndef TTUNEOBJECT_H #define TTUNEOBJECT_H - #include "nootkacoreglobal.h" #include "ttune.h" #include <QtCore/qobject.h> - /** * Wraps @class Ttune with @class QObject * It is used only by @p Tglobals class. @@ -32,60 +30,58 @@ */ class NOOTKACORE_EXPORT TtuneObject : public QObject { - Q_OBJECT + Q_OBJECT - Q_PROPERTY(QString name READ name NOTIFY tuningChanged) - Q_PROPERTY(int type READ typeInt NOTIFY tuningChanged) - Q_PROPERTY(int stringNumber READ stringNumber NOTIFY tuningChanged) - Q_PROPERTY(bool scordature READ scordature NOTIFY scordatureChanged) + Q_PROPERTY(QString name READ name NOTIFY tuningChanged) + Q_PROPERTY(int type READ typeInt NOTIFY tuningChanged) + Q_PROPERTY(int stringNumber READ stringNumber NOTIFY tuningChanged) + Q_PROPERTY(bool scordature READ scordature NOTIFY scordatureChanged) public: + explicit TtuneObject(QObject *parent = nullptr); + ~TtuneObject() override; - explicit TtuneObject(QObject* parent = nullptr); - ~TtuneObject() override; + void setTune(Ttune *t); - void setTune(Ttune* t); + QString name() const; - QString name() const; + int typeInt() const { return static_cast<int>(m_tuning->type()); } - int typeInt() const { return static_cast<int>(m_tuning->type()); } + Q_INVOKABLE Tnote string(int realStrNr) const { return m_tuning->str(realStrNr); } - Q_INVOKABLE Tnote string(int realStrNr) const { return m_tuning->str(realStrNr); } + /** + * Name of a given string (note name without octave) + */ + Q_INVOKABLE QString stringName(int realStrNr) const; - /** - * Name of a given string (note name without octave) - */ - Q_INVOKABLE QString stringName(int realStrNr) const; + /** + * Returns @p TRUE when given string is different than the same string in standard guitar tuning + */ + Q_INVOKABLE bool otherThanStd(int realStrNr) const; - /** - * Returns @p TRUE when given string is different than the same string in standard guitar tuning - */ - Q_INVOKABLE bool otherThanStd(int realStrNr) const; + int stringNumber() { return static_cast<int>(m_tuning->stringNr()); } - int stringNumber() { return static_cast<int>(m_tuning->stringNr()); } + /** + * @p TRUE when tuning is guitar tuning (not bass) and differs from standard EADGBE one + */ + bool scordature() const; - /** - * @p TRUE when tuning is guitar tuning (not bass) and differs from standard EADGBE one - */ - bool scordature() const; + /** + * Number of strings that were changed (tuned) + */ + Q_INVOKABLE int changedStrings() const; - /** - * Number of strings that were changed (tuned) - */ - Q_INVOKABLE int changedStrings() const; - - /** - * Raw copy of the tuning (rather CPU expensive) - */ - Q_INVOKABLE Ttune raw() { return *m_tuning; } + /** + * Raw copy of the tuning (rather CPU expensive) + */ + Q_INVOKABLE Ttune raw() { return *m_tuning; } signals: - void tuningChanged(); - void scordatureChanged(); + void tuningChanged(); + void scordatureChanged(); private: - Ttune *m_tuning; + Ttune *m_tuning; }; - #endif // TTUNEOBJECT_H diff --git a/src/libs/core/nootkacoreglobal.h b/src/libs/core/nootkacoreglobal.h index 9987062070f96eef395f0ecef05779400f36d14e..bc45e933786da4964ea0d74bd088b1fcd1736608 100644 --- a/src/libs/core/nootkacoreglobal.h +++ b/src/libs/core/nootkacoreglobal.h @@ -19,8 +19,7 @@ #include <QtCore/QtGlobal> #if defined(NOOTKACORE_LIBRARY) -# define NOOTKACORE_EXPORT Q_DECL_EXPORT +#define NOOTKACORE_EXPORT Q_DECL_EXPORT #else -# define NOOTKACORE_EXPORT Q_DECL_IMPORT +#define NOOTKACORE_EXPORT Q_DECL_IMPORT #endif - diff --git a/src/libs/core/qtr.h b/src/libs/core/qtr.h old mode 100755 new mode 100644 index 7f3f1402f7c0cc8968527300abb88c610cbc75d6..2fb17a2297ab2efd60e90e7fe30fb08faa97ba48 --- a/src/libs/core/qtr.h +++ b/src/libs/core/qtr.h @@ -1,17 +1,16 @@ #ifndef QTR_H #define QTR_H - #include <QtGui/qguiapplication.h> - /** HACK: * wrapper of standard Qt @p QApplication::translate method * to avoid parsing texts by lupdate. * This way translations of Qt can be used without adding them to *.ts files. */ -inline QString qTR(const char* context, const char* key, const char* disambiguation = 0, int n = -1) { - return QGuiApplication::translate(context, key, disambiguation, n); +inline QString qTR(const char *context, const char *key, const char *disambiguation = 0, int n = -1) +{ + return QGuiApplication::translate(context, key, disambiguation, n); } #endif // QTR_H diff --git a/src/libs/core/score/taddnoteitem.cpp b/src/libs/core/score/taddnoteitem.cpp index bbdea6d2501f2875c6fb95c80f473f9744f3271c..6f8300877afda9e4e0b3802aec67e9ed52db6053 100644 --- a/src/libs/core/score/taddnoteitem.cpp +++ b/src/libs/core/score/taddnoteitem.cpp @@ -17,143 +17,142 @@ ***************************************************************************/ #include "taddnoteitem.h" -#include "tscoreobject.h" #include "tnoteitem.h" +#include "tscoreobject.h" -#include <QtCore/qtimer.h> #include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> - -TaddNoteItem::TaddNoteItem(QQuickItem* parent) : - QQuickItem(parent), - m_yPos(0.0) +TaddNoteItem::TaddNoteItem(QQuickItem *parent) + : QQuickItem(parent) + , m_yPos(0.0) { - m_hideTimer = new QTimer(this); - m_hideTimer->setSingleShot(true); - connect(m_hideTimer, &QTimer::timeout, this, [=]{ - if (m_yPos != 0.0) { - m_yPos = 0.0; - emit yPosChanged(); - } - if (m_active) { - m_active = false; - emit activeChanged(); - } - }); - - setAcceptHoverEvents(true); - setAcceptedMouseButtons(Qt::LeftButton); + m_hideTimer = new QTimer(this); + m_hideTimer->setSingleShot(true); + connect(m_hideTimer, &QTimer::timeout, this, [=] { + if (m_yPos != 0.0) { + m_yPos = 0.0; + emit yPosChanged(); + } + if (m_active) { + m_active = false; + emit activeChanged(); + } + }); + + setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::LeftButton); } - -void TaddNoteItem::setActive(bool a) { - if (a != m_active) { - m_active = a; - emit activeChanged(); - } +void TaddNoteItem::setActive(bool a) +{ + if (a != m_active) { + m_active = a; + emit activeChanged(); + } } - -void TaddNoteItem::setYpos(qreal yp) { - if (static_cast<int>(yp) != static_cast<int>(m_yPos)) { - m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : qFloor(yp); - emit yPosChanged(); - } +void TaddNoteItem::setYpos(qreal yp) +{ + if (static_cast<int>(yp) != static_cast<int>(m_yPos)) { + m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : qFloor(yp); + emit yPosChanged(); + } } - -void TaddNoteItem::setScoreObject(TscoreObject* sc) { - if (sc != m_scoreObject) { - m_scoreObject = sc; - } +void TaddNoteItem::setScoreObject(TscoreObject *sc) +{ + if (sc != m_scoreObject) { + m_scoreObject = sc; + } } - -void TaddNoteItem::hoverEnterEvent(QHoverEvent* event) { - if (event->pos().y() > 1.0) { - m_hideTimer->stop(); - m_active = true; - m_hovered = true; - int yy = qFloor(event->pos().y()); - if (yy != static_cast<int>(m_yPos)) { - m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : qFloor(event->pos().y()); - emit activeChanged(); - emit yPosChanged(); +void TaddNoteItem::hoverEnterEvent(QHoverEvent *event) +{ + if (event->pos().y() > 1.0) { + m_hideTimer->stop(); + m_active = true; + m_hovered = true; + int yy = qFloor(event->pos().y()); + if (yy != static_cast<int>(m_yPos)) { + m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : qFloor(event->pos().y()); + emit activeChanged(); + emit yPosChanged(); + } } - } } - -void TaddNoteItem::hoverLeaveEvent(QHoverEvent*) { - m_hideTimer->start(500); - m_hovered = false; +void TaddNoteItem::hoverLeaveEvent(QHoverEvent *) +{ + m_hideTimer->start(500); + m_hovered = false; } - -void TaddNoteItem::hoverMoveEvent(QHoverEvent* event) { - int yy = qFloor(event->pos().y()); - if (yy > 1 && yy != static_cast<int>(m_yPos)) { - if (!m_active) { // sometimes hover enter seems to be omitted - fix that then - m_active = true; - m_hovered = true; - emit activeChanged(); +void TaddNoteItem::hoverMoveEvent(QHoverEvent *event) +{ + int yy = qFloor(event->pos().y()); + if (yy > 1 && yy != static_cast<int>(m_yPos)) { + if (!m_active) { // sometimes hover enter seems to be omitted - fix that then + m_active = true; + m_hovered = true; + emit activeChanged(); + } + m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : yy; + emit yPosChanged(); } - m_yPos = m_scoreObject->clefType() == Tclef::NoClef ? m_scoreObject->upperLine() + 7.0 : yy; - emit yPosChanged(); - } } - -void TaddNoteItem::mousePressEvent(QMouseEvent* event) { - m_hideTimer->stop(); - setKeepMouseGrab(true); - if (event->pos().y() > 1.0) { - if (!m_hovered) { - m_active = true; - emit activeChanged(); - m_scoreObject->setTouched(true); - m_touchElapsed.restart(); +void TaddNoteItem::mousePressEvent(QMouseEvent *event) +{ + m_hideTimer->stop(); + setKeepMouseGrab(true); + if (event->pos().y() > 1.0) { + if (!m_hovered) { + m_active = true; + emit activeChanged(); + m_scoreObject->setTouched(true); + m_touchElapsed.restart(); + } } - } } - -void TaddNoteItem::mouseReleaseEvent(QMouseEvent* event) { - if (keepMouseGrab()) - setKeepMouseGrab(false); - if (event->pos().y() > 1.0) { - if (m_active) { - if (m_hovered) { // mouse - addNote(); - } else { // touch - if (m_touchElapsed.elapsed() < 190) { - if (m_yPos > 0.0) - addNote(); - } - m_hideTimer->stop(); - m_hideTimer->start(2500); - m_scoreObject->setTouched(false); - } +void TaddNoteItem::mouseReleaseEvent(QMouseEvent *event) +{ + if (keepMouseGrab()) + setKeepMouseGrab(false); + if (event->pos().y() > 1.0) { + if (m_active) { + if (m_hovered) { // mouse + addNote(); + } else { // touch + if (m_touchElapsed.elapsed() < 190) { + if (m_yPos > 0.0) + addNote(); + } + m_hideTimer->stop(); + m_hideTimer->start(2500); + m_scoreObject->setTouched(false); + } + } } - } } - /** * Bound Y coordinate [0 - 46] FIXME: It seems to be Qt bug */ -void TaddNoteItem::mouseMoveEvent(QMouseEvent* event) { - int yy = qFloor(event->pos().y()); - if (m_touchElapsed.elapsed() > 200 && yy > 1 && yy < 49 && static_cast<int>(m_yPos) != yy) { - m_yPos = static_cast<qreal>(yy); - emit yPosChanged(); - } +void TaddNoteItem::mouseMoveEvent(QMouseEvent *event) +{ + int yy = qFloor(event->pos().y()); + if (m_touchElapsed.elapsed() > 200 && yy > 1 && yy < 49 && static_cast<int>(m_yPos) != yy) { + m_yPos = static_cast<qreal>(yy); + emit yPosChanged(); + } } - -void TaddNoteItem::addNote() { - Tnote n = m_scoreObject->posToNote(m_yPos); - n.setOnUpperStaff(!(m_scoreObject->isPianoStaff() && m_yPos > m_scoreObject->upperLine() + 13.0)); - m_scoreObject->addNote(n, true); - m_scoreObject->setSelectedItem(m_scoreObject->lastNote()); +void TaddNoteItem::addNote() +{ + Tnote n = m_scoreObject->posToNote(m_yPos); + n.setOnUpperStaff(!(m_scoreObject->isPianoStaff() && m_yPos > m_scoreObject->upperLine() + 13.0)); + m_scoreObject->addNote(n, true); + m_scoreObject->setSelectedItem(m_scoreObject->lastNote()); } diff --git a/src/libs/core/score/taddnoteitem.h b/src/libs/core/score/taddnoteitem.h index 3f17cf6129d23a79c339392f61c64e1ad7473734..9bb9837045ea6d5ab6908eef1b4caf644fc99c8b 100644 --- a/src/libs/core/score/taddnoteitem.h +++ b/src/libs/core/score/taddnoteitem.h @@ -19,71 +19,67 @@ #ifndef TADDNOTEITEM_H #define TADDNOTEITEM_H - #include "nootkacoreglobal.h" -#include <QtQuick/qquickitem.h> #include <QtCore/qelapsedtimer.h> - +#include <QtQuick/qquickitem.h> class TscoreObject; class QTimer; - /** * This is C++ part of @p NoteAdd component which is not able to handle mouse grabbing, * so in occasion a few logic routines are done here */ class NOOTKACORE_EXPORT TaddNoteItem : public QQuickItem { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(TscoreObject* scoreObject READ scoreObject WRITE setScoreObject) - Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(qreal yPos READ yPos WRITE setYpos NOTIFY yPosChanged) + Q_PROPERTY(TscoreObject *scoreObject READ scoreObject WRITE setScoreObject) + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(qreal yPos READ yPos WRITE setYpos NOTIFY yPosChanged) public: - explicit TaddNoteItem(QQuickItem* parent = nullptr); - ~TaddNoteItem() {} + explicit TaddNoteItem(QQuickItem *parent = nullptr); + ~TaddNoteItem() { } - TscoreObject* scoreObject() { return m_scoreObject; } - void setScoreObject(TscoreObject* sc); + TscoreObject *scoreObject() { return m_scoreObject; } + void setScoreObject(TscoreObject *sc); - bool active() const { return m_active; } - void setActive(bool a); + bool active() const { return m_active; } + void setActive(bool a); - qreal yPos() const { return m_yPos; } + qreal yPos() const { return m_yPos; } - /** - * WARNING: Use this with caution! - * Usually @p yPos property is handled by mouse hover or touch. - * - * It is used also to perform score usage animation. - */ - void setYpos(qreal yp); + /** + * WARNING: Use this with caution! + * Usually @p yPos property is handled by mouse hover or touch. + * + * It is used also to perform score usage animation. + */ + void setYpos(qreal yp); signals: - void activeChanged(); - void yPosChanged(); + void activeChanged(); + void yPosChanged(); protected: - void hoverEnterEvent(QHoverEvent* event) override; - void hoverLeaveEvent(QHoverEvent*) override; - void hoverMoveEvent(QHoverEvent* event) override; + void hoverEnterEvent(QHoverEvent *event) override; + void hoverLeaveEvent(QHoverEvent *) override; + void hoverMoveEvent(QHoverEvent *event) override; - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; - void addNote(); + void addNote(); private: - TscoreObject *m_scoreObject = nullptr; - bool m_active = false; - bool m_hovered = false; - QTimer *m_hideTimer; - qreal m_yPos; - QElapsedTimer m_touchElapsed; + TscoreObject *m_scoreObject = nullptr; + bool m_active = false; + bool m_hovered = false; + QTimer *m_hideTimer; + qreal m_yPos; + QElapsedTimer m_touchElapsed; }; #endif // TADDNOTEITEM_H diff --git a/src/libs/core/score/tbeamobject.cpp b/src/libs/core/score/tbeamobject.cpp index e970669289547c7481acfffcfeb4fde367126e36..589600bab79e2f20337dc4aa07ab89aa1b59d707 100644 --- a/src/libs/core/score/tbeamobject.cpp +++ b/src/libs/core/score/tbeamobject.cpp @@ -17,102 +17,102 @@ ***************************************************************************/ #include "tbeamobject.h" -#include "tscoreobject.h" -#include "tstaffitem.h" #include "tmeasureobject.h" #include "tnoteitem.h" #include "tnotepair.h" +#include "tscoreobject.h" +#include "tstaffitem.h" #include <music/tnote.h> -#include <qmath.h> #include <QtGui/qguiapplication.h> -#include <QtGui/qpalette.h> #include <QtGui/qpainter.h> +#include <QtGui/qpalette.h> +#include <qmath.h> #include <QtCore/qdebug.h> - /** * Simple structure to describe second beam line (sixteenth) * which can be chunked when there are rests or eights in the group */ -class T16beam { +class T16beam +{ public: - T16beam(int firstStemNr = 0) : startStem(firstStemNr) {} - int startStem = -1; /**< Undefined by default (-1) */ - int endStem = -1; /**< When remains undefined (-1) - beam is partial */ + T16beam(int firstStemNr = 0) + : startStem(firstStemNr) + { + } + int startStem = -1; /**< Undefined by default (-1) */ + int endStem = -1; /**< When remains undefined (-1) - beam is partial */ - /** @p TRUE when beam is not connected to another stem */ - bool isHalf() { return endStem == -1; } + /** @p TRUE when beam is not connected to another stem */ + bool isHalf() { return endStem == -1; } }; - #define MIN_STEM_HEIGHT (4.0) // minimal stem height (distance of note head to staff boundary) #define BEAM_THICK (0.8) // thickness of a beam #define HALF_THICK (0.4) // half of beam thickness - - -TbeamObject::TbeamObject(TnotePair* sn, TmeasureObject* m) : - QQuickPaintedItem(m->staff()), - m_measure(m) +TbeamObject::TbeamObject(TnotePair *sn, TmeasureObject *m) + : QQuickPaintedItem(m->staff()) + , m_measure(m) { - setAcceptHoverEvents(true); - setRenderTarget(QQuickPaintedItem::FramebufferObject); - setAntialiasing(true); - addNote(sn); - setParent(m_measure->score()); - connect(qApp, &QGuiApplication::paletteChanged, this, [=]{ update(); }); - connect(this, &QQuickPaintedItem::visibleChanged, this, [=]{ - if (isVisible() && count() > 1) - drawBeam(); - }); + setAcceptHoverEvents(true); + setRenderTarget(QQuickPaintedItem::FramebufferObject); + setAntialiasing(true); + addNote(sn); + setParent(m_measure->score()); + connect(qApp, &QGuiApplication::paletteChanged, this, [=] { + update(); + }); + connect(this, &QQuickPaintedItem::visibleChanged, this, [=] { + if (isVisible() && count() > 1) + drawBeam(); + }); } - TbeamObject::~TbeamObject() { -// qDebug() << " [BEAM] deleted of id" << (m_notes.isEmpty() ? -1 : first()->index()); - for (TnotePair* np : qAsConst(m_notes)) { - np->addChange(TnotePair::e_beamChanged); - np->setBeam(nullptr); -// resetBeam(np); - } + // qDebug() << " [BEAM] deleted of id" << (m_notes.isEmpty() ? -1 : first()->index()); + for (TnotePair *np : qAsConst(m_notes)) { + np->addChange(TnotePair::e_beamChanged); + np->setBeam(nullptr); + // resetBeam(np); + } } - -void TbeamObject::addNote(TnotePair* np) { - if (np == nullptr) - return; - - if (np->beam() == nullptr) - np->setBeam(this); - else - qDebug() << " [BEAM] note" << np->index() << "has already a beam"; - - if (m_notes.count() > 1) - m_notes.last()->note()->rtm.setBeam(Trhythm::e_beamCont); // no need to be updated - if (m_notes.isEmpty()) - np->note()->rtm.setBeam(Trhythm::e_beamStart); - else - np->note()->rtm.setBeam(Trhythm::e_beamEnd); - if (np->item()) // mark it changed only when object exists, otherwise its note will be updated during creation - np->addChange(TnotePair::e_beamChanged); - m_notes << np; - - if (np->note()->rhythm() == Trhythm::Sixteenth) { - Tnote* beforeLastNote = m_notes.count() > 1 ? m_notes[m_notes.count() - 2]->note() : nullptr; - if (m_16beams.isEmpty() || (beforeLastNote && beforeLastNote->rhythm() != Trhythm::Sixteenth)) { - // is first in beam or previous note was not a sixteenth - m_16beams << T16beam(m_notes.count() - 1); // then create new beam segment - } else if (!m_16beams.isEmpty() && (beforeLastNote && beforeLastNote->rhythm() == Trhythm::Sixteenth)) { - // there is 16 beam and previous note and this notes are 16th - m_16beams.last().endStem = m_notes.count() - 1; // then set end stem for it +void TbeamObject::addNote(TnotePair *np) +{ + if (np == nullptr) + return; + + if (np->beam() == nullptr) + np->setBeam(this); + else + qDebug() << " [BEAM] note" << np->index() << "has already a beam"; + + if (m_notes.count() > 1) + m_notes.last()->note()->rtm.setBeam(Trhythm::e_beamCont); // no need to be updated + if (m_notes.isEmpty()) + np->note()->rtm.setBeam(Trhythm::e_beamStart); + else + np->note()->rtm.setBeam(Trhythm::e_beamEnd); + if (np->item()) // mark it changed only when object exists, otherwise its note will be updated during creation + np->addChange(TnotePair::e_beamChanged); + m_notes << np; + + if (np->note()->rhythm() == Trhythm::Sixteenth) { + Tnote *beforeLastNote = m_notes.count() > 1 ? m_notes[m_notes.count() - 2]->note() : nullptr; + if (m_16beams.isEmpty() || (beforeLastNote && beforeLastNote->rhythm() != Trhythm::Sixteenth)) { + // is first in beam or previous note was not a sixteenth + m_16beams << T16beam(m_notes.count() - 1); // then create new beam segment + } else if (!m_16beams.isEmpty() && (beforeLastNote && beforeLastNote->rhythm() == Trhythm::Sixteenth)) { + // there is 16 beam and previous note and this notes are 16th + m_16beams.last().endStem = m_notes.count() - 1; // then set end stem for it + } } - } } - /** * With @p stemDirStrength (strength of stem direction) we trying to determine preferred common direction of stems in the group. * More far from middle of the staff (18) note is placed - stronger its direction has to be preserved @@ -122,186 +122,189 @@ void TbeamObject::addNote(TnotePair* np) { * if not stems go down. * @p stemTop it top stem position of all notes. It is set to middle of the staff if necessary (to look well) */ -void TbeamObject::prepareBeam() { - int stemDirStrength = 0; - bool stemsUpPossible = true; - qreal hiNote = 99.0, loNote = 0.0; - for (TnotePair* np : qAsConst(m_notes)) { - stemDirStrength += np->item()->notePosY() - (m_measure->staff()->upperLine() - + (m_measure->score()->isPianoStaff() && np->item()->notePosY() > m_measure->staff()->upperLine() + 13.0 ? 26.0 : 4.0)); - if (np->item()->notePosY() < MIN_STEM_HEIGHT) - stemsUpPossible = false; - hiNote = qMin(hiNote, np->item()->notePosY()); - loNote = qMax(loNote, np->item()->notePosY()); - } - bool allStemsDown = !stemsUpPossible; - qreal minStemHeight = MIN_STEM_HEIGHT + (m_16beams.empty() ? 0.0 : 1.0); - if (stemDirStrength < 0) - allStemsDown = true; // stems down are always possible - qreal stemTop = allStemsDown ? loNote + minStemHeight : hiNote - minStemHeight; - if (m_measure->score()->isPianoStaff() && !first()->note()->onUpperStaff()) { // when note lays on the lower staff - qreal lowerMidLine = m_measure->staff()->upperLine() + 26.0; - if ((allStemsDown && stemTop < lowerMidLine) || (!allStemsDown && stemTop > lowerMidLine)) - stemTop = lowerMidLine; // keep beam on the lower staff middle line - } else { - qreal upperMidLine = m_measure->staff()->upperLine() + 4.0; - if ((allStemsDown && stemTop < upperMidLine) || (!allStemsDown && stemTop > upperMidLine)) - stemTop = upperMidLine; // keep beam on staff middle line - } - for (TnotePair* np : qAsConst(m_notes)) { - np->note()->rtm.setStemDown(allStemsDown); - np->addChange(TnotePair::e_stemDirChanged); - np->item()->setStemHeight(qAbs(np->item()->notePosY() - stemTop)); - np->approve(); - } - update(); +void TbeamObject::prepareBeam() +{ + int stemDirStrength = 0; + bool stemsUpPossible = true; + qreal hiNote = 99.0, loNote = 0.0; + for (TnotePair *np : qAsConst(m_notes)) { + stemDirStrength += np->item()->notePosY() + - (m_measure->staff()->upperLine() + + (m_measure->score()->isPianoStaff() && np->item()->notePosY() > m_measure->staff()->upperLine() + 13.0 ? 26.0 : 4.0)); + if (np->item()->notePosY() < MIN_STEM_HEIGHT) + stemsUpPossible = false; + hiNote = qMin(hiNote, np->item()->notePosY()); + loNote = qMax(loNote, np->item()->notePosY()); + } + bool allStemsDown = !stemsUpPossible; + qreal minStemHeight = MIN_STEM_HEIGHT + (m_16beams.empty() ? 0.0 : 1.0); + if (stemDirStrength < 0) + allStemsDown = true; // stems down are always possible + qreal stemTop = allStemsDown ? loNote + minStemHeight : hiNote - minStemHeight; + if (m_measure->score()->isPianoStaff() && !first()->note()->onUpperStaff()) { // when note lays on the lower staff + qreal lowerMidLine = m_measure->staff()->upperLine() + 26.0; + if ((allStemsDown && stemTop < lowerMidLine) || (!allStemsDown && stemTop > lowerMidLine)) + stemTop = lowerMidLine; // keep beam on the lower staff middle line + } else { + qreal upperMidLine = m_measure->staff()->upperLine() + 4.0; + if ((allStemsDown && stemTop < upperMidLine) || (!allStemsDown && stemTop > upperMidLine)) + stemTop = upperMidLine; // keep beam on staff middle line + } + for (TnotePair *np : qAsConst(m_notes)) { + np->note()->rtm.setStemDown(allStemsDown); + np->addChange(TnotePair::e_stemDirChanged); + np->item()->setStemHeight(qAbs(np->item()->notePosY() - stemTop)); + np->approve(); + } + update(); } - /** * Polygons are painted, single for 8ths and possible a few for 16ths. * Painter canvas orientation depends on stems direction (up or down), * for stems-up, top beam is 8ths and bottom are 16ths in contrary to stems-down */ -void TbeamObject::paint(QPainter* painter) { - if (count() > 1) { - qreal s = first()->note()->rtm.stemDown() ? -1.0 : 1.0; // scale - qreal t = first()->note()->rtm.stemDown() ? height() : 0.0; // translation - painter->setPen(Qt::NoPen); - painter->setBrush(qApp->palette().text().color()); - QPolygonF topBeam; - qreal endX = last()->item()->stemTop().x() - x() + 0.3; - topBeam << QPointF(0.0, t) << QPointF(0.0, t + s * BEAM_THICK) << QPointF(endX, t + s * BEAM_THICK) << QPointF(endX, t) << QPointF(0.0, t); - painter->drawPolygon(topBeam); - for (int b = 0; b < m_16beams.count(); ++b) { - T16beam& b16 = m_16beams[b]; - qreal startX = m_notes[b16.startStem]->item()->stemTop().x() - x(); - // 16th beam of fist stem is right-sided (2.0) others are left-sided (-2.0) - endX = (b16.isHalf() ? startX + BEAM_THICK * (b16.startStem == 0 ? 2.0 : -2.0) : m_notes[b16.endStem]->item()->stemTop().x() - x()) + 0.3; - QPolygonF polyB; - polyB << QPointF(startX, t + s * 1.5 * BEAM_THICK) << QPointF(startX, t + s * 2.5 * BEAM_THICK) << QPointF(endX, t + s * 2.5 * BEAM_THICK) - << QPointF(endX, t + s * 1.5 * BEAM_THICK) << QPointF(startX, t + s * 1.5 * BEAM_THICK); - painter->drawPolygon(polyB); +void TbeamObject::paint(QPainter *painter) +{ + if (count() > 1) { + qreal s = first()->note()->rtm.stemDown() ? -1.0 : 1.0; // scale + qreal t = first()->note()->rtm.stemDown() ? height() : 0.0; // translation + painter->setPen(Qt::NoPen); + painter->setBrush(qApp->palette().text().color()); + QPolygonF topBeam; + qreal endX = last()->item()->stemTop().x() - x() + 0.3; + topBeam << QPointF(0.0, t) << QPointF(0.0, t + s * BEAM_THICK) << QPointF(endX, t + s * BEAM_THICK) << QPointF(endX, t) << QPointF(0.0, t); + painter->drawPolygon(topBeam); + for (int b = 0; b < m_16beams.count(); ++b) { + T16beam &b16 = m_16beams[b]; + qreal startX = m_notes[b16.startStem]->item()->stemTop().x() - x(); + // 16th beam of fist stem is right-sided (2.0) others are left-sided (-2.0) + endX = (b16.isHalf() ? startX + BEAM_THICK * (b16.startStem == 0 ? 2.0 : -2.0) : m_notes[b16.endStem]->item()->stemTop().x() - x()) + 0.3; + QPolygonF polyB; + polyB << QPointF(startX, t + s * 1.5 * BEAM_THICK) << QPointF(startX, t + s * 2.5 * BEAM_THICK) << QPointF(endX, t + s * 2.5 * BEAM_THICK) + << QPointF(endX, t + s * 1.5 * BEAM_THICK) << QPointF(startX, t + s * 1.5 * BEAM_THICK); + painter->drawPolygon(polyB); + } } - } } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# /** * According to first and last notes in the beam, this method sets item geometry. * @p setTextureSize() is called to make texture big enough and avoid pixelization */ -void TbeamObject::drawBeam() { - if (m_notes.isEmpty()) - return; - - auto p1 = first()->item()->stemTop(); - auto p2 = last()->item()->stemTop(); - setWidth(qAbs(p2.x() - p1.x()) + 1.0); - setHeight(qAbs(p1.y() - p2.y()) + BEAM_THICK * 2.5); - setX(p1.x()); - setY(qMin(p1.y(), p2.y()) - (first()->note()->rtm.stemDown() ? 2.0 * BEAM_THICK : HALF_THICK)); - setTextureSize(QSize(qCeil(width() * m_measure->staff()->scale()), qCeil(height() * m_measure->staff()->scale()))); - /** - * @p update() should be called here to invoke @p paint() - * but it is invoked by Qt itself whenever width, height, x or y change - */ +void TbeamObject::drawBeam() +{ + if (m_notes.isEmpty()) + return; + + auto p1 = first()->item()->stemTop(); + auto p2 = last()->item()->stemTop(); + setWidth(qAbs(p2.x() - p1.x()) + 1.0); + setHeight(qAbs(p1.y() - p2.y()) + BEAM_THICK * 2.5); + setX(p1.x()); + setY(qMin(p1.y(), p2.y()) - (first()->note()->rtm.stemDown() ? 2.0 * BEAM_THICK : HALF_THICK)); + setTextureSize(QSize(qCeil(width() * m_measure->staff()->scale()), qCeil(height() * m_measure->staff()->scale()))); + /** + * @p update() should be called here to invoke @p paint() + * but it is invoked by Qt itself whenever width, height, x or y change + */ } - -void TbeamObject::changeStaff(TstaffItem* st) { - setParentItem(st); +void TbeamObject::changeStaff(TstaffItem *st) +{ + setParentItem(st); } - -bool TbeamObject::removeNote(TnotePair* np) { - bool deleteBeam = false; - int noteId = m_notes.indexOf(np); - if (noteId == -1) { // TODO remove this if does not occur - qDebug() << " [BEAM] of note id" << first()->index() << "has no note to remove"; - return false; - } - if (noteId > 1) { // there are at least two notes at the beam beginning - if (count() - noteId > 2) { // split beam - resetBeam(m_notes.takeAt(noteId)); - int tempCount = count(); - TbeamObject *otherBeam = nullptr; - for (int n = noteId; n < tempCount; ++n) { - auto noteForOtherBeam = m_notes.takeAt(noteId); - resetBeam(noteForOtherBeam); - if (otherBeam) - otherBeam->addNote(noteForOtherBeam); - else - otherBeam = m_measure->score()->getBeam(noteForOtherBeam, m_measure); //new TbeamObject(noteForOtherBeam, m_measure); - } - otherBeam->prepareBeam(); - otherBeam->drawBeam(); - } else { // remove rest of notes - int tempCount = count(); - for (int n = noteId; n < tempCount; ++n) - resetBeam(m_notes.takeLast()); - } - } else { - if (count() - noteId > 2) { - for (int n = 0; n <= noteId; ++n) - resetBeam(m_notes.takeFirst()); - } else - deleteBeam = true; - } - if (!deleteBeam && !m_16beams.isEmpty()) { // renew 16th beam(s) state - m_16beams.clear(); - for (int n = 0; n < count(); ++n) { - auto nt = m_notes[n]; - if (nt->note()->rhythm() == Trhythm::Sixteenth) { - if (m_16beams.isEmpty()) - m_16beams << T16beam(n); - else { - T16beam& last16Beam = m_16beams.last(); - if (last16Beam.isHalf()) { - if (last16Beam.startStem == n - 1) - last16Beam.endStem = n; - else - m_16beams << T16beam(n); - } else { - if (last16Beam.endStem == n - 1) - last16Beam.endStem = n; +bool TbeamObject::removeNote(TnotePair *np) +{ + bool deleteBeam = false; + int noteId = m_notes.indexOf(np); + if (noteId == -1) { // TODO remove this if does not occur + qDebug() << " [BEAM] of note id" << first()->index() << "has no note to remove"; + return false; + } + if (noteId > 1) { // there are at least two notes at the beam beginning + if (count() - noteId > 2) { // split beam + resetBeam(m_notes.takeAt(noteId)); + int tempCount = count(); + TbeamObject *otherBeam = nullptr; + for (int n = noteId; n < tempCount; ++n) { + auto noteForOtherBeam = m_notes.takeAt(noteId); + resetBeam(noteForOtherBeam); + if (otherBeam) + otherBeam->addNote(noteForOtherBeam); else - m_16beams << T16beam(n); + otherBeam = m_measure->score()->getBeam(noteForOtherBeam, m_measure); // new TbeamObject(noteForOtherBeam, m_measure); } + otherBeam->prepareBeam(); + otherBeam->drawBeam(); + } else { // remove rest of notes + int tempCount = count(); + for (int n = noteId; n < tempCount; ++n) + resetBeam(m_notes.takeLast()); } - } + } else { + if (count() - noteId > 2) { + for (int n = 0; n <= noteId; ++n) + resetBeam(m_notes.takeFirst()); + } else + deleteBeam = true; } - } - return deleteBeam; + if (!deleteBeam && !m_16beams.isEmpty()) { // renew 16th beam(s) state + m_16beams.clear(); + for (int n = 0; n < count(); ++n) { + auto nt = m_notes[n]; + if (nt->note()->rhythm() == Trhythm::Sixteenth) { + if (m_16beams.isEmpty()) + m_16beams << T16beam(n); + else { + T16beam &last16Beam = m_16beams.last(); + if (last16Beam.isHalf()) { + if (last16Beam.startStem == n - 1) + last16Beam.endStem = n; + else + m_16beams << T16beam(n); + } else { + if (last16Beam.endStem == n - 1) + last16Beam.endStem = n; + else + m_16beams << T16beam(n); + } + } + } + } + } + return deleteBeam; } - -void TbeamObject::setMeasure(TmeasureObject* m) { - if (m != m_measure) { - m_measure = m; - if (m_measure) - changeStaff(m_measure->staff()); - } +void TbeamObject::setMeasure(TmeasureObject *m) +{ + if (m != m_measure) { + m_measure = m; + if (m_measure) + changeStaff(m_measure->staff()); + } } - -void TbeamObject::deleteBeam() { - m_measure->score()->storeBeam(this); - for (TnotePair* np : qAsConst(m_notes)) { - resetBeam(np); - } - m_16beams.clear(); - m_notes.clear(); - changeStaff(nullptr); - m_measure = nullptr; +void TbeamObject::deleteBeam() +{ + m_measure->score()->storeBeam(this); + for (TnotePair *np : qAsConst(m_notes)) { + resetBeam(np); + } + m_16beams.clear(); + m_notes.clear(); + changeStaff(nullptr); + m_measure = nullptr; } - -void TbeamObject::resetBeam(TnotePair* noteToRemove) { - noteToRemove->note()->rtm.setBeam(Trhythm::e_noBeam); // restore beams - noteToRemove->addChange(TnotePair::e_beamChanged); // and inform TnotePair about changes (it has to invoke approve to fix them) - noteToRemove->setBeam(nullptr); +void TbeamObject::resetBeam(TnotePair *noteToRemove) +{ + noteToRemove->note()->rtm.setBeam(Trhythm::e_noBeam); // restore beams + noteToRemove->addChange(TnotePair::e_beamChanged); // and inform TnotePair about changes (it has to invoke approve to fix them) + noteToRemove->setBeam(nullptr); } diff --git a/src/libs/core/score/tbeamobject.h b/src/libs/core/score/tbeamobject.h index 1dcdceed65d51964871fb12d25d3e9e5cae2a19c..1536c2506766fa6eacf22076fa68468d25d4209f 100644 --- a/src/libs/core/score/tbeamobject.h +++ b/src/libs/core/score/tbeamobject.h @@ -19,10 +19,8 @@ #ifndef TBEAMOBJECT_H #define TBEAMOBJECT_H - -#include <nootkacoreglobal.h> #include <QtQuick/qquickpainteditem.h> - +#include <nootkacoreglobal.h> class TstaffItem; class TmeasureObject; @@ -30,7 +28,6 @@ class TnoteItem; class TnotePair; class T16beam; - /** * This class manages displaying beams of rhythmic group. * It paints either eight beam line and 16th beam line(s) @@ -43,71 +40,69 @@ class T16beam; */ class NOOTKACORE_EXPORT TbeamObject : public QQuickPaintedItem { + Q_OBJECT - Q_OBJECT - - friend class TscoreObject; - friend class TstaffItem; - friend class TmeasureObject; - friend class TnoteItem; - friend class TnotePair; + friend class TscoreObject; + friend class TstaffItem; + friend class TmeasureObject; + friend class TnoteItem; + friend class TnotePair; public: - explicit TbeamObject(TnotePair* sn, TmeasureObject* m); - ~TbeamObject() override; - - /** - * Adds @p TnotePair to beam group - * and according to adding order sets appropriate beam flag. - * It changes stem direction of note(s) when necessary. - * It does not perform painting yet - */ - void addNote(TnotePair* np); + explicit TbeamObject(TnotePair *sn, TmeasureObject *m); + ~TbeamObject() override; - /** - * Returns note @p id in beam group - */ - TnotePair* note(int id) { return m_notes[id]; } + /** + * Adds @p TnotePair to beam group + * and according to adding order sets appropriate beam flag. + * It changes stem direction of note(s) when necessary. + * It does not perform painting yet + */ + void addNote(TnotePair *np); - TnotePair* first() { return m_notes.first(); } - TnotePair* last() { return m_notes.last(); } + /** + * Returns note @p id in beam group + */ + TnotePair *note(int id) { return m_notes[id]; } - /** - * Number of notes in the beam group - */ - int count() { return m_notes.count(); } + TnotePair *first() { return m_notes.first(); } + TnotePair *last() { return m_notes.last(); } + /** + * Number of notes in the beam group + */ + int count() { return m_notes.count(); } - void paint(QPainter* painter) override; + void paint(QPainter *painter) override; protected: - void prepareBeam(); - void drawBeam(); + void prepareBeam(); + void drawBeam(); - /** - * Because beams are parented with staff it is important - * to change their staff when measure is shifted between staves - */ - void changeStaff(TstaffItem* st); + /** + * Because beams are parented with staff it is important + * to change their staff when measure is shifted between staves + */ + void changeStaff(TstaffItem *st); - /** - * Removes given note from the beam, - * if necessary it creates a new beam in case this beam was split by rest. - * Returns @p TRUE if this beam becomes broken (not enough notes) and has to be deleted - */ - bool removeNote(TnotePair* np); + /** + * Removes given note from the beam, + * if necessary it creates a new beam in case this beam was split by rest. + * Returns @p TRUE if this beam becomes broken (not enough notes) and has to be deleted + */ + bool removeNote(TnotePair *np); - void setMeasure(TmeasureObject* m); + void setMeasure(TmeasureObject *m); - void deleteBeam(); + void deleteBeam(); private: - void resetBeam(TnotePair* noteToRemove); + void resetBeam(TnotePair *noteToRemove); private: - TmeasureObject *m_measure; - QList<TnotePair*> m_notes; - QList<T16beam> m_16beams; /**< list of lines of sixteenths */ + TmeasureObject *m_measure; + QList<TnotePair *> m_notes; + QList<T16beam> m_16beams; /**< list of lines of sixteenths */ }; #endif // TBEAMOBJECT_H diff --git a/src/libs/core/score/tdummychord.cpp b/src/libs/core/score/tdummychord.cpp index 1718233c598565a23ffcc78e09bae2be6f871364..3c79efc25389836157831c251515f0943ebaa4c1 100644 --- a/src/libs/core/score/tdummychord.cpp +++ b/src/libs/core/score/tdummychord.cpp @@ -17,119 +17,121 @@ ***************************************************************************/ #include "tdummychord.h" +#include "music/timportscore.h" +#include "music/tmelody.h" #include "score/tnoteitem.h" #include "score/tscoreobject.h" -#include "music/tmelody.h" -#include "music/timportscore.h" #include <QtCore/qdebug.h> - -TdummyChord::TdummyChord(QQuickItem *parent) : - QQuickItem(parent) +TdummyChord::TdummyChord(QQuickItem *parent) + : QQuickItem(parent) { } +TdummyChord::~TdummyChord() +{ +} -TdummyChord::~TdummyChord() {} - - -int TdummyChord::chordModel() const { - return m_alaChord ? m_alaChord->notes()->length() : 0; +int TdummyChord::chordModel() const +{ + return m_alaChord ? m_alaChord->notes()->length() : 0; } +void TdummyChord::setParentItem(QQuickItem *pi) +{ + m_parentNote = qobject_cast<TnoteItem *>(pi); + QQuickItem::setParentItem(pi); + if (m_parentNote) { + findHiLoPos(); + connect(m_parentNote->parent(), &QObject::destroyed, this, [=] { + m_parentNote = nullptr; + }); + emit chordChanged(); + } +} -void TdummyChord::setParentItem(QQuickItem* pi) { - m_parentNote = qobject_cast<TnoteItem*>(pi); - QQuickItem::setParentItem(pi); - if (m_parentNote) { +void TdummyChord::setChord(TalaChord *c) +{ + m_alaChord = c; + m_alaChord->setDummyChord(this); findHiLoPos(); - connect(m_parentNote->parent(), &QObject::destroyed, this, [=]{ m_parentNote = nullptr; }); emit chordChanged(); - } + if (m_selected == -1) + setSelected(0); } - -void TdummyChord::setChord(TalaChord* c) { - m_alaChord = c; - m_alaChord->setDummyChord(this); - findHiLoPos(); - emit chordChanged(); - if (m_selected == -1) - setSelected(0); -} - - -void TdummyChord::setSelected(int s) { - if (s != m_selected) { - bool changeNote = m_selected > 0 || s > 0; - m_selected = s; - emit selectedChanged(); - if (changeNote) { // change the 'original' melody only when user selected other note from the chord - Tnote& n = m_alaChord->part->melody()->note(m_alaChord->noteNr())->p(); - auto ch = m_alaChord->notes()->note(m_selected)->p(); - n.setNote(ch.note()); - n.setOctave(ch.octave()); - n.setAlter(ch.alter()); +void TdummyChord::setSelected(int s) +{ + if (s != m_selected) { + bool changeNote = m_selected > 0 || s > 0; + m_selected = s; + emit selectedChanged(); + if (changeNote) { // change the 'original' melody only when user selected other note from the chord + Tnote &n = m_alaChord->part->melody()->note(m_alaChord->noteNr())->p(); + auto ch = m_alaChord->notes()->note(m_selected)->p(); + n.setNote(ch.note()); + n.setOctave(ch.octave()); + n.setAlter(ch.alter()); + } } - } } - -QString TdummyChord::noteHead() const { - return m_parentNote ? m_parentNote->getHeadText(m_parentNote->note()->rtm) : QString(); +QString TdummyChord::noteHead() const +{ + return m_parentNote ? m_parentNote->getHeadText(m_parentNote->note()->rtm) : QString(); } - -qreal TdummyChord::headPos(int id) { - return m_parentNote && m_alaChord ? m_parentNote->getHeadY(m_alaChord->notes()->note(id)->p()) : 0.0; +qreal TdummyChord::headPos(int id) +{ + return m_parentNote && m_alaChord ? m_parentNote->getHeadY(m_alaChord->notes()->note(id)->p()) : 0.0; } - -QString TdummyChord::alterText(int id) { - Tnote& n = m_alaChord->notes()->note(id)->p(); - QString a = TnoteItem::unicodeGlyphArray(n.alter()); - if (m_alaChord->part->score() && m_alaChord->part->score()->accidInKey(n.note() - 1)) { - // when key signature has an accidental on this note - if (n.alter() == 0) // show neutral if note has not any accidental - a = QStringLiteral("\ue261"); - else - a.clear(); - } - return a; +QString TdummyChord::alterText(int id) +{ + Tnote &n = m_alaChord->notes()->note(id)->p(); + QString a = TnoteItem::unicodeGlyphArray(n.alter()); + if (m_alaChord->part->score() && m_alaChord->part->score()->accidInKey(n.note() - 1)) { + // when key signature has an accidental on this note + if (n.alter() == 0) // show neutral if note has not any accidental + a = QStringLiteral("\ue261"); + else + a.clear(); + } + return a; } - -QVariant TdummyChord::part() { - return QVariant::fromValue(m_alaChord->part); +QVariant TdummyChord::part() +{ + return QVariant::fromValue(m_alaChord->part); } - -bool TdummyChord::selectSingle() const { - return m_alaChord && m_alaChord->notes()->note(0)->p().rhythm() == Trhythm::NoRhythm; +bool TdummyChord::selectSingle() const +{ + return m_alaChord && m_alaChord->notes()->note(0)->p().rhythm() == Trhythm::NoRhythm; } - -void TdummyChord::setRhythm() { - m_alaChord->setRhythm(); - emit selectSingleChanged(); +void TdummyChord::setRhythm() +{ + m_alaChord->setRhythm(); + emit selectSingleChanged(); } - -void TdummyChord::arpeggiateChord() { - m_alaChord->arpeggiateChord(); +void TdummyChord::arpeggiateChord() +{ + m_alaChord->arpeggiateChord(); } - -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# -void TdummyChord::findHiLoPos() { - if (m_loPosY < 1.0 && m_alaChord) { // yet unset - for (int n = 0; n < m_alaChord->notes()->length(); ++n) { - auto hp = headPos(n); - m_hiPosY = qMin(m_hiPosY, hp); - m_loPosY = qMax(m_loPosY, hp); +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# +void TdummyChord::findHiLoPos() +{ + if (m_loPosY < 1.0 && m_alaChord) { // yet unset + for (int n = 0; n < m_alaChord->notes()->length(); ++n) { + auto hp = headPos(n); + m_hiPosY = qMin(m_hiPosY, hp); + m_loPosY = qMax(m_loPosY, hp); + } } - } } diff --git a/src/libs/core/score/tdummychord.h b/src/libs/core/score/tdummychord.h index aa1e9669d306ae84dd8662941cf17bb916b8cd6d..7d11209057511af5ea35a821848ed1e89ea1ec6c 100644 --- a/src/libs/core/score/tdummychord.h +++ b/src/libs/core/score/tdummychord.h @@ -19,16 +19,13 @@ #ifndef TDUMMYCHORD_H #define TDUMMYCHORD_H - -#include <nootkacoreglobal.h> -#include "music/tnote.h" #include "music/timportscore.h" +#include "music/tnote.h" #include <QtQuick/qquickitem.h> - +#include <nootkacoreglobal.h> class TnoteItem; - /** * @class TdummyChord is QML item over @p TnoteItem score note * with chord notes from @p TalaChord member. @@ -41,71 +38,69 @@ class TnoteItem; */ class NOOTKACORE_EXPORT TdummyChord : public QQuickItem { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(QString noteHead READ noteHead NOTIFY chordChanged) - Q_PROPERTY(int chordModel READ chordModel NOTIFY chordChanged) - Q_PROPERTY(Tmelody* chord READ chord NOTIFY chordChanged) - Q_PROPERTY(int selected READ selected WRITE setSelected NOTIFY selectedChanged) - Q_PROPERTY(qreal hiPosY READ hiPosY NOTIFY chordChanged) - Q_PROPERTY(qreal loPosY READ loPosY NOTIFY chordChanged) - Q_PROPERTY(bool selectSingle READ selectSingle NOTIFY selectSingleChanged) + Q_PROPERTY(QString noteHead READ noteHead NOTIFY chordChanged) + Q_PROPERTY(int chordModel READ chordModel NOTIFY chordChanged) + Q_PROPERTY(Tmelody *chord READ chord NOTIFY chordChanged) + Q_PROPERTY(int selected READ selected WRITE setSelected NOTIFY selectedChanged) + Q_PROPERTY(qreal hiPosY READ hiPosY NOTIFY chordChanged) + Q_PROPERTY(qreal loPosY READ loPosY NOTIFY chordChanged) + Q_PROPERTY(bool selectSingle READ selectSingle NOTIFY selectSingleChanged) public: - explicit TdummyChord(QQuickItem* parent = nullptr); - ~TdummyChord(); - - int chordModel() const; + explicit TdummyChord(QQuickItem *parent = nullptr); + ~TdummyChord(); - Tmelody* chord() { return m_alaChord->notes(); } - void setChord(TalaChord* c); + int chordModel() const; - /** - * Number of selected note from this chord. - */ - int selected() const { return m_selected; } - void setSelected(int s); + Tmelody *chord() { return m_alaChord->notes(); } + void setChord(TalaChord *c); - QString noteHead() const; + /** + * Number of selected note from this chord. + */ + int selected() const { return m_selected; } + void setSelected(int s); + QString noteHead() const; - qreal hiPosY() const { return m_hiPosY; } - qreal loPosY() const { return m_loPosY; } + qreal hiPosY() const { return m_hiPosY; } + qreal loPosY() const { return m_loPosY; } - /** - * @p TRUE when single note from the chord is to select, - * otherwise arpeggiating is done - */ - bool selectSingle() const; + /** + * @p TRUE when single note from the chord is to select, + * otherwise arpeggiating is done + */ + bool selectSingle() const; - /** - * Handle note item (parent) change. - * Connect with note parent (@p TscoreObj::destroyed) - * to reset @p m_parentNote - */ - void setParentItem(QQuickItem* pi); + /** + * Handle note item (parent) change. + * Connect with note parent (@p TscoreObj::destroyed) + * to reset @p m_parentNote + */ + void setParentItem(QQuickItem *pi); - Q_INVOKABLE qreal headPos(int id); - Q_INVOKABLE QString alterText(int id); - Q_INVOKABLE QVariant part(); + Q_INVOKABLE qreal headPos(int id); + Q_INVOKABLE QString alterText(int id); + Q_INVOKABLE QVariant part(); - Q_INVOKABLE void setRhythm(); - Q_INVOKABLE void arpeggiateChord(); + Q_INVOKABLE void setRhythm(); + Q_INVOKABLE void arpeggiateChord(); signals: - void chordChanged(); - void selectedChanged(); - void selectSingleChanged(); + void chordChanged(); + void selectedChanged(); + void selectSingleChanged(); protected: - void findHiLoPos(); + void findHiLoPos(); private: - TalaChord *m_alaChord = nullptr; - TnoteItem *m_parentNote = nullptr; - int m_selected = -1; - qreal m_hiPosY = 50.0, m_loPosY = 0.0; + TalaChord *m_alaChord = nullptr; + TnoteItem *m_parentNote = nullptr; + int m_selected = -1; + qreal m_hiPosY = 50.0, m_loPosY = 0.0; }; #endif // TDUMMYCHORD_H diff --git a/src/libs/core/score/tmeasureobject.cpp b/src/libs/core/score/tmeasureobject.cpp index e79b10222aae31af4c71aa108598e5444cd795d2..708b7cf6de848d09092dff6db9cc5ae9fc9f0978 100644 --- a/src/libs/core/score/tmeasureobject.cpp +++ b/src/libs/core/score/tmeasureobject.cpp @@ -17,493 +17,491 @@ ***************************************************************************/ #include "tmeasureobject.h" -#include "tscoreobject.h" -#include "tstaffitem.h" -#include "tnoteitem.h" -#include "tnotepair.h" -#include "tbeamobject.h" #include "music/tmeter.h" #include "music/tnote.h" +#include "tbeamobject.h" +#include "tnoteitem.h" +#include "tnotepair.h" +#include "tscoreobject.h" +#include "tstaffitem.h" +#include <QtCore/qdebug.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> -#include <QtCore/qdebug.h> +void content(TmeasureObject *m) +{ + QString c = QStringLiteral("| "); + for (int n = 0; n < m->noteCount(); ++n) + c += QString("(%1)").arg(m->note(n)->index()) + QLatin1String("<") + m->note(n)->note()->toText() + QLatin1String(">") + + m->note(n)->note()->rtm.string() + QLatin1String(" "); + c += QStringLiteral("|"); + qDebug() << m->debug() << c; +} + +TmeasureObject::TmeasureObject(int nr, TscoreObject *parent) + : QObject(parent) + , m_number(nr) + , m_score(parent) + , m_staff(nullptr) + , m_firstInGr(new qint8[1]) + , m_barLine(nullptr) +{ + clearAccidState(); + m_duration = m_score->meter()->duration(); + m_free = m_duration; -void content(TmeasureObject* m) { - QString c = QStringLiteral("| "); - for (int n = 0; n < m->noteCount(); ++n) - c += QString("(%1)").arg(m->note(n)->index()) + QLatin1String("<") + m->note(n)->note()->toText() + QLatin1String(">") + m->note(n)->note()->rtm.string() + QLatin1String(" "); - c += QStringLiteral("|"); - qDebug() << m->debug() << c; + connect(qApp, &QGuiApplication::paletteChanged, this, [=] { + if (m_barLine) + m_barLine->setProperty("color", qApp->palette().text().color()); + }); } - -TmeasureObject::TmeasureObject(int nr, TscoreObject* parent) : - QObject(parent), - m_number(nr), - m_score(parent), - m_staff(nullptr), - m_firstInGr(new qint8[1]), - m_barLine(nullptr) +TmeasureObject::~TmeasureObject() { - clearAccidState(); - m_duration = m_score->meter()->duration(); - m_free = m_duration; - - connect(qApp, &QGuiApplication::paletteChanged, this, [=]{ if (m_barLine) - m_barLine->setProperty("color", qApp->palette().text().color()); - }); + delete m_barLine; + delete[] m_firstInGr; + // qDebug() << debug() << "is going delete"; } - -TmeasureObject::~TmeasureObject() +void TmeasureObject::setNumber(int nr) { - if (m_barLine) - delete m_barLine; - delete[] m_firstInGr; -// qDebug() << debug() << "is going delete"; + m_number = nr; } - -void TmeasureObject::setNumber(int nr) { - m_number = nr; +void TmeasureObject::setStaff(TstaffItem *st) +{ + if (m_staff != st) { + m_staff = st; + for (TnotePair *np : qAsConst(m_notes)) + np->item()->setStaff(m_staff); + } } - -void TmeasureObject::setStaff(TstaffItem* st) { - if (m_staff != st) { - m_staff = st; - for (TnotePair* np : qAsConst(m_notes)) - np->item()->setStaff(m_staff); - } +int TmeasureObject::durationFrom(int id) +{ + int dur = m_free; + if (id < noteCount()) { + for (int n = id; n < noteCount(); ++n) + dur += note(n)->note()->duration(); + } else // TODO It should never occur, delete if so + qDebug() << debug() << "FIXME! This note doesn't belong to this measure or doesn't exist at all!" << id; + return dur; } - -int TmeasureObject::durationFrom(int id) { - int dur = m_free; - if (id < noteCount()) { - for (int n = id; n < noteCount(); ++n) - dur += note(n)->note()->duration(); - } else // TODO It should never occur, delete if so - qDebug() << debug() << "FIXME! This note doesn't belong to this measure or doesn't exist at all!" << id; - return dur; +int TmeasureObject::durationBefore(TnoteItem *it) +{ + int dur = 0; + bool found = false; + for (int n = 0; n < noteCount(); ++n) { + if (note(n)->item() != it) + dur += note(n)->note()->duration(); + else { + found = true; + break; + } + } + return found ? dur : 0; } - -int TmeasureObject::durationBefore(TnoteItem* it) { - int dur = 0; - bool found = false; - for (int n = 0; n < noteCount(); ++n) { - if (note(n)->item() != it) - dur += note(n)->note()->duration(); - else { - found = true; - break; +void TmeasureObject::appendNewNotes(int segmentId, int count) +{ + // so far we are sure there is enough space for whole note list in this measure + // qDebug() << debug() << "append" << count << "note(s) from" << segmentId << "measure duration" << duration(); + for (int n = segmentId; n < segmentId + count; ++n) + m_notes.append(m_score->noteSegment(n)); + updateRhythmicGroups(); + int grWithBeam = beamGroup(segmentId); + for (int n = segmentId; n < segmentId + count; ++n) { + auto np = m_score->noteSegment(n); + if (np->item() == nullptr) + np->setNoteItem(new TnoteItem(m_staff, np)); + else + np->item()->setStaff(m_staff); + np->item()->setMeasure(this); + checkAccidentals(); + np->item()->setNote(*np->note()); + if (m_score->showNoteNames()) + np->item()->setNoteNameVisible(true); + } + if (grWithBeam > -1) { + auto firstInGrId = m_score->noteSegment(firstNoteId() + m_firstInGr[grWithBeam])->index(); + TbeamObject *curBeam = nullptr; + while (firstInGrId < m_score->notesCount()) { + auto ns = m_score->noteSegment(firstInGrId); + if (ns->beam()) { + if (ns->beam() != curBeam) { + ns->beam()->prepareBeam(); + curBeam = ns->beam(); + } + } + ++firstInGrId; + } } - } - return found ? dur : 0; + refresh(); + m_staff->refresh(); + checkBarLine(); } +void TmeasureObject::insertNotes(Tpairs &nList, int startId) +{ + int listDur = TnotePair::pairsDuration(nList); + Tpairs outNotes; + if (listDur > m_score->meter()->duration()) + qDebug() << debug() << "FIXME! Ooh, notes to insert are longer than entire measure can contain!" << listDur; + + if (listDur > m_free) { + int leftDur = releaseAtEnd(listDur - m_free, outNotes, startId); + if (leftDur) // TODO it should never happen - DELETE IT! + qDebug() << debug() << "FIXME! Can't insert" << listDur << leftDur << listDur - m_free; + } + for (int n = 0; n < nList.count(); ++n) { + auto noteIn = nList[n]; + m_notes.insert(startId, noteIn); + if (noteIn->item() == nullptr) + noteIn->setNoteItem(new TnoteItem(m_staff, noteIn)); + else if (m_staff != noteIn->item()->staff()) + noteIn->item()->setStaff(m_staff); + noteIn->item()->setMeasure(this); + } + update(m_notes[startId]->rhythmGroup()); // nList.first()->rhythmGroup() + shiftReleased(outNotes); +} -void TmeasureObject::appendNewNotes(int segmentId, int count) { - // so far we are sure there is enough space for whole note list in this measure -// qDebug() << debug() << "append" << count << "note(s) from" << segmentId << "measure duration" << duration(); - for (int n = segmentId; n < segmentId + count; ++n) - m_notes.append(m_score->noteSegment(n)); - updateRhythmicGroups(); - int grWithBeam = beamGroup(segmentId); - for (int n = segmentId; n < segmentId + count; ++n) { - auto np = m_score->noteSegment(n); - if (np->item() == nullptr) - np->setNoteItem(new TnoteItem(m_staff, np)); - else - np->item()->setStaff(m_staff); - np->item()->setMeasure(this); - checkAccidentals(); - np->item()->setNote(*np->note()); - if (m_score->showNoteNames()) - np->item()->setNoteNameVisible(true); - } - if (grWithBeam > -1) { - auto firstInGrId = m_score->noteSegment(firstNoteId() + m_firstInGr[grWithBeam])->index(); - TbeamObject *curBeam = nullptr; - while (firstInGrId < m_score->notesCount()) { - auto ns = m_score->noteSegment(firstInGrId); - if (ns->beam()) { - if (ns->beam() != curBeam) { - ns->beam()->prepareBeam(); - curBeam = ns->beam(); +void TmeasureObject::insertNote(TnoteItem *afterItem) +{ + if (afterItem) { + int afterIdInBar = afterItem->index() - afterItem->measure()->firstNoteId(); + int possibleDur = afterItem->measure()->durationFrom(afterIdInBar); + int workDur = m_score->workRhythm().duration(); + // cut note duration if it is longer than possible measure duration starting from the item + auto newRtmList = Trhythm::resolve(workDur > possibleDur ? possibleDur : workDur); + Tpairs nl; + for (Trhythm rtm : qAsConst(newRtmList)) { + Tnote newNote(0, 0, 0, Trhythm(rtm.rhythm(), true)); + auto np = m_score->insertSilently(afterItem->index(), newNote, this); + m_notes.removeAt(afterIdInBar); + nl << np; } - } - ++firstInGrId; + insertNotes(nl, afterIdInBar); } - } - refresh(); - m_staff->refresh(); - checkBarLine(); -} - - -void TmeasureObject::insertNotes(Tpairs& nList, int startId) { - int listDur = TnotePair::pairsDuration(nList); - Tpairs outNotes; - if (listDur > m_score->meter()->duration()) - qDebug() << debug() << "FIXME! Ooh, notes to insert are longer than entire measure can contain!" << listDur; - - if (listDur > m_free) { - int leftDur = releaseAtEnd(listDur - m_free, outNotes, startId); - if (leftDur) // TODO it should never happen - DELETE IT! - qDebug() << debug() << "FIXME! Can't insert" << listDur << leftDur << listDur - m_free; - } - for (int n = 0; n < nList.count(); ++n) { - auto noteIn = nList[n]; - m_notes.insert(startId, noteIn); - if (noteIn->item() == nullptr) - noteIn->setNoteItem(new TnoteItem(m_staff, noteIn)); - else if (m_staff != noteIn->item()->staff()) - noteIn->item()->setStaff(m_staff); - noteIn->item()->setMeasure(this); - } - update(m_notes[startId]->rhythmGroup()); // nList.first()->rhythmGroup() - shiftReleased(outNotes); -} - - -void TmeasureObject::insertNote(TnoteItem* afterItem) { - if (afterItem) { - int afterIdInBar = afterItem->index() - afterItem->measure()->firstNoteId(); - int possibleDur = afterItem->measure()->durationFrom(afterIdInBar); - int workDur = m_score->workRhythm().duration(); - // cut note duration if it is longer than possible measure duration starting from the item - auto newRtmList = Trhythm::resolve(workDur > possibleDur ? possibleDur : workDur); - Tpairs nl; - for (Trhythm rtm : qAsConst(newRtmList)) { - Tnote newNote(0, 0, 0, Trhythm(rtm.rhythm(), true)); - auto np = m_score->insertSilently(afterItem->index(), newNote, this); - m_notes.removeAt(afterIdInBar); - nl << np; +} + +void TmeasureObject::removeNote(TnotePair *n) +{ + m_free += n->item()->note()->duration(); // n->note() is already null here + m_notes.takeAt(n->index() - firstNoteId()); + fill(); +} + +void TmeasureObject::removeLastNote() +{ + if (m_free == 0 && m_barLine) { + m_barLine->setVisible(false); + m_barLine->setParentItem(nullptr); } - insertNotes(nl, afterIdInBar); - } -} - - -void TmeasureObject::removeNote(TnotePair* n) { - m_free += n->item()->note()->duration(); // n->note() is already null here - m_notes.takeAt(n->index() - firstNoteId()); - fill(); -} - - -void TmeasureObject::removeLastNote() { - if (m_free == 0 && m_barLine) { - m_barLine->setVisible(false); - m_barLine->setParentItem(nullptr); - } - auto noteToRemove = m_notes.takeLast(); - updateRhythmicGroups(); - // TODO Try to use resolveBeaming here - if (noteToRemove->beam()) { - if (noteToRemove->beam()->count() < 3) - noteToRemove->beam()->deleteBeam(); - else - noteToRemove->beam()->removeNote(noteToRemove); - int segId = m_firstInGr[noteToRemove->rhythmGroup()]; - while (segId < m_notes.count()) { // update notes of entire rhythm group - m_notes[segId]->approve(); - segId++; + auto noteToRemove = m_notes.takeLast(); + updateRhythmicGroups(); + // TODO Try to use resolveBeaming here + if (noteToRemove->beam()) { + if (noteToRemove->beam()->count() < 3) + noteToRemove->beam()->deleteBeam(); + else + noteToRemove->beam()->removeNote(noteToRemove); + int segId = m_firstInGr[noteToRemove->rhythmGroup()]; + while (segId < m_notes.count()) { // update notes of entire rhythm group + m_notes[segId]->approve(); + segId++; + } } - } - refresh(); - m_staff->refresh(); + refresh(); + m_staff->refresh(); } +void TmeasureObject::keySignatureChanged() +{ + for (int n = 0; n < m_notes.size(); ++n) { + m_notes[n]->item()->keySignatureChanged(); + } + refresh(); +} -void TmeasureObject::keySignatureChanged() { - for (int n = 0; n < m_notes.size(); ++n) { - m_notes[n]->item()->keySignatureChanged(); - } - refresh(); +int TmeasureObject::firstNoteId() const +{ + return m_notes.isEmpty() ? 0 : m_notes.first()->index(); } +int TmeasureObject::lastNoteId() const +{ + return m_notes.isEmpty() ? 0 : m_notes.last()->index(); +} -int TmeasureObject::firstNoteId() const { - return m_notes.isEmpty() ? 0 : m_notes.first()->index(); +char TmeasureObject::debug() +{ + QTextStream o(stdout); + o << " \033[01;33m[" << QString("%1/%2").arg(number() + 1).arg(m_staff ? m_staff->number() + 1 : -1) << " MEASURE]\033[01;00m"; + return 32; // fake } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -int TmeasureObject::lastNoteId() const { - return m_notes.isEmpty() ? 0 : m_notes.last()->index(); +void TmeasureObject::flush() +{ + if (m_barLine) + m_barLine->setVisible(false); + m_notes.clear(); + setStaff(nullptr); + m_allNotesWidth = 0.0; + m_gapsSum = 0.0; + delete[] m_firstInGr; + m_firstInGr = new qint8[1]; } - -char TmeasureObject::debug() { - QTextStream o(stdout); - o << " \033[01;33m[" << QString("%1/%2").arg(number() + 1).arg(m_staff ? m_staff->number() + 1 : -1) << " MEASURE]\033[01;00m"; - return 32; // fake +void TmeasureObject::updateRhythmicGroups() +{ + if (duration() == 0) + return; + + int notePos = 0, grNr = 0, currGr = 0; + delete[] m_firstInGr; + m_firstInGr = new qint8[m_score->groupCount()]; + m_firstInGr[0] = 0; // first note in measure also begins first rhythmic group + for (int i = 0; i < m_notes.size(); ++i) { + if (currGr != grNr) { + m_firstInGr[grNr] = i; + currGr = grNr; + } + m_notes[i]->setRhythmGroup(grNr); + /** We are cheating here: no-rhythm-note gets duration of 1 - such a rhythm doesn't exists */ + notePos += m_notes[i]->note()->rhythm() == Trhythm::NoRhythm ? 1 : m_notes[i]->note()->duration(); + while (grNr < m_score->groupCount() && notePos >= m_score->groupPos(grNr)) + grNr++; + } + if (currGr < m_score->groupCount() - 1) { // fill the rest of the array + for (int gr = currGr + 1; gr < m_score->groupCount(); ++gr) + m_firstInGr[gr] = -1; // with '-1' - means no notes yet + } + m_free = m_duration - notePos; } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +void TmeasureObject::checkBarLine() +{ + if (m_free == 0 && m_score->meter()->meter() != Tmeter::NoMeter) { + // qDebug() << debug() << "check bar line"; + auto lastNote = last()->item(); + if (!m_barLine) { + m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle { width: 0.3 }", QUrl()); + m_barLine = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_barLine->setProperty("color", qApp->palette().text().color()); + m_barLine->setParentItem(lastNote); + } + m_barLine->setParentItem(lastNote); + m_barLine->setVisible(true); + qreal xOff = lastNote == m_staff->lastMeasure()->last()->item() ? 0.2 : 0.0; // fit line at the staff end + m_barLine->setX(lastNote->rightX() - lastNote->x() + xOff); + m_barLine->setY(m_staff->upperLine()); + m_barLine->setHeight(m_score->isPianoStaff() ? 30.0 : 8.0); + } +} -void TmeasureObject::flush() { - if (m_barLine) - m_barLine->setVisible(false); - m_notes.clear(); - setStaff(nullptr); - m_allNotesWidth = 0.0; - m_gapsSum = 0.0; - delete[] m_firstInGr; - m_firstInGr = new qint8[1]; +void TmeasureObject::meterChanged() +{ + m_duration = m_score->meter()->duration(); + updateRhythmicGroups(); } +void TmeasureObject::refresh() +{ + m_gapsSum = 0.0; + m_allNotesWidth = 0.0; + for (int n = 0; n < m_notes.size(); ++n) { + auto noteObj = note(n)->item(); + m_gapsSum += noteObj->rhythmFactor(); + m_allNotesWidth += noteObj->width(); + } +} -void TmeasureObject::updateRhythmicGroups() { - if (duration() == 0) - return; +void TmeasureObject::checkAccidentals() +{ + clearAccidState(); + for (int n = 0; n < m_notes.size(); ++n) { + auto np = note(n); + if (np->note()->isValid() && !np->note()->isRest()) + m_accidsState[np->note()->note() - 1] = np->note()->alter(); // register accidental of a note + } +} - int notePos = 0, grNr = 0, currGr = 0; - delete[] m_firstInGr; - m_firstInGr = new qint8[m_score->groupCount()]; - m_firstInGr[0] = 0; // first note in measure also begins first rhythmic group - for (int i = 0; i < m_notes.size(); ++i) { - if (currGr != grNr) { - m_firstInGr[grNr] = i; - currGr = grNr; +int TmeasureObject::beamGroup(int segmentId) +{ + int currGr = m_score->noteSegment(segmentId)->rhythmGroup(); + int segId = m_firstInGr[currGr] + 1; + if (segId < 0) { // TODO it should never happen - DELETE IT! + qDebug() << debug() << "FIXME! beamGroup()"; + return -1; } - m_notes[i]->setRhythmGroup(grNr); - /** We are cheating here: no-rhythm-note gets duration of 1 - such a rhythm doesn't exists */ - notePos += m_notes[i]->note()->rhythm() == Trhythm::NoRhythm ? 1 : m_notes[i]->note()->duration(); - while (grNr < m_score->groupCount() && notePos >= m_score->groupPos(grNr)) - grNr++; - } - if (currGr < m_score->groupCount() - 1) { // fill the rest of the array - for (int gr = currGr + 1; gr < m_score->groupCount(); ++gr) - m_firstInGr[gr] = -1; // with '-1' - means no notes yet - } - m_free = m_duration - notePos; -} - - -void TmeasureObject::checkBarLine() { - if (m_free == 0 && m_score->meter()->meter() != Tmeter::NoMeter) { -// qDebug() << debug() << "check bar line"; - auto lastNote = last()->item(); - if (!m_barLine) { - m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle { width: 0.3 }", QUrl()); - m_barLine = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_barLine->setProperty("color", qApp->palette().text().color()); - m_barLine->setParentItem(lastNote); + int grWithBeam = -1; + while (segId < m_notes.count() && m_notes[segId]->rhythmGroup() == currGr) { + auto noteSeg = m_notes[segId]; + auto prevSeg = m_notes[segId - 1]; + if (!noteSeg->note()->isRest() && !prevSeg->note()->isRest() // not a rest + && noteSeg->note()->rhythm() > Trhythm::Quarter // sixteenth or eighth + && prevSeg->note()->rhythm() > Trhythm::Quarter + && (!m_score->isPianoStaff() || noteSeg->note()->onUpperStaff() == prevSeg->note()->onUpperStaff())) { + if (prevSeg->note()->rtm.beam() == Trhythm::e_noBeam) // start beam group + prevSeg->setBeam(m_score->getBeam(prevSeg, this)); + auto beam = prevSeg->beam(); + if (noteSeg->beam() == nullptr) + beam->addNote(noteSeg); + grWithBeam = currGr; + } + segId++; } - m_barLine->setParentItem(lastNote); - m_barLine->setVisible(true); - qreal xOff = lastNote == m_staff->lastMeasure()->last()->item() ? 0.2 : 0.0; // fit line at the staff end - m_barLine->setX(lastNote->rightX() - lastNote->x() + xOff); - m_barLine->setY(m_staff->upperLine()); - m_barLine->setHeight(m_score->isPianoStaff() ? 30.0 : 8.0); - } -} - - -void TmeasureObject::meterChanged() { - m_duration = m_score->meter()->duration(); - updateRhythmicGroups(); -} - - -void TmeasureObject::refresh() { - m_gapsSum = 0.0; - m_allNotesWidth = 0.0; - for (int n = 0; n < m_notes.size(); ++n) { - auto noteObj = note(n)->item(); - m_gapsSum += noteObj->rhythmFactor(); - m_allNotesWidth += noteObj->width(); - } -} - - -void TmeasureObject::checkAccidentals() { - clearAccidState(); - for (int n = 0; n < m_notes.size(); ++n) { - auto np = note(n); - if (np->note()->isValid() && !np->note()->isRest()) - m_accidsState[np->note()->note() - 1] = np->note()->alter(); // register accidental of a note - } -} - - -int TmeasureObject::beamGroup(int segmentId) { - int currGr = m_score->noteSegment(segmentId)->rhythmGroup(); - int segId = m_firstInGr[currGr] + 1; - if (segId < 0) { // TODO it should never happen - DELETE IT! - qDebug() << debug() << "FIXME! beamGroup()"; - return -1; - } - int grWithBeam = -1; - while (segId < m_notes.count() && m_notes[segId]->rhythmGroup() == currGr) { - auto noteSeg = m_notes[segId]; - auto prevSeg = m_notes[segId - 1]; - if (!noteSeg->note()->isRest() && !prevSeg->note()->isRest() // not a rest - && noteSeg->note()->rhythm() > Trhythm::Quarter // sixteenth or eighth - && prevSeg->note()->rhythm() > Trhythm::Quarter - && (!m_score->isPianoStaff() || noteSeg->note()->onUpperStaff() == prevSeg->note()->onUpperStaff()) - ) - { - if (prevSeg->note()->rtm.beam() == Trhythm::e_noBeam) // start beam group - prevSeg->setBeam(m_score->getBeam(prevSeg, this)); - auto beam = prevSeg->beam(); - if (noteSeg->beam() == nullptr) - beam->addNote(noteSeg); - grWithBeam = currGr; + return grWithBeam; +} + +void TmeasureObject::noteGoingRest(TnotePair *np) +{ + if (np->beam()) { + if (np->beam()->count() > 2) { + if (np->beam()->removeNote(np)) + np->beam()->deleteBeam(); + } else + np->beam()->deleteBeam(); + int segId = m_firstInGr[np->rhythmGroup()]; + while (segId < m_notes.count() && m_notes[segId]->rhythmGroup() == np->rhythmGroup()) { // update notes of entire rhythm group + m_notes[segId]->approve(); + segId++; + } } - segId++; - } - return grWithBeam; -} - - -void TmeasureObject::noteGoingRest(TnotePair* np) { - if (np->beam()) { - if (np->beam()->count() > 2) { - if (np->beam()->removeNote(np)) - np->beam()->deleteBeam(); - } else - np->beam()->deleteBeam(); - int segId = m_firstInGr[np->rhythmGroup()]; - while (segId < m_notes.count() && m_notes[segId]->rhythmGroup() == np->rhythmGroup()) { // update notes of entire rhythm group - m_notes[segId]->approve(); - segId++; +} + +void TmeasureObject::restGoingNote(TnotePair *np) +{ + QList<QList<int>> beamLists; + QList<int> l1; + beamLists << l1; + for (int bb = m_firstInGr[np->rhythmGroup()]; bb < m_notes.count() && m_notes[bb]->rhythmGroup() == np->rhythmGroup(); ++bb) { + auto prevSeg = m_notes[bb]; + if (!prevSeg->note()->isRest() && prevSeg->note()->rhythm() > Trhythm::Quarter) { + auto lastBeam = beamLists.last(); + if (!lastBeam.isEmpty()) { + if (lastBeam.last() < bb - 1) { // a new beam if there was a rest in between notes in current rhythm group + QList<int> newBeam; + beamLists << newBeam; + } + } + beamLists.last() << bb; + } } - } -} - - -void TmeasureObject::restGoingNote(TnotePair* np) { - QList<QList<int>> beamLists; - QList<int> l1; - beamLists << l1; - for (int bb = m_firstInGr[np->rhythmGroup()]; bb < m_notes.count() && m_notes[bb]->rhythmGroup() == np->rhythmGroup(); ++bb) { - auto prevSeg = m_notes[bb]; - if (!prevSeg->note()->isRest() && prevSeg->note()->rhythm() > Trhythm::Quarter) { - auto lastBeam = beamLists.last(); - if (!lastBeam.isEmpty()) { - if (lastBeam.last() < bb - 1) { // a new beam if there was a rest in between notes in current rhythm group - QList<int> newBeam; - beamLists << newBeam; + int noteId = np->index() - firstNoteId(); + for (int bl = 0; bl < beamLists.size(); ++bl) { + QList<int> &beam = beamLists[bl]; + if (beam.size() > 1 && noteId >= beam.first() && noteId <= beam.last()) { + TbeamObject *newBeam = nullptr; + for (int b = beam.first(); b <= beam.last(); ++b) { + auto noteInBeam = m_notes[b]; + if (noteInBeam->beam()) + delete noteInBeam->beam(); + if (newBeam) + newBeam->addNote(noteInBeam); + else + newBeam = m_score->getBeam(noteInBeam, this); + } + newBeam->prepareBeam(); + newBeam->drawBeam(); } - } - beamLists.last() << bb; } - } - int noteId = np->index() - firstNoteId(); - for (int bl = 0; bl < beamLists.size(); ++bl) { - QList<int>& beam = beamLists[bl]; - if (beam.size() > 1 && noteId >= beam.first() && noteId <= beam.last()) { - TbeamObject *newBeam = nullptr; - for (int b = beam.first(); b <= beam.last(); ++b) { - auto noteInBeam = m_notes[b]; - if (noteInBeam->beam()) - delete noteInBeam->beam(); - if (newBeam) - newBeam->addNote(noteInBeam); - else - newBeam = m_score->getBeam(noteInBeam, this); - } - newBeam->prepareBeam(); - newBeam->drawBeam(); +} + +void TmeasureObject::changeNoteDuration(TnotePair *np, const Tnote &newNote) +{ + int prevDur = np->note()->duration(); + int newDur = newNote.duration(); + if (prevDur == newDur) { + qDebug() << debug() << "FIXME! calling changeNoteDuration() when duration doesn't changed"; + return; } - } -} - - -void TmeasureObject::changeNoteDuration(TnotePair* np, const Tnote& newNote) { - int prevDur = np->note()->duration(); - int newDur = newNote.duration(); - if (prevDur == newDur) { - qDebug() << debug() << "FIXME! calling changeNoteDuration() when duration doesn't changed"; - return; - } - Tnote nn = newNote; - Tpairs notesToOut; - if (m_free - (newDur - prevDur) < 0) { // There is not enough space for new note - its duration is longer than possible free space in the measure - /** 1. Try to release measure (move notes after this @p np one to the next measure) */ - int leftDur = releaseAtEnd(newDur - prevDur - m_free, notesToOut, np->index() - firstNoteId() + 1); - if (leftDur) { - /** 2. There is still not enough space for new duration - so cut duration of this @p np note */ - auto thisBarRtms = Trhythm::resolve(m_free + prevDur); - nn.setRhythm(thisBarRtms.first()); - for (int r = 1; r < thisBarRtms.count(); ++r) { - if (!newNote.isRest()) - thisBarRtms[r].setTie(Trhythm::e_tieCont); - m_score->insertSilently(np->index() + r, Tnote(newNote, thisBarRtms[r]), this); + Tnote nn = newNote; + Tpairs notesToOut; + if (m_free - (newDur - prevDur) < 0) { // There is not enough space for new note - its duration is longer than possible free space in the measure + /** 1. Try to release measure (move notes after this @p np one to the next measure) */ + int leftDur = releaseAtEnd(newDur - prevDur - m_free, notesToOut, np->index() - firstNoteId() + 1); + if (leftDur) { + /** 2. There is still not enough space for new duration - so cut duration of this @p np note */ + auto thisBarRtms = Trhythm::resolve(m_free + prevDur); + nn.setRhythm(thisBarRtms.first()); + for (int r = 1; r < thisBarRtms.count(); ++r) { + if (!newNote.isRest()) + thisBarRtms[r].setTie(Trhythm::e_tieCont); + m_score->insertSilently(np->index() + r, Tnote(newNote, thisBarRtms[r]), this); + } } - } - np->setNote(nn); - - update(np->rhythmGroup()); - checkBarLine(); - } else { // measure duration is less than meter - take notes from the next measure - m_free += prevDur - newDur; - np->setNote(nn); - fill(); // it updates measure - } - shiftReleased(notesToOut); -} - - -void TmeasureObject::resolveBeaming(int firstGroup, int endGroup) { - if (noteCount() < 2) - return; - - if (endGroup == -1) - endGroup = m_score->groupCount() - 1; - - // delete beams in group range of [firstGroup to endGroup] - int noteId = m_firstInGr[firstGroup]; - if (noteId < 0) { // TODO it should never happen - DELETE IT! - qDebug() << debug() << "FIXME! wrong grouping"; - return; - } - int grId = firstGroup; - while (noteId < noteCount() && grId <= endGroup) { - if (m_notes[noteId]->beam()) - m_notes[noteId]->beam()->deleteBeam(); - noteId++; - if (noteId < noteCount()) - grId = m_notes[noteId]->rhythmGroup(); - } - - for (int g = firstGroup; g <= endGroup; ++g) { - if (m_firstInGr[g] < 0 || m_firstInGr[g] > noteCount() - 1) - break; - int gr = beamGroup(m_firstInGr[g] + firstNoteId()); - if (gr > -1 && gr < m_score->groupCount()) { - TbeamObject* beam = nullptr; TbeamObject* prevBeam = nullptr; - for (int n = m_firstInGr[gr]; n < noteCount(); ++n) { - if (m_notes[n]->rhythmGroup() != gr) - break; - beam = m_notes[n]->beam(); - if (beam && beam != prevBeam) { - beam->prepareBeam(); - prevBeam = beam; + np->setNote(nn); + + update(np->rhythmGroup()); + checkBarLine(); + } else { // measure duration is less than meter - take notes from the next measure + m_free += prevDur - newDur; + np->setNote(nn); + fill(); // it updates measure + } + shiftReleased(notesToOut); +} + +void TmeasureObject::resolveBeaming(int firstGroup, int endGroup) +{ + if (noteCount() < 2) + return; + + if (endGroup == -1) + endGroup = m_score->groupCount() - 1; + + // delete beams in group range of [firstGroup to endGroup] + int noteId = m_firstInGr[firstGroup]; + if (noteId < 0) { // TODO it should never happen - DELETE IT! + qDebug() << debug() << "FIXME! wrong grouping"; + return; + } + int grId = firstGroup; + while (noteId < noteCount() && grId <= endGroup) { + if (m_notes[noteId]->beam()) + m_notes[noteId]->beam()->deleteBeam(); + noteId++; + if (noteId < noteCount()) + grId = m_notes[noteId]->rhythmGroup(); + } + + for (int g = firstGroup; g <= endGroup; ++g) { + if (m_firstInGr[g] < 0 || m_firstInGr[g] > noteCount() - 1) + break; + int gr = beamGroup(m_firstInGr[g] + firstNoteId()); + if (gr > -1 && gr < m_score->groupCount()) { + TbeamObject *beam = nullptr; + TbeamObject *prevBeam = nullptr; + for (int n = m_firstInGr[gr]; n < noteCount(); ++n) { + if (m_notes[n]->rhythmGroup() != gr) + break; + beam = m_notes[n]->beam(); + if (beam && beam != prevBeam) { + beam->prepareBeam(); + prevBeam = beam; + } + } } - } } - } - // approve beam changes for all eighths and sixteenths that have no beam just flag - to revert the flag - if (m_firstInGr[firstGroup] >= 0 && m_firstInGr[firstGroup] < noteCount()) { - for (int n = m_firstInGr[firstGroup]; n < noteCount(); ++n) { - auto note = m_notes[n]; - if (!note->beam() && !note->note()->isRest() && note->note()->rhythm() > Trhythm::Quarter) - note->approve(); + // approve beam changes for all eighths and sixteenths that have no beam just flag - to revert the flag + if (m_firstInGr[firstGroup] >= 0 && m_firstInGr[firstGroup] < noteCount()) { + for (int n = m_firstInGr[firstGroup]; n < noteCount(); ++n) { + auto note = m_notes[n]; + if (!note->beam() && !note->note()->isRest() && note->note()->rhythm() > Trhythm::Quarter) + note->approve(); + } } - } } - /** * - iterate through notes in backward order (right to left), take note by note to release required duration * - create a list from taken notes to send it to the next measure @@ -511,153 +509,153 @@ void TmeasureObject::resolveBeaming(int firstGroup, int endGroup) { * - half of the duration remains in current measure at the end tied with * a new note that has to be created and push to the beginning of the next measure */ -int TmeasureObject::releaseAtEnd(int dur, Tpairs& notesToOut, int endNote) { - int noteNr = m_notes.count() - 1; - while (noteNr >= endNote && dur > 0) { - auto lastNote = last(); - int lastDur = lastNote->note()->duration(); - if (lastDur > dur) { // last note is longer than required space - split it and create and move the rest of its duration to the next measure - auto rList = Trhythm::resolve(lastDur - dur); - auto lastTie = lastNote->note()->rtm.tie(); - if (!lastNote->note()->isRest()) { - rList.first().setTie(lastTie > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); - } - for (int r = 1; r < rList.count(); ++r) { - if (!lastNote->note()->isRest()) - rList[r].setTie(Trhythm::e_tieCont); - m_score->insertSilently(lastNote->index() + r, Tnote(*lastNote->note(), rList[r]), this); - } - lastNote->setNote(Tnote(*lastNote->note(), rList.first())); - // remaining part of the note that goes to next measure - auto rtmToNext = Trhythm::resolve(dur); - int indexToInsert = rtmToNext.count() > 1 ? 0 : notesToOut.count(); - for (int r = 0; r < rtmToNext.count(); ++r) { - if (!lastNote->note()->isRest()) { - if (r < rtmToNext.count() - 1) - rtmToNext[r].setTie(Trhythm::e_tieCont); - else - rtmToNext[r].setTie(lastTie == Trhythm::e_tieCont ? lastTie : Trhythm::e_tieEnd); - } - m_score->insertSilently(last()->index() + r + 1, Tnote(*lastNote->note(), rtmToNext[r]), this); - notesToOut.insert(indexToInsert, m_notes.takeLast()); - } - lastDur = dur; // instead of: dur = 0; m_free += lastDur; lastDur = 0; - } else { // last note is the same long or smaller than required space - so move it to the next measure - notesToOut << m_notes.takeLast(); - // TODO maybe clear beams here - } - dur -= lastDur; - m_free += lastDur; // note was taken out so there is more free space in the measure - noteNr--; - } - - return dur; -} - - -void TmeasureObject::releaseAtStart(int dur, Tpairs& notesToOut) { - int retDur = 0; - TnotePair* firstNote; - Trhythm::Etie firstTie; - while (!m_notes.isEmpty() && dur > 0) { - firstNote = first(); - int firstDur = firstNote->note()->duration(); - if (firstDur > dur) { // first measure note is longer than required duration - shrink it and create new one - auto rList = Trhythm::resolve(firstDur - dur); - firstTie = firstNote->note()->rtm.tie(); - if (!firstNote->note()->isRest()) - rList.first().setTie(firstTie > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieEnd); - firstNote->setNote(Tnote(*firstNote->note(), rList.first())); - for (int r = 1; r < rList.count(); ++r) { - if (!firstNote->note()->isRest()) - rList[r].setTie(Trhythm::e_tieCont); - m_score->insertSilently(firstNoteId(), Tnote(*firstNote->note(), rList[r]), this); +int TmeasureObject::releaseAtEnd(int dur, Tpairs ¬esToOut, int endNote) +{ + int noteNr = m_notes.count() - 1; + while (noteNr >= endNote && dur > 0) { + auto lastNote = last(); + int lastDur = lastNote->note()->duration(); + if (lastDur > dur) { // last note is longer than required space - split it and create and move the rest of its duration to the next measure + auto rList = Trhythm::resolve(lastDur - dur); + auto lastTie = lastNote->note()->rtm.tie(); + if (!lastNote->note()->isRest()) { + rList.first().setTie(lastTie > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); + } + for (int r = 1; r < rList.count(); ++r) { + if (!lastNote->note()->isRest()) + rList[r].setTie(Trhythm::e_tieCont); + m_score->insertSilently(lastNote->index() + r, Tnote(*lastNote->note(), rList[r]), this); + } + lastNote->setNote(Tnote(*lastNote->note(), rList.first())); + // remaining part of the note that goes to next measure + auto rtmToNext = Trhythm::resolve(dur); + int indexToInsert = rtmToNext.count() > 1 ? 0 : notesToOut.count(); + for (int r = 0; r < rtmToNext.count(); ++r) { + if (!lastNote->note()->isRest()) { + if (r < rtmToNext.count() - 1) + rtmToNext[r].setTie(Trhythm::e_tieCont); + else + rtmToNext[r].setTie(lastTie == Trhythm::e_tieCont ? lastTie : Trhythm::e_tieEnd); + } + m_score->insertSilently(last()->index() + r + 1, Tnote(*lastNote->note(), rtmToNext[r]), this); + notesToOut.insert(indexToInsert, m_notes.takeLast()); + } + lastDur = dur; // instead of: dur = 0; m_free += lastDur; lastDur = 0; + } else { // last note is the same long or smaller than required space - so move it to the next measure + notesToOut << m_notes.takeLast(); + // TODO maybe clear beams here } - firstDur = dur; - retDur = dur; - dur = 0; - } else { // first note is the same long or smaller than required space - so move it to the next measure - notesToOut << m_notes.takeFirst(); - dur -= firstDur; - // TODO maybe clear beams here + dur -= lastDur; + m_free += lastDur; // note was taken out so there is more free space in the measure + noteNr--; } - m_free += firstDur; - } + return dur; +} - if (m_free) - fill(); +void TmeasureObject::releaseAtStart(int dur, Tpairs ¬esToOut) +{ + int retDur = 0; + TnotePair *firstNote; + Trhythm::Etie firstTie; + while (!m_notes.isEmpty() && dur > 0) { + firstNote = first(); + int firstDur = firstNote->note()->duration(); + if (firstDur > dur) { // first measure note is longer than required duration - shrink it and create new one + auto rList = Trhythm::resolve(firstDur - dur); + firstTie = firstNote->note()->rtm.tie(); + if (!firstNote->note()->isRest()) + rList.first().setTie(firstTie > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieEnd); + firstNote->setNote(Tnote(*firstNote->note(), rList.first())); + for (int r = 1; r < rList.count(); ++r) { + if (!firstNote->note()->isRest()) + rList[r].setTie(Trhythm::e_tieCont); + m_score->insertSilently(firstNoteId(), Tnote(*firstNote->note(), rList[r]), this); + } + firstDur = dur; + retDur = dur; + dur = 0; + } else { // first note is the same long or smaller than required space - so move it to the next measure + notesToOut << m_notes.takeFirst(); + dur -= firstDur; + // TODO maybe clear beams here + } + m_free += firstDur; + } - if (retDur) { - // remaining part of the note that goes to previous measure - auto rtmToPrev = Trhythm::resolve(retDur); - for (int r = 0; r < rtmToPrev.count(); ++r) { - if (!firstNote->note()->isRest()) { - if (r < rtmToPrev.count() - 1) - rtmToPrev[r].setTie(Trhythm::e_tieCont); - else - rtmToPrev[r].setTie(firstTie == Trhythm::e_tieCont ? firstTie : Trhythm::e_tieStart); - } - m_score->insertSilently(firstNoteId(), Tnote(*firstNote->note(), rtmToPrev[r]), this); - notesToOut.append(m_notes.takeFirst()); + if (m_free) + fill(); + + if (retDur) { + // remaining part of the note that goes to previous measure + auto rtmToPrev = Trhythm::resolve(retDur); + for (int r = 0; r < rtmToPrev.count(); ++r) { + if (!firstNote->note()->isRest()) { + if (r < rtmToPrev.count() - 1) + rtmToPrev[r].setTie(Trhythm::e_tieCont); + else + rtmToPrev[r].setTie(firstTie == Trhythm::e_tieCont ? firstTie : Trhythm::e_tieStart); + } + m_score->insertSilently(firstNoteId(), Tnote(*firstNote->note(), rtmToPrev[r]), this); + notesToOut.append(m_notes.takeFirst()); + } } - } - if (m_free && m_barLine) { - m_barLine->setVisible(false); - m_barLine->setParentItem(nullptr); - } + if (m_free && m_barLine) { + m_barLine->setVisible(false); + m_barLine->setParentItem(nullptr); + } } - -void TmeasureObject::insertSilently(int id, TnotePair* np) { - m_notes.insert(id, np); - if (np->item() == nullptr) - np->setNoteItem(new TnoteItem(m_staff, np)); - else if (m_staff != np->item()->staff()) - np->item()->setStaff(m_staff); - np->item()->setMeasure(this); - np->item()->setNote(*np->note()); - if (m_score->showNoteNames()) - np->item()->setNoteNameVisible(true); +void TmeasureObject::insertSilently(int id, TnotePair *np) +{ + m_notes.insert(id, np); + if (np->item() == nullptr) + np->setNoteItem(new TnoteItem(m_staff, np)); + else if (m_staff != np->item()->staff()) + np->item()->setStaff(m_staff); + np->item()->setMeasure(this); + np->item()->setNote(*np->note()); + if (m_score->showNoteNames()) + np->item()->setNoteNameVisible(true); } +// ################################################################################################# +// ################### PRIVATE ############################################ +// ################################################################################################# -//################################################################################################# -//################### PRIVATE ############################################ -//################################################################################################# - -void TmeasureObject::clearAccidState() { - for (int i = 0; i < 7; ++i) - m_accidsState[i] = 100; // note doesn't occur in a measure +void TmeasureObject::clearAccidState() +{ + for (int i = 0; i < 7; ++i) + m_accidsState[i] = 100; // note doesn't occur in a measure } - -void TmeasureObject::shiftReleased(Tpairs& notesToOut) { - if (!notesToOut.isEmpty()) - m_staff->shiftToMeasure(m_number + 1, notesToOut); +void TmeasureObject::shiftReleased(Tpairs ¬esToOut) +{ + if (!notesToOut.isEmpty()) + m_staff->shiftToMeasure(m_number + 1, notesToOut); } +void TmeasureObject::fill() +{ + Tpairs notesToShift; + m_staff->shiftFromMeasure(m_number + 1, m_free, notesToShift); + int lastId = lastNoteId() + 1; -void TmeasureObject::fill() { - Tpairs notesToShift; - m_staff->shiftFromMeasure(m_number + 1, m_free, notesToShift); - int lastId = lastNoteId() + 1; - - for (int i = 0; i < notesToShift.count(); ++i) - insertSilently(lastId + i, notesToShift[i]); + for (int i = 0; i < notesToShift.count(); ++i) + insertSilently(lastId + i, notesToShift[i]); - update(); - if (m_free && m_barLine) { - m_barLine->setVisible(false); - m_barLine->setParentItem(nullptr); - } + update(); + if (m_free && m_barLine) { + m_barLine->setVisible(false); + m_barLine->setParentItem(nullptr); + } } - -void TmeasureObject::update(int beamGrToResolve) { - updateRhythmicGroups(); - checkAccidentals(); - resolveBeaming(beamGrToResolve); - refresh(); +void TmeasureObject::update(int beamGrToResolve) +{ + updateRhythmicGroups(); + checkAccidentals(); + resolveBeaming(beamGrToResolve); + refresh(); } diff --git a/src/libs/core/score/tmeasureobject.h b/src/libs/core/score/tmeasureobject.h index 30e89ecd2aa46ff0d4b337e3d4b4f0c0e306ff9a..80c35ff9f2e2f4e32a6bd44eea88b09c73e1b152 100644 --- a/src/libs/core/score/tmeasureobject.h +++ b/src/libs/core/score/tmeasureobject.h @@ -19,11 +19,9 @@ #ifndef TMEASUREOBJECT_H #define TMEASUREOBJECT_H - #include "nootkacoreglobal.h" #include <QtCore/qobject.h> - class QQuickItem; class TscoreObject; class TstaffItem; @@ -32,205 +30,201 @@ class TnotePair; class TbeamObject; class Tnote; - -typedef QList<TnotePair*> Tpairs; - +typedef QList<TnotePair *> Tpairs; /** * @class TmeasureObject is an implementation of the measure */ class NOOTKACORE_EXPORT TmeasureObject : public QObject { - Q_OBJECT + Q_OBJECT - friend class TscoreObject; - friend class TstaffItem; - friend class TnoteItem; + friend class TscoreObject; + friend class TstaffItem; + friend class TnoteItem; public: - - explicit TmeasureObject(int nr = -1, TscoreObject* parent = nullptr); - ~TmeasureObject() override; - - int number() const { return m_number; } - void setNumber(int nr); - - /** - * Measure duration depending on time signature - * TODO Remove it and use TscoreObject::measureDuration() - */ - int duration() const { return m_duration; } - - /** - * Duration in measure starting from note @p id (where id is note number in the measure) - */ - int durationFrom(int id); - - /** - * Duration of notes before given @p it item. - * @ret 0 when no item was found - */ - int durationBefore(TnoteItem* it); - - /** - * Free 'rhythm space' remained in the measure - */ - int free() const { return m_free; } - - /** - * Adds list of @p count notes at the measure end starting from @p segmentId note number in the score - * Measure has to have already space for the whole list! - */ - void appendNewNotes(int segmentId, int count); - - /** - * Inserts given @p nList list of notes starting from @p startId - */ - void insertNotes(Tpairs& nList, int startId = 0); - - void insertNote(TnoteItem* afterItem); - - void removeNote(TnotePair* n); - - void removeLastNote(); - - TscoreObject* score() { return m_score; } - - TstaffItem* staff() { return m_staff; } - void setStaff(TstaffItem* st); - - int noteCount() { return m_notes.count(); } - TnotePair* note(int nr) { return m_notes[nr]; } - TnotePair* first() { return m_notes.first(); } - TnotePair* last() { return m_notes.last(); } - bool isEmpty() { return m_notes.isEmpty(); } - - /** - * Staff index of the first measure note - */ - int firstNoteId() const; - - /** - * Staff index of the last measure note - */ - int lastNoteId() const; - - /** - * Summarized width of all notes in this measure - */ - qreal allNotesWidth() { return m_allNotesWidth; } - - /** - * Sum of all rhythm spaces depending on rhythm values after every measure note - */ - qreal gapsSum() { return m_gapsSum; } - - /** - * Prints debug message with [number MEASURE] - */ - char debug(); + explicit TmeasureObject(int nr = -1, TscoreObject *parent = nullptr); + ~TmeasureObject() override; + + int number() const { return m_number; } + void setNumber(int nr); + + /** + * Measure duration depending on time signature + * TODO Remove it and use TscoreObject::measureDuration() + */ + int duration() const { return m_duration; } + + /** + * Duration in measure starting from note @p id (where id is note number in the measure) + */ + int durationFrom(int id); + + /** + * Duration of notes before given @p it item. + * @ret 0 when no item was found + */ + int durationBefore(TnoteItem *it); + + /** + * Free 'rhythm space' remained in the measure + */ + int free() const { return m_free; } + + /** + * Adds list of @p count notes at the measure end starting from @p segmentId note number in the score + * Measure has to have already space for the whole list! + */ + void appendNewNotes(int segmentId, int count); + + /** + * Inserts given @p nList list of notes starting from @p startId + */ + void insertNotes(Tpairs &nList, int startId = 0); + + void insertNote(TnoteItem *afterItem); + + void removeNote(TnotePair *n); + + void removeLastNote(); + + TscoreObject *score() { return m_score; } + + TstaffItem *staff() { return m_staff; } + void setStaff(TstaffItem *st); + + int noteCount() { return m_notes.count(); } + TnotePair *note(int nr) { return m_notes[nr]; } + TnotePair *first() { return m_notes.first(); } + TnotePair *last() { return m_notes.last(); } + bool isEmpty() { return m_notes.isEmpty(); } + + /** + * Staff index of the first measure note + */ + int firstNoteId() const; + + /** + * Staff index of the last measure note + */ + int lastNoteId() const; + + /** + * Summarized width of all notes in this measure + */ + qreal allNotesWidth() { return m_allNotesWidth; } + + /** + * Sum of all rhythm spaces depending on rhythm values after every measure note + */ + qreal gapsSum() { return m_gapsSum; } + + /** + * Prints debug message with [number MEASURE] + */ + char debug(); protected: - - void flush(); - - /** - * Sets appropriate @p setRhythmGroup of every note in the measure. - */ - void updateRhythmicGroups(); - void checkBarLine(); - - void meterChanged(); - - /** - * Updates @p m_allNotesWidth @p m_allGaps according to current measure content - */ - void refresh(); - - void checkAccidentals(); - - void keySignatureChanged(); - - qint8 accidState(int noteNr) { return m_accidsState[noteNr]; } - - /** - * Checks notes rhythms of group @p segmentId belongs to - * for 8ths and 16ths and crates beams (@p TbeamObject) if they occurs - * It can be called before @p TnoteObject creation. - * When beam was set or note was added to it, it returns number of that group - * or -1 if no beams were added - */ - int beamGroup(int segmentId); - - /** - * Fixes a beam if given note has it. - */ - void noteGoingRest(TnotePair* np); - - /** - * Beams given note with surrounding notes. WARNING: Given note has to be 8th or 16th - */ - void restGoingNote(TnotePair* np); - - /** - * - */ - void changeNoteDuration(TnotePair* np, const Tnote& newNote); - - /** - * Sets beams in measure notes starting from beam group of @p firstGroup - * till @p endGroup or through all groups if not set (-1). - */ - void resolveBeaming(int firstGroup, int endGroup = -1); - - /** - * Takes @p dur (duration) of notes at the measure end - * and creates a list of notes @p notesToOut to shift to the next measure. - * Also, splits last note when needed and ties with the next note. - * @p endNote is note number till taking out is performed (by default 0 - whole measure) - * Returned value is remaining duration that can't be obtained. - */ - int releaseAtEnd(int dur, Tpairs& notesToOut, int endNote = 0); - - /** - * Takes @p dur (duration) of notes at the measure beginning - * and packs them into @p notesToOut list. - */ - void releaseAtStart(int dur, Tpairs& notesToOut); - - /** - * squeezes extra note @p np silently, without invoking visual changes - */ - void insertSilently(int id, TnotePair* np); + void flush(); + + /** + * Sets appropriate @p setRhythmGroup of every note in the measure. + */ + void updateRhythmicGroups(); + void checkBarLine(); + + void meterChanged(); + + /** + * Updates @p m_allNotesWidth @p m_allGaps according to current measure content + */ + void refresh(); + + void checkAccidentals(); + + void keySignatureChanged(); + + qint8 accidState(int noteNr) { return m_accidsState[noteNr]; } + + /** + * Checks notes rhythms of group @p segmentId belongs to + * for 8ths and 16ths and crates beams (@p TbeamObject) if they occurs + * It can be called before @p TnoteObject creation. + * When beam was set or note was added to it, it returns number of that group + * or -1 if no beams were added + */ + int beamGroup(int segmentId); + + /** + * Fixes a beam if given note has it. + */ + void noteGoingRest(TnotePair *np); + + /** + * Beams given note with surrounding notes. WARNING: Given note has to be 8th or 16th + */ + void restGoingNote(TnotePair *np); + + /** + * + */ + void changeNoteDuration(TnotePair *np, const Tnote &newNote); + + /** + * Sets beams in measure notes starting from beam group of @p firstGroup + * till @p endGroup or through all groups if not set (-1). + */ + void resolveBeaming(int firstGroup, int endGroup = -1); + + /** + * Takes @p dur (duration) of notes at the measure end + * and creates a list of notes @p notesToOut to shift to the next measure. + * Also, splits last note when needed and ties with the next note. + * @p endNote is note number till taking out is performed (by default 0 - whole measure) + * Returned value is remaining duration that can't be obtained. + */ + int releaseAtEnd(int dur, Tpairs ¬esToOut, int endNote = 0); + + /** + * Takes @p dur (duration) of notes at the measure beginning + * and packs them into @p notesToOut list. + */ + void releaseAtStart(int dur, Tpairs ¬esToOut); + + /** + * squeezes extra note @p np silently, without invoking visual changes + */ + void insertSilently(int id, TnotePair *np); private: - void clearAccidState(); + void clearAccidState(); - /** - * Common routine that performs shifting @p notesToOut to the next measure (if not empty) - */ - void shiftReleased(Tpairs& notesToOut); + /** + * Common routine that performs shifting @p notesToOut to the next measure (if not empty) + */ + void shiftReleased(Tpairs ¬esToOut); - /** - * Common method calling the staff for notes from the next measure - * to fill this one. @p m_free has to be updated before. - */ - void fill(); + /** + * Common method calling the staff for notes from the next measure + * to fill this one. @p m_free has to be updated before. + */ + void fill(); - void update(int beamGrToResolve = 0); + void update(int beamGrToResolve = 0); private: - int m_number; - int m_duration; - TscoreObject *m_score; - TstaffItem *m_staff; - int m_free; - QList<TnotePair*> m_notes; - qint8 *m_firstInGr; /**< qint8 is sufficient - measure never has more than 127 notes */ - QQuickItem *m_barLine; - qreal m_allNotesWidth = 0.0; - qreal m_gapsSum = 0.0; - qint8 m_accidsState[7]; + int m_number; + int m_duration; + TscoreObject *m_score; + TstaffItem *m_staff; + int m_free; + QList<TnotePair *> m_notes; + qint8 *m_firstInGr; /**< qint8 is sufficient - measure never has more than 127 notes */ + QQuickItem *m_barLine; + qreal m_allNotesWidth = 0.0; + qreal m_gapsSum = 0.0; + qint8 m_accidsState[7]; }; #endif // TMEASUREOBJECT_H diff --git a/src/libs/core/score/tmelodypreview.cpp b/src/libs/core/score/tmelodypreview.cpp index 6f7aa0b71210adbc5263e5255df748b644bb147b..8828b408d2cfb2be596123831caf15a770ad0466 100644 --- a/src/libs/core/score/tmelodypreview.cpp +++ b/src/libs/core/score/tmelodypreview.cpp @@ -17,66 +17,66 @@ ***************************************************************************/ #include "tmelodypreview.h" -#include "tscoreobject.h" #include "music/tmelody.h" +#include "tscoreobject.h" - -TmelodyPreview::TmelodyPreview(QQuickItem *parent) : - QQuickItem(parent) -{} - - -QString TmelodyPreview::composer() const { - return m_melody ? m_melody->composer() : QString(); +TmelodyPreview::TmelodyPreview(QQuickItem *parent) + : QQuickItem(parent) +{ } - -QString TmelodyPreview::title() const { - return m_melody ? m_melody->title() : QString(); +QString TmelodyPreview::composer() const +{ + return m_melody ? m_melody->composer() : QString(); } - -void TmelodyPreview::setScore(TscoreObject* sc) { - m_score = sc; - setSelectReadOnly(m_selectReadOnly); +QString TmelodyPreview::title() const +{ + return m_melody ? m_melody->title() : QString(); } - -QVariant TmelodyPreview::melody() { - return QVariant::fromValue(m_melody); +void TmelodyPreview::setScore(TscoreObject *sc) +{ + m_score = sc; + setSelectReadOnly(m_selectReadOnly); } - -void TmelodyPreview::setMelody(QVariant v) { - auto m = qvariant_cast<Tmelody*>(v); - if (m != m_melody) { - m_melody = m; - if (m_melody && m_score) - m_score->setMelody(m_melody); - emit melodyChanged(); - } +QVariant TmelodyPreview::melody() +{ + return QVariant::fromValue(m_melody); } - -bool TmelodyPreview::selectReadOnly() const { - return m_score ? m_score->selectInReadOnly() : false; +void TmelodyPreview::setMelody(QVariant v) +{ + auto m = qvariant_cast<Tmelody *>(v); + if (m != m_melody) { + m_melody = m; + if (m_melody && m_score) + m_score->setMelody(m_melody); + emit melodyChanged(); + } } +bool TmelodyPreview::selectReadOnly() const +{ + return m_score ? m_score->selectInReadOnly() : false; +} -void TmelodyPreview::setSelectReadOnly(bool selRO) { - if (selRO != m_selectReadOnly || (m_score && m_score->selectInReadOnly() != selRO)) { - m_selectReadOnly = selRO; - if (m_score) { - m_score->setSelectInReadOnly(selRO); - if (selRO) - connect(m_score, &TscoreObject::readOnlyNoteClicked, this, &TmelodyPreview::readOnlyNoteClicked, Qt::UniqueConnection); - emit selectReadOnlyChanged(); +void TmelodyPreview::setSelectReadOnly(bool selRO) +{ + if (selRO != m_selectReadOnly || (m_score && m_score->selectInReadOnly() != selRO)) { + m_selectReadOnly = selRO; + if (m_score) { + m_score->setSelectInReadOnly(selRO); + if (selRO) + connect(m_score, &TscoreObject::readOnlyNoteClicked, this, &TmelodyPreview::readOnlyNoteClicked, Qt::UniqueConnection); + emit selectReadOnlyChanged(); + } } - } } - -void TmelodyPreview::reload() { - if (m_melody && m_score) - m_score->setMelody(m_melody); +void TmelodyPreview::reload() +{ + if (m_melody && m_score) + m_score->setMelody(m_melody); } diff --git a/src/libs/core/score/tmelodypreview.h b/src/libs/core/score/tmelodypreview.h index d82d643d7e0bbd480acea820379face2922ae304..6d0c918b6fcc42e81cd11f30d48df32fcb36af63 100644 --- a/src/libs/core/score/tmelodypreview.h +++ b/src/libs/core/score/tmelodypreview.h @@ -19,15 +19,12 @@ #ifndef TMELODYPREVIEW_H #define TMELODYPREVIEW_H - -#include <nootkacoreglobal.h> #include <QtQuick/qquickitem.h> - +#include <nootkacoreglobal.h> class Tmelody; class TscoreObject; - /** * C++ part of QML item with entire melody preview. * It is @p TpopupDialog with @p TscoreObject inside. @@ -35,42 +32,41 @@ class TscoreObject; */ class NOOTKACORE_EXPORT TmelodyPreview : public QQuickItem { - Q_OBJECT + Q_OBJECT - Q_PROPERTY(QString title READ title NOTIFY melodyChanged) - Q_PROPERTY(QString composer READ composer NOTIFY melodyChanged) - Q_PROPERTY(QVariant melody READ melody WRITE setMelody NOTIFY melodyChanged) - Q_PROPERTY(TscoreObject* score READ score WRITE setScore) - Q_PROPERTY(bool selectReadOnly READ selectReadOnly WRITE setSelectReadOnly NOTIFY selectReadOnlyChanged) + Q_PROPERTY(QString title READ title NOTIFY melodyChanged) + Q_PROPERTY(QString composer READ composer NOTIFY melodyChanged) + Q_PROPERTY(QVariant melody READ melody WRITE setMelody NOTIFY melodyChanged) + Q_PROPERTY(TscoreObject *score READ score WRITE setScore) + Q_PROPERTY(bool selectReadOnly READ selectReadOnly WRITE setSelectReadOnly NOTIFY selectReadOnlyChanged) public: - TmelodyPreview(QQuickItem* parent = nullptr); + TmelodyPreview(QQuickItem *parent = nullptr); - QString title() const; - QString composer() const; + QString title() const; + QString composer() const; - QVariant melody(); - void setMelody(QVariant v); + QVariant melody(); + void setMelody(QVariant v); - TscoreObject* score() { return m_score; } - void setScore(TscoreObject* sc); + TscoreObject *score() { return m_score; } + void setScore(TscoreObject *sc); - bool selectReadOnly() const; - void setSelectReadOnly(bool selRO); + bool selectReadOnly() const; + void setSelectReadOnly(bool selRO); - Q_INVOKABLE void reload(); + Q_INVOKABLE void reload(); signals: - void melodyChanged(); - void selectReadOnlyChanged(); - void readOnlyNoteClicked(int noteId); + void melodyChanged(); + void selectReadOnlyChanged(); + void readOnlyNoteClicked(int noteId); private: - QString m_nr; - Tmelody *m_melody = nullptr; - TscoreObject *m_score = nullptr; - bool m_selectReadOnly = false; + QString m_nr; + Tmelody *m_melody = nullptr; + TscoreObject *m_score = nullptr; + bool m_selectReadOnly = false; }; - #endif // TMELODYPREVIEW_H diff --git a/src/libs/core/score/tnoteitem.cpp b/src/libs/core/score/tnoteitem.cpp index 5e2332b94aa110c77addf24f624d9dc218cd6d8a..0c5793975d0efc8619623122816a6042e0783fc8 100644 --- a/src/libs/core/score/tnoteitem.cpp +++ b/src/libs/core/score/tnoteitem.cpp @@ -17,20 +17,19 @@ ***************************************************************************/ #include "tnoteitem.h" -#include "tscoreobject.h" -#include "tstaffitem.h" -#include "tmeasureobject.h" +#include "music/tnote.h" #include "tbeamobject.h" +#include "tmeasureobject.h" #include "tnotepair.h" -#include "music/tnote.h" +#include "tscoreobject.h" +#include "tstaffitem.h" +#include <QtCore/qtimer.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> -#include <QtCore/qtimer.h> -#include <QtCore/qdebug.h> #include "checktime.h" - +#include <QtCore/qdebug.h> /** * Width of every accidental for Scorek font of pixel size set to 7.0 @@ -38,139 +37,138 @@ */ // static const qreal accWidthTable[6] = { 2.78125, 1.671875, 0.0, 1.765625, 2.03125, 2.34375 }; - - /** - * WARNING This is for comparison purposes only. - * Do not call any method behind this pointer - */ -TnoteItem* TnoteItem::m_heldNote = nullptr; +/** + * WARNING This is for comparison purposes only. + * Do not call any method behind this pointer + */ +TnoteItem *TnoteItem::m_heldNote = nullptr; QElapsedTimer TnoteItem::m_touchDuration = QElapsedTimer(); - -QString tieDebug(Trhythm::Etie t) { - switch (t) { +QString tieDebug(Trhythm::Etie t) +{ + switch (t) { case Trhythm::e_tieEnd: - return "TE"; + return "TE"; case Trhythm::e_tieStart: - return "TS"; + return "TS"; case Trhythm::e_tieCont: - return "TC"; + return "TC"; default: - return QString(); - } + return QString(); + } } - -TnoteItem::TnoteItem(TstaffItem* staffObj, TnotePair* wrapper) : - QQuickItem(staffObj), - m_staff(staffObj), - m_wrapper(wrapper), - m_stemHeight(STEM_HEIGHT) +TnoteItem::TnoteItem(TstaffItem *staffObj, TnotePair *wrapper) + : QQuickItem(staffObj) + , m_staff(staffObj) + , m_wrapper(wrapper) + , m_stemHeight(STEM_HEIGHT) { - setParent(m_staff->score()); // to avoid deleting with parent staff - m_note = new Tnote(); - - m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle {}", QUrl()); - m_stem = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_stem->setParentItem(this); - m_stem->setWidth(0.3); - m_stem->setHeight(m_stemHeight); - m_stem->setVisible(false); - - for (int i = 0; i < 7; ++i) { - m_upLines << createAddLine(); - m_loLines << createAddLine(); - } + setParent(m_staff->score()); // to avoid deleting with parent staff + m_note = new Tnote(); + + m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle {}", QUrl()); + m_stem = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_stem->setParentItem(this); + m_stem->setWidth(0.3); + m_stem->setHeight(m_stemHeight); + m_stem->setVisible(false); + + for (int i = 0; i < 7; ++i) { + m_upLines << createAddLine(); + m_loLines << createAddLine(); + } - m_staff->score()->component()->setData("import QtQuick 2.9; Text { font { family: \"Scorek\"; pixelSize: 7 }}", QUrl()); - m_head = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_head->setParentItem(this); + m_staff->score()->component()->setData("import QtQuick 2.9; Text { font { family: \"Scorek\"; pixelSize: 7 }}", QUrl()); + m_head = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_head->setParentItem(this); - m_alter = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_alter->setParentItem(m_head); - connect(m_alter, &QQuickItem::widthChanged, this, &TnoteItem::alterWidthChanged); + m_alter = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_alter->setParentItem(m_head); + connect(m_alter, &QQuickItem::widthChanged, this, &TnoteItem::alterWidthChanged); - m_flag = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_flag->setParentItem(m_stem); - m_flag->setX(0.1); + m_flag = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_flag->setParentItem(m_stem); + m_flag->setX(0.1); -// m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 2 }}", QUrl()); -// m_debug = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); -// m_debug->setParentItem(this); + // m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 2 }}", QUrl()); + // m_debug = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); + // m_debug->setParentItem(this); - setColor(qApp->palette().text().color()); - setHeight(staffObj->height()); - setAcceptHoverEvents(true); - setZ(10); - setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); + setColor(qApp->palette().text().color()); + setHeight(staffObj->height()); + setAcceptHoverEvents(true); + setZ(10); + setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); - updateNoteHead(); - connect(qApp, &QGuiApplication::paletteChanged, this, [=]{ setColor(qApp->palette().text().color()); }); + updateNoteHead(); + connect(qApp, &QGuiApplication::paletteChanged, this, [=] { + setColor(qApp->palette().text().color()); + }); } - -TnoteItem::~TnoteItem() { -// qDebug() << debug() << "is going deleted"; - delete m_note; +TnoteItem::~TnoteItem() +{ + // qDebug() << debug() << "is going deleted"; + delete m_note; } - -int TnoteItem::index() const { - return m_wrapper->index(); +int TnoteItem::index() const +{ + return m_wrapper->index(); } - -void TnoteItem::setStaff(TstaffItem* staffObj) { - if (staffObj != m_staff) { - m_staff = staffObj; - if (m_staff) { - setParentItem(m_staff); - if (m_wrapper->beam() && m_wrapper->beam()->last()->item() == this) - m_wrapper->beam()->changeStaff(m_staff); - } else { - setParentItem(nullptr); - } - if (m_name) - m_name->setParentItem(parentItem()); - } else - qDebug() << debug() << "has staff set already"; +void TnoteItem::setStaff(TstaffItem *staffObj) +{ + if (staffObj != m_staff) { + m_staff = staffObj; + if (m_staff) { + setParentItem(m_staff); + if (m_wrapper->beam() && m_wrapper->beam()->last()->item() == this) + m_wrapper->beam()->changeStaff(m_staff); + } else { + setParentItem(nullptr); + } + if (m_name) + m_name->setParentItem(parentItem()); + } else + qDebug() << debug() << "has staff set already"; } - -void TnoteItem::setMeasure(TmeasureObject* m) { - m_measure = m; +void TnoteItem::setMeasure(TmeasureObject *m) +{ + m_measure = m; } - -void TnoteItem::setStemHeight(qreal sh) { - m_stem->setHeight(sh); +void TnoteItem::setStemHeight(qreal sh) +{ + m_stem->setHeight(sh); } - -void TnoteItem::setColor(const QColor& c) { - m_head->setProperty("color", c); - m_alter->setProperty("color", c); - m_flag->setProperty("color", c); - m_stem->setProperty("color", c); - for (auto line : qAsConst(m_upLines)) - line->setProperty("color", c); - for (auto line : qAsConst(m_loLines)) - line->setProperty("color", c); - for (auto line : qAsConst(m_underLoLines)) - line->setProperty("color", c); - if (m_tie) - m_tie->setProperty("color", c); - if (m_name) - m_name->setProperty("color", c); - if (m_stringNumber) - m_stringNumber->setProperty("color", c); - if (m_bowing) - m_bowing->setProperty("color", c); - if (m_fingerNumber) - m_fingerNumber->setProperty("color", c); +void TnoteItem::setColor(const QColor &c) +{ + m_head->setProperty("color", c); + m_alter->setProperty("color", c); + m_flag->setProperty("color", c); + m_stem->setProperty("color", c); + for (auto line : qAsConst(m_upLines)) + line->setProperty("color", c); + for (auto line : qAsConst(m_loLines)) + line->setProperty("color", c); + for (auto line : qAsConst(m_underLoLines)) + line->setProperty("color", c); + if (m_tie) + m_tie->setProperty("color", c); + if (m_name) + m_name->setProperty("color", c); + if (m_stringNumber) + m_stringNumber->setProperty("color", c); + if (m_bowing) + m_bowing->setProperty("color", c); + if (m_fingerNumber) + m_fingerNumber->setProperty("color", c); } - /** * @p pitch/octave * - note pos Y @@ -191,376 +189,383 @@ void TnoteItem::setColor(const QColor& c) { * - stem visibility * - width */ -void TnoteItem::setNote(const Tnote& n) { - bool updateHead = n.rhythm() != m_note->rhythm() || n.isRest() != m_note->isRest() || n.hasDot() != m_note->hasDot(); - bool fixBeam = n.isRest() != m_note->isRest(); - bool updateStem = updateHead || fixBeam || ((n.rtm.beam() != Trhythm::e_noBeam) != (m_note->rtm.beam() != Trhythm::e_noBeam)) - || (n.rtm.stemDown() != m_note->rtm.stemDown() || m_stem->height() != m_stemHeight) - || n.onUpperStaff() != m_note->onUpperStaff(); - bool updateTie = n.rtm.tie() != m_note->rtm.tie(); - - *m_note = n; - - if (fixBeam) { - if (m_note->isRest()) { - if (m_wrapper->beam()) - m_measure->noteGoingRest(m_wrapper); - } else { - if (m_note->rhythm() > Trhythm::Quarter) - m_measure->restGoingNote(m_wrapper); +void TnoteItem::setNote(const Tnote &n) +{ + bool updateHead = n.rhythm() != m_note->rhythm() || n.isRest() != m_note->isRest() || n.hasDot() != m_note->hasDot(); + bool fixBeam = n.isRest() != m_note->isRest(); + bool updateStem = updateHead || fixBeam || ((n.rtm.beam() != Trhythm::e_noBeam) != (m_note->rtm.beam() != Trhythm::e_noBeam)) + || (n.rtm.stemDown() != m_note->rtm.stemDown() || m_stem->height() != m_stemHeight) || n.onUpperStaff() != m_note->onUpperStaff(); + bool updateTie = n.rtm.tie() != m_note->rtm.tie(); + + *m_note = n; + + if (fixBeam) { + if (m_note->isRest()) { + if (m_wrapper->beam()) + m_measure->noteGoingRest(m_wrapper); + } else { + if (m_note->rhythm() > Trhythm::Quarter) + m_measure->restGoingNote(m_wrapper); + } } - } - if (updateHead) - updateNoteHead(); + if (updateHead) + updateNoteHead(); - int oldNotePos = static_cast<int>(m_notePosY); - if (m_note->isRest()) - m_notePosY = staff()->upperLine() + (m_note->onUpperStaff() ? 0.0 : 22.0) + (m_note->rhythm() == Trhythm::Whole ? 2.0 : 4.0); - else { - if (m_note->isValid()) { - m_notePosY = getHeadY(n); - } else { - if (staff()->score()->singleNote()) { - m_notePosY = 0.0; - oldNotePos = -1.0; // cheat the logic to force add lines check - } else // no clef - rhythm only, note placed at lower staff field - m_notePosY = staff()->upperLine() + 7.0; + int oldNotePos = static_cast<int>(m_notePosY); + if (m_note->isRest()) + m_notePosY = staff()->upperLine() + (m_note->onUpperStaff() ? 0.0 : 22.0) + (m_note->rhythm() == Trhythm::Whole ? 2.0 : 4.0); + else { + if (m_note->isValid()) { + m_notePosY = getHeadY(n); + } else { + if (staff()->score()->singleNote()) { + m_notePosY = 0.0; + oldNotePos = -1.0; // cheat the logic to force add lines check + } else // no clef - rhythm only, note placed at lower staff field + m_notePosY = staff()->upperLine() + 7.0; + } } - } - if (m_notePosY < 2.0 || m_notePosY > height() - 1.0) - m_notePosY = 0.0; - - if (oldNotePos != static_cast<int>(m_notePosY)) { - if (m_notePosY) { - m_head->setVisible(true); - m_head->setY(m_notePosY - 15.0); - } else - m_head->setVisible(false); - - checkAddLinesVisibility(); - updateStem = true; - } + if (m_notePosY < 2.0 || m_notePosY > height() - 1.0) + m_notePosY = 0.0; - if (updateStem) - checkStem(); - - updateAlter(); - updateWidth(); - - if (updateTie) - checkTie(); + if (oldNotePos != static_cast<int>(m_notePosY)) { + if (m_notePosY) { + m_head->setVisible(true); + m_head->setY(m_notePosY - 15.0); + } else + m_head->setVisible(false); - if (oldNotePos != static_cast<int>(m_notePosY)) - emit notePosYchanged(); + checkAddLinesVisibility(); + updateStem = true; + } - updateNamePos(); + if (updateStem) + checkStem(); - if (m_bowing && m_wrapper) - setBowing(static_cast<EbowDirection>(m_wrapper->techicalData().bowing())); + updateAlter(); + updateWidth(); -// updateDebug(); -// qDebug() << debug() << "set note" << m_note->toText() << m_note->rtm.string() << "note pos" << m_notePosY << "width:" << width(); -} + if (updateTie) + checkTie(); + if (oldNotePos != static_cast<int>(m_notePosY)) + emit notePosYchanged(); -quint32 TnoteItem::technical() const { return m_wrapper ? m_wrapper->technical() : 255; } + updateNamePos(); + if (m_bowing && m_wrapper) + setBowing(static_cast<EbowDirection>(m_wrapper->techicalData().bowing())); -void TnoteItem::setTechnical(quint32 tech) { - if (m_wrapper) - m_wrapper->setTechnical(tech); + // updateDebug(); + // qDebug() << debug() << "set note" << m_note->toText() << m_note->rtm.string() << "note pos" << m_notePosY << "width:" << width(); } - -void TnoteItem::setX(qreal xx) { - if (staff()->score()->singleNote()) - QQuickItem::setX(xx); - else { - updateTieScale(); - QQuickItem::setX(xx + (m_alter->width())); - if (m_wrapper->beam() && m_wrapper->beam()->last()->item() == this) - m_wrapper->beam()->last()->beam()->drawBeam(); - if (m_name) - m_name->setX(x() - m_alter->width() + (width() - m_name->width()) / 2.0); - emit rightXChanged(); - } +quint32 TnoteItem::technical() const +{ + return m_wrapper ? m_wrapper->technical() : 255; } - -Trhythm TnoteItem::rhythm() const { - return m_note->rtm; +void TnoteItem::setTechnical(quint32 tech) +{ + if (m_wrapper) + m_wrapper->setTechnical(tech); } - -qreal TnoteItem::rightX() const { - return x() + width() + staff()->gapFactor() * rhythmFactor() - m_alter->width(); +void TnoteItem::setX(qreal xx) +{ + if (staff()->score()->singleNote()) + QQuickItem::setX(xx); + else { + updateTieScale(); + QQuickItem::setX(xx + (m_alter->width())); + if (m_wrapper->beam() && m_wrapper->beam()->last()->item() == this) + m_wrapper->beam()->last()->beam()->drawBeam(); + if (m_name) + m_name->setX(x() - m_alter->width() + (width() - m_name->width()) / 2.0); + emit rightXChanged(); + } } +Trhythm TnoteItem::rhythm() const +{ + return m_note->rtm; +} -bool TnoteItem::hasTie() const { - return m_note->rtm.tie() > Trhythm::e_tieStart; +qreal TnoteItem::rightX() const +{ + return x() + width() + staff()->gapFactor() * rhythmFactor() - m_alter->width(); } +bool TnoteItem::hasTie() const +{ + return m_note->rtm.tie() > Trhythm::e_tieStart; +} -void TnoteItem::setHeight(qreal hh) { - if (hh != height()) { - QQuickItem::setHeight(hh); - for (int l = 0; l < 7; ++l) { - m_upLines[l]->setY(2 * (l + 1) - 0.1); - m_loLines[l]->setY(staff()->upperLine() + 10.0 + 2 * l - 0.1); - } - if (staff()->isPianoStaff()) { - if (m_underLoLines.isEmpty()) { - m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle {}", QUrl()); - for (int l = 0; l < 2; ++l) { - auto line = createAddLine(); - line->setY(m_staff->upperLine() + 32.0 + l * 2.0 - 0.1); - line->setProperty("color", m_head->property("color")); - m_underLoLines << line; +void TnoteItem::setHeight(qreal hh) +{ + if (hh != height()) { + QQuickItem::setHeight(hh); + for (int l = 0; l < 7; ++l) { + m_upLines[l]->setY(2 * (l + 1) - 0.1); + m_loLines[l]->setY(staff()->upperLine() + 10.0 + 2 * l - 0.1); } - } + if (staff()->isPianoStaff()) { + if (m_underLoLines.isEmpty()) { + m_staff->score()->component()->setData("import QtQuick 2.9; Rectangle {}", QUrl()); + for (int l = 0; l < 2; ++l) { + auto line = createAddLine(); + line->setY(m_staff->upperLine() + 32.0 + l * 2.0 - 0.1); + line->setProperty("color", m_head->property("color")); + m_underLoLines << line; + } + } + } + checkAddLinesVisibility(); } - checkAddLinesVisibility(); - } } +qreal TnoteItem::rhythmFactor() const +{ + if (m_note->rhythm() == Trhythm::NoRhythm) + return 0.75; -qreal TnoteItem::rhythmFactor() const { - if (m_note->rhythm() == Trhythm::NoRhythm) - return 0.75; - - /** - * Static array with space definitions for each rhythm value - */ - const qreal rtmGapArray[5][3] = { -// | bare | dot | triplet | - { 5.0, 6.0, 4.5}, // whole note - { 4.0, 5.0, 3.3}, // half note - { 2.0, 2.5, 1.3}, // quarter note - { 1.0, 1.5, 0.3}, // eighth note - { 0.15, 0.5, 0.0} // sixteenth note - }; + /** + * Static array with space definitions for each rhythm value + */ + const qreal rtmGapArray[5][3] = { + // | bare | dot | triplet | + {5.0, 6.0, 4.5}, // whole note + {4.0, 5.0, 3.3}, // half note + {2.0, 2.5, 1.3}, // quarter note + {1.0, 1.5, 0.3}, // eighth note + {0.15, 0.5, 0.0} // sixteenth note + }; - int add = m_note->hasDot() ? 1 : (m_note->isTriplet() ? 2 : 0); - return rtmGapArray[static_cast<int>(m_note->rhythm()) - 1][add]; + int add = m_note->hasDot() ? 1 : (m_note->isTriplet() ? 2 : 0); + return rtmGapArray[static_cast<int>(m_note->rhythm()) - 1][add]; } - -char TnoteItem::debug() { - QTextStream o(stdout); - o << " \033[01;29m[NOTE " << index() << "]\033[01;00m"; - return 32; // fake +char TnoteItem::debug() +{ + QTextStream o(stdout); + o << " \033[01;29m[NOTE " << index() << "]\033[01;00m"; + return 32; // fake } - -void TnoteItem::shiftHead(qreal shift) { - if (shift != m_head->x()) { - m_head->setX(shift); - for (int l = 0; l < 7; ++l) { - m_upLines[l]->setX(-0.5 + shift); - m_loLines[l]->setX(-0.5 + shift); +void TnoteItem::shiftHead(qreal shift) +{ + if (shift != m_head->x()) { + m_head->setX(shift); + for (int l = 0; l < 7; ++l) { + m_upLines[l]->setX(-0.5 + shift); + m_loLines[l]->setX(-0.5 + shift); + } + for (int l = 0; l < m_underLoLines.count(); ++l) + m_underLoLines[l]->setX(-0.5 + shift); } - for (int l = 0; l < m_underLoLines.count(); ++l) - m_underLoLines[l]->setX(-0.5 + shift); - } } - -void TnoteItem::checkTie() { - if (m_tie && (m_note->rtm.tie() == Trhythm::e_noTie || m_note->rtm.tie() == Trhythm::e_tieEnd)) { - delete m_tie; - m_tie = nullptr; - } else if (m_tie == nullptr && (m_note->rtm.tie() == Trhythm::e_tieStart || m_note->rtm.tie() == Trhythm::e_tieCont)) { - QQmlComponent comp(m_staff->score()->qmlEngine(), QUrl(QStringLiteral("qrc:/score/Tie.qml"))); - m_tie = qobject_cast<QQuickItem*>(comp.create()); - m_tie->setParentItem(m_head); - m_tie->setProperty("color", qApp->palette().text().color()); - updateTieScale(); - m_tie->setX(m_head->width() - 0.75); - } -// updateDebug(); +void TnoteItem::checkTie() +{ + if (m_tie && (m_note->rtm.tie() == Trhythm::e_noTie || m_note->rtm.tie() == Trhythm::e_tieEnd)) { + delete m_tie; + m_tie = nullptr; + } else if (m_tie == nullptr && (m_note->rtm.tie() == Trhythm::e_tieStart || m_note->rtm.tie() == Trhythm::e_tieCont)) { + QQmlComponent comp(m_staff->score()->qmlEngine(), QUrl(QStringLiteral("qrc:/score/Tie.qml"))); + m_tie = qobject_cast<QQuickItem *>(comp.create()); + m_tie->setParentItem(m_head); + m_tie->setProperty("color", qApp->palette().text().color()); + updateTieScale(); + m_tie->setX(m_head->width() - 0.75); + } + // updateDebug(); } - -void TnoteItem::updateDebug() { -// m_debug->setProperty("text", QString("%1 %2").arg(index()).arg(tieDebug(m_note->rtm.tie()))); -// m_debug->setProperty("text", QString("%1/%2(%3) %4").arg(index()).arg(m_measure->number() + 1).arg(m_wrapper->rhythmGroup()).arg(tieDebug(m_note->rtm.tie()))); -// m_debug->setProperty("text", QString("%1(%2|%3) %4").arg(index()).arg(m_note->rtm.string()).arg(m_wrapper->rhythmGroup()).arg(tieDebug(m_note->rtm.tie()))); -// m_debug->setY(height() - (2 + index() % 2) * m_debug->height()); +void TnoteItem::updateDebug() +{ + // m_debug->setProperty("text", QString("%1 %2").arg(index()).arg(tieDebug(m_note->rtm.tie()))); + // m_debug->setProperty("text", QString("%1/%2(%3) %4").arg(index()).arg(m_measure->number() + + // 1).arg(m_wrapper->rhythmGroup()).arg(tieDebug(m_note->rtm.tie()))); m_debug->setProperty("text", QString("%1(%2|%3) + // %4").arg(index()).arg(m_note->rtm.string()).arg(m_wrapper->rhythmGroup()).arg(tieDebug(m_note->rtm.tie()))); m_debug->setY(height() - (2 + index() % 2) + // * m_debug->height()); } - -qreal TnoteItem::tieWidth() { - return qMax(1.5, - staff()->gapFactor() * rhythmFactor() - + (this == m_measure->last()->item() ? 1.5 : 0.0) + (m_note->rtm.stemDown() ? 1.5 : m_flag->width() + 1.3)); +qreal TnoteItem::tieWidth() +{ + return qMax(1.5, + staff()->gapFactor() * rhythmFactor() + (this == m_measure->last()->item() ? 1.5 : 0.0) + + (m_note->rtm.stemDown() ? 1.5 : m_flag->width() + 1.3)); } - -QPointF TnoteItem::stemTop() { - return mapToItem(parentItem(), QPointF(m_stem->x(), m_stem->y() + (m_note->rtm.stemDown() ? m_stem->height() : 0.0))); +QPointF TnoteItem::stemTop() +{ + return mapToItem(parentItem(), QPointF(m_stem->x(), m_stem->y() + (m_note->rtm.stemDown() ? m_stem->height() : 0.0))); } - /** * When set to @p TRUE - creates @p m_name object, which belongs to parent staff (due to issues with blocking hover events over this name text item) * @p updateNamePos() method takes care about @p y() coordinate, * but @p x() has to be set when this @p TnoteObject changes its X coordinate */ -void TnoteItem::setNoteNameVisible(bool nameVisible) { - if (nameVisible) { - if (!m_name) { - m_staff->score()->component()->setData( - "import QtQuick 2.9; Text { font { pixelSize: 12; family: \"Scorek\" }" - "transformOrigin: Item.Top; scale: 0.25; textFormat: Text.PlainText; style: Text.Outline }", QUrl()); - m_name = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_name->setParentItem(parentItem()); - m_name->setProperty("color", qApp->palette().text().color()); - m_name->setProperty("styleColor", m_measure->score()->nameColor()); - updateNamePos(); - } - } else { - if (m_name) { - delete m_name; - m_name = nullptr; - } - } +void TnoteItem::setNoteNameVisible(bool nameVisible) +{ + if (nameVisible) { + if (!m_name) { + m_staff->score()->component()->setData( + "import QtQuick 2.9; Text { font { pixelSize: 12; family: \"Scorek\" }" + "transformOrigin: Item.Top; scale: 0.25; textFormat: Text.PlainText; style: Text.Outline }", + QUrl()); + m_name = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_name->setParentItem(parentItem()); + m_name->setProperty("color", qApp->palette().text().color()); + m_name->setProperty("styleColor", m_measure->score()->nameColor()); + updateNamePos(); + } + } else { + if (m_name) { + delete m_name; + m_name = nullptr; + } + } } - /** * Used glyphs are: * - note heads: @p 0xf4be @p 0xf4bd (half and black over-sized) and @p 0xf486 (whole smaller) * - rests: starts form 0xe4e3 (whole) */ -QString TnoteItem::getHeadText(const Trhythm& r) { - if (r.rhythm() == Trhythm::NoRhythm) - return QStringLiteral("\uf4be"); // just black note-head - - if (r.isRest()) - return QString(QChar(0xe4e2 + static_cast<int>(r.rhythm()))); - else { - if (r.rhythm() == Trhythm::Whole) - return QStringLiteral("\uf468"); - else if (r.rhythm() == Trhythm::Half) - return QStringLiteral("\uf4bd"); - else - return QStringLiteral("\uf4be"); - } -} - - -QString TnoteItem::unicodeGlyphArray(int alter) { - static const QString accCharTable[6] = { - QStringLiteral("\ue264"), // [0] = bb - double flat - QStringLiteral("\ue260"), // [1] = b - flat - QString(), // [2] = none - QStringLiteral("\ue262"), // [3] = # - sharp - QStringLiteral("\ue263"), // [4] = x - double sharp - QStringLiteral("\ue261") // [5] = neutral - }; - return accCharTable[alter + 2]; -} - - -QString TnoteItem::extraAccidString(int alter) { - switch (alter) { - case -2: return QStringLiteral("\ue273"); - case -1: return QStringLiteral("\ue271"); - case 1: return QStringLiteral("\ue270"); - case 2: return QStringLiteral("\ue272"); - default: return QString(); - } -} - - -void TnoteItem::setStringNumber(int strNr) { - if (!m_stringNumber && strNr > 0 && strNr < 7) { - m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 4; family: \"Nootka\" } }", QUrl()); - m_stringNumber = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_stringNumber->setParentItem(this); - } - if (strNr > 0 && strNr < 7) { - m_stringNumber->setProperty("text", QString::number(strNr)); - m_stringNumber->setX((width() - m_stringNumber->width()) / 2.0); - // TODO set Y position - m_stringNumber->setVisible(true); - } else { - if (m_stringNumber) - m_stringNumber->setVisible(false); - } -} - - -TnoteItem::EbowDirection TnoteItem::bowing() const { - return static_cast<EbowDirection>(m_wrapper->techicalData().bowing()); -} - - -void TnoteItem::setBowing(EbowDirection bowDir) { - if (!m_bowing && bowDir != BowUndefined) { - m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 5; family: \"Scorek\" } }", QUrl()); - m_bowing = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - m_bowing->setParentItem(this); - } - if (bowDir != BowUndefined) { - qreal bowY = 0.0; - if (m_note->onUpperStaff()) { - if (m_notePosY < m_staff->upperLine() - 2.0) // high pitch notes, above staff - bowY = m_staff->upperLine(); // bow below staff - else if (m_notePosY < m_staff->upperLine() + 1.0) // not that high but still above staff - bowY = m_notePosY - 12.5; // above note head - else // on the staff or below - bowY = m_staff->upperLine() - 12.0; // above staff - } else { - if (m_notePosY < m_staff->upperLine() + 24.0) - bowY = m_staff->upperLine() + 21.0; // below lower staff - else - bowY = m_staff->upperLine() + 9.0; // above lower staff - } - m_bowing->setProperty("text", QString(QChar(0xe610 + (bowDir == BowDown ? 0 : 2)))); - m_bowing->setX((width() - m_bowing->width()) / 2.0); - m_bowing->setY(bowY); - m_bowing->setVisible(true); - } else { - if (m_bowing) - m_bowing->setVisible(false); - } - m_wrapper->techicalData().setBowing(static_cast<Ttechnical::EbowDirection>(bowDir)); +QString TnoteItem::getHeadText(const Trhythm &r) +{ + if (r.rhythm() == Trhythm::NoRhythm) + return QStringLiteral("\uf4be"); // just black note-head + + if (r.isRest()) + return QString(QChar(0xe4e2 + static_cast<int>(r.rhythm()))); + else { + if (r.rhythm() == Trhythm::Whole) + return QStringLiteral("\uf468"); + else if (r.rhythm() == Trhythm::Half) + return QStringLiteral("\uf4bd"); + else + return QStringLiteral("\uf4be"); + } } +QString TnoteItem::unicodeGlyphArray(int alter) +{ + static const QString accCharTable[6] = { + QStringLiteral("\ue264"), // [0] = bb - double flat + QStringLiteral("\ue260"), // [1] = b - flat + QString(), // [2] = none + QStringLiteral("\ue262"), // [3] = # - sharp + QStringLiteral("\ue263"), // [4] = x - double sharp + QStringLiteral("\ue261") // [5] = neutral + }; + return accCharTable[alter + 2]; +} + +QString TnoteItem::extraAccidString(int alter) +{ + switch (alter) { + case -2: + return QStringLiteral("\ue273"); + case -1: + return QStringLiteral("\ue271"); + case 1: + return QStringLiteral("\ue270"); + case 2: + return QStringLiteral("\ue272"); + default: + return QString(); + } +} -void TnoteItem::setFingerNumber(int fiNr) +void TnoteItem::setStringNumber(int strNr) { - Q_UNUSED(fiNr) + if (!m_stringNumber && strNr > 0 && strNr < 7) { + m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 4; family: \"Nootka\" } }", QUrl()); + m_stringNumber = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_stringNumber->setParentItem(this); + } + if (strNr > 0 && strNr < 7) { + m_stringNumber->setProperty("text", QString::number(strNr)); + m_stringNumber->setX((width() - m_stringNumber->width()) / 2.0); + // TODO set Y position + m_stringNumber->setVisible(true); + } else { + if (m_stringNumber) + m_stringNumber->setVisible(false); + } } +TnoteItem::EbowDirection TnoteItem::bowing() const +{ + return static_cast<EbowDirection>(m_wrapper->techicalData().bowing()); +} -void TnoteItem::markNoteHead(const QColor& outlineColor) { - if (outlineColor.alpha() == 0) { - m_head->setProperty("style", 0); // In Qt private qquicktext_p.h it is first value of enum TextStyle - } else { - m_head->setProperty("style", 1); // In Qt private qquicktext_p.h it is second value of enum TextStyle - m_head->setProperty("styleColor", outlineColor); - } +void TnoteItem::setBowing(EbowDirection bowDir) +{ + if (!m_bowing && bowDir != BowUndefined) { + m_staff->score()->component()->setData("import QtQuick 2.9; Text { z: -1; font { pixelSize: 5; family: \"Scorek\" } }", QUrl()); + m_bowing = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + m_bowing->setParentItem(this); + } + if (bowDir != BowUndefined) { + qreal bowY = 0.0; + if (m_note->onUpperStaff()) { + if (m_notePosY < m_staff->upperLine() - 2.0) // high pitch notes, above staff + bowY = m_staff->upperLine(); // bow below staff + else if (m_notePosY < m_staff->upperLine() + 1.0) // not that high but still above staff + bowY = m_notePosY - 12.5; // above note head + else // on the staff or below + bowY = m_staff->upperLine() - 12.0; // above staff + } else { + if (m_notePosY < m_staff->upperLine() + 24.0) + bowY = m_staff->upperLine() + 21.0; // below lower staff + else + bowY = m_staff->upperLine() + 9.0; // above lower staff + } + m_bowing->setProperty("text", QString(QChar(0xe610 + (bowDir == BowDown ? 0 : 2)))); + m_bowing->setX((width() - m_bowing->width()) / 2.0); + m_bowing->setY(bowY); + m_bowing->setVisible(true); + } else { + if (m_bowing) + m_bowing->setVisible(false); + } + m_wrapper->techicalData().setBowing(static_cast<Ttechnical::EbowDirection>(bowDir)); } +void TnoteItem::setFingerNumber(int fiNr) +{ + Q_UNUSED(fiNr) +} -qreal TnoteItem::getHeadY(const Tnote& n) { - qreal yPos = staff()->score()->clefOffset().total() + staff()->upperLine() - (n.octave() * 7 + (n.note() - 1)); - if (staff()->isPianoStaff()) { - if (n.onUpperStaff()) { - if (yPos > staff()->upperLine() + 13.0) - yPos += 10.0; +void TnoteItem::markNoteHead(const QColor &outlineColor) +{ + if (outlineColor.alpha() == 0) { + m_head->setProperty("style", 0); // In Qt private qquicktext_p.h it is first value of enum TextStyle } else { - if (yPos > staff()->upperLine() + 3.0) - yPos += 10.0; + m_head->setProperty("style", 1); // In Qt private qquicktext_p.h it is second value of enum TextStyle + m_head->setProperty("styleColor", outlineColor); } - } - return yPos; } +qreal TnoteItem::getHeadY(const Tnote &n) +{ + qreal yPos = staff()->score()->clefOffset().total() + staff()->upperLine() - (n.octave() * 7 + (n.note() - 1)); + if (staff()->isPianoStaff()) { + if (n.onUpperStaff()) { + if (yPos > staff()->upperLine() + 13.0) + yPos += 10.0; + } else { + if (yPos > staff()->upperLine() + 3.0) + yPos += 10.0; + } + } + return yPos; +} // #define LINE_WIDTH (0.2) // void TnoteItem::paint(QPainter* painter) { @@ -600,159 +605,158 @@ qreal TnoteItem::getHeadY(const Tnote& n) { // } // } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -QString TnoteItem::getAccidText() { - if (!m_note->isValid()) - return QString(); - - QString a = unicodeGlyphArray(m_note->alter()); - qint8 accidInKey = m_staff->score()->accidInKey(m_note->note() - 1); - bool extraAccid = false; - if (accidInKey) { // key signature has an accidental on this note - if (m_note->alter() == 0) // show neutral if note has not any accidental - a = unicodeGlyphArray(3); // neutral - else { - if (accidInKey == m_note->alter()) { // accidental in key, do not show - if (m_staff->score()->showExtraAccids() && accidInKey) { // or user wants it at any cost - extraAccid = true; - a = extraAccidString(m_note->alter()); - } else - a.clear(); - } +QString TnoteItem::getAccidText() +{ + if (!m_note->isValid()) + return QString(); + + QString a = unicodeGlyphArray(m_note->alter()); + qint8 accidInKey = m_staff->score()->accidInKey(m_note->note() - 1); + bool extraAccid = false; + if (accidInKey) { // key signature has an accidental on this note + if (m_note->alter() == 0) // show neutral if note has not any accidental + a = unicodeGlyphArray(3); // neutral + else { + if (accidInKey == m_note->alter()) { // accidental in key, do not show + if (m_staff->score()->showExtraAccids() && accidInKey) { // or user wants it at any cost + extraAccid = true; + a = extraAccidString(m_note->alter()); + } else + a.clear(); + } + } } - } - - if (m_note->rtm.tie() > Trhythm::e_tieStart) { - a.clear(); // do not display accidental of first note in measure if it has tie - } else if (!extraAccid) { - int id = index() - 1; // check the previous notes for accidentals - Tnote* checkNote; - while (id > -1 && m_staff->score()->noteSegment(id)->item()->measure() == measure()) { - checkNote = m_staff->score()->noteSegment(id)->note(); - if (checkNote->note() == m_note->note()) { - if (checkNote->rtm.tie() > Trhythm::e_tieStart && checkNote->alter() == m_note->alter()) { - // Ignore notes prolonged with ties - they could be continued from the previous measure - // and then, the accidental has to be displayed again in current measure + + if (m_note->rtm.tie() > Trhythm::e_tieStart) { + a.clear(); // do not display accidental of first note in measure if it has tie + } else if (!extraAccid) { + int id = index() - 1; // check the previous notes for accidentals + Tnote *checkNote; + while (id > -1 && m_staff->score()->noteSegment(id)->item()->measure() == measure()) { + checkNote = m_staff->score()->noteSegment(id)->note(); + if (checkNote->note() == m_note->note()) { + if (checkNote->rtm.tie() > Trhythm::e_tieStart && checkNote->alter() == m_note->alter()) { + // Ignore notes prolonged with ties - they could be continued from the previous measure + // and then, the accidental has to be displayed again in current measure + id--; + continue; + } + if (checkNote->alter() != 0 && m_note->alter() == 0) { + if (a.isEmpty()) + a = unicodeGlyphArray(3); // and add neutral when some of previous notes with the same step had an accidental + } else if (checkNote->alter() == m_note->alter()) { // do not display it twice + if (m_staff->score()->showExtraAccids()) // accidental with parenthesis + a = extraAccidString(m_note->alter()); + else // or nothing + a.clear(); + } else if (accidInKey == m_note->alter() && checkNote->alter() != m_note->alter()) + a = unicodeGlyphArray( + m_note->alter()); // There is already accidental in key signature but some of the previous notes had another one, show it again + break; + } id--; - continue; - } - if (checkNote->alter() != 0 && m_note->alter() == 0) { - if (a.isEmpty()) - a = unicodeGlyphArray(3); // and add neutral when some of previous notes with the same step had an accidental - } else if (checkNote->alter() == m_note->alter()) { // do not display it twice - if (m_staff->score()->showExtraAccids()) // accidental with parenthesis - a = extraAccidString(m_note->alter()); - else // or nothing - a.clear(); - } else if (accidInKey == m_note->alter() && checkNote->alter() != m_note->alter()) - a = unicodeGlyphArray(m_note->alter());// There is already accidental in key signature but some of the previous notes had another one, show it again - break; } - id--; - } - } -// TODO, NOTE: Seems it will be not implemented anytime soon, if any -// if (m_staff->score()->remindAccids() && m_measure->number() > 0) { -// auto prevMeas = m_staff->score()->measure(m_measure->number() - 1); -// auto accidState = prevMeas->accidState(m_note->note - 1); -// if (accidState != 100 && accidState != 0 && a.isEmpty() && m_alter == 0) { -// a = a = accCharTable[5]; -// m_alter->setProperty("color", QColor(255, 0, 0)); -// } else if (accidState == 0 && accidInKey != 0) { -// a = accCharTable[m_note->alter + 2]; -// m_alter->setProperty("color", QColor(255, 0, 0)); -// } -// } - return a; -} - - -QString TnoteItem::getHeadText() const { - return getHeadText(m_note->rtm); + } + // TODO, NOTE: Seems it will be not implemented anytime soon, if any + // if (m_staff->score()->remindAccids() && m_measure->number() > 0) { + // auto prevMeas = m_staff->score()->measure(m_measure->number() - 1); + // auto accidState = prevMeas->accidState(m_note->note - 1); + // if (accidState != 100 && accidState != 0 && a.isEmpty() && m_alter == 0) { + // a = a = accCharTable[5]; + // m_alter->setProperty("color", QColor(255, 0, 0)); + // } else if (accidState == 0 && accidInKey != 0) { + // a = accCharTable[m_note->alter + 2]; + // m_alter->setProperty("color", QColor(255, 0, 0)); + // } + // } + return a; +} + +QString TnoteItem::getHeadText() const +{ + return getHeadText(m_note->rtm); } - -QString TnoteItem::getFlagText() { - if (m_note->rhythm() < Trhythm::Eighth || m_note->isRest() || m_note->rtm.beam() != Trhythm::e_noBeam) - return QString(); - // In Scorek font, flag glyphs are placed: flag for stem-up, then flag for stem-down, starting from 0xe240 - return QString(QChar(0xe240 + (static_cast<int>(m_note->rhythm()) - 4) * 2 + (m_note->rtm.stemDown() ? 1 : 0))); +QString TnoteItem::getFlagText() +{ + if (m_note->rhythm() < Trhythm::Eighth || m_note->isRest() || m_note->rtm.beam() != Trhythm::e_noBeam) + return QString(); + // In Scorek font, flag glyphs are placed: flag for stem-up, then flag for stem-down, starting from 0xe240 + return QString(QChar(0xe240 + (static_cast<int>(m_note->rhythm()) - 4) * 2 + (m_note->rtm.stemDown() ? 1 : 0))); } - -void TnoteItem::keySignatureChanged() { - updateAlter(); - updateWidth(); +void TnoteItem::keySignatureChanged() +{ + updateAlter(); + updateWidth(); } - -void TnoteItem::hoverEnterEvent(QHoverEvent* event) { - if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { - if (event->pos().y() > 2.0 && event->pos().y() < height()) { - m_measure->score()->setHoveredNote(this); - m_measure->score()->changeActiveNote(this); +void TnoteItem::hoverEnterEvent(QHoverEvent *event) +{ + if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { + if (event->pos().y() > 2.0 && event->pos().y() < height()) { + m_measure->score()->setHoveredNote(this); + m_measure->score()->changeActiveNote(this); + } } - } } - -void TnoteItem::hoverLeaveEvent(QHoverEvent*) { - if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { - m_measure->score()->setHoveredNote(nullptr); - m_measure->score()->changeActiveNote(nullptr); - } +void TnoteItem::hoverLeaveEvent(QHoverEvent *) +{ + if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { + m_measure->score()->setHoveredNote(nullptr); + m_measure->score()->changeActiveNote(nullptr); + } } +void TnoteItem::hoverMoveEvent(QHoverEvent *event) +{ + if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { + if (m_staff->score()->clefType() == Tclef::NoClef) + return; -void TnoteItem::hoverMoveEvent(QHoverEvent* event) { - if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { - - if (m_staff->score()->clefType() == Tclef::NoClef) - return; - - if (m_measure->score()->hoveredNote() != this) { - m_measure->score()->setHoveredNote(this); - m_measure->score()->changeActiveNote(this); - } + if (m_measure->score()->hoveredNote() != this) { + m_measure->score()->setHoveredNote(this); + m_measure->score()->changeActiveNote(this); + } - if (event->pos().y() > 2.0 && event->pos().y() < height()) { - if (!m_measure->score()->pressedNote() && m_measure->score()->hoveredNote() - && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) - m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + if (event->pos().y() > 2.0 && event->pos().y() < height()) { + if (!m_measure->score()->pressedNote() && m_measure->score()->hoveredNote() + && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + } } - } } - /** * Mouse events are used to handle touch when supported (they are ignored when enter event occurred before invoked by mouse) * Press event displays note cursor and locks grabbing the mouse - so moving a finger doesn't scroll entire score */ -void TnoteItem::mousePressEvent(QMouseEvent* event) { - if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { - if (event->button() == Qt::LeftButton && event->pos().y() > 2.0 && event->pos().y() < height()) { - setKeepMouseGrab(true); - m_measure->score()->setPressedNote(this); - if (m_measure->score()->activeNote() != this) { - m_measure->score()->changeActiveNote(this); - m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); - } else if (m_staff->score()->singleNote()) - m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); - - if (!m_measure->score()->hoveredNote()) { - m_measure->score()->touchHideTimer()->stop(); - m_touchDuration.restart(); - m_measure->score()->setTouched(true); - } +void TnoteItem::mousePressEvent(QMouseEvent *event) +{ + if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { + if (event->button() == Qt::LeftButton && event->pos().y() > 2.0 && event->pos().y() < height()) { + setKeepMouseGrab(true); + m_measure->score()->setPressedNote(this); + if (m_measure->score()->activeNote() != this) { + m_measure->score()->changeActiveNote(this); + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + } else if (m_staff->score()->singleNote()) + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + + if (!m_measure->score()->hoveredNote()) { + m_measure->score()->touchHideTimer()->stop(); + m_touchDuration.restart(); + m_measure->score()->setTouched(true); + } + } } - } } - /** * @p touchHideTimer() of score becomes active after first release of a finger, * note cursor remains visible for 2.5 seconds. @@ -760,157 +764,158 @@ void TnoteItem::mousePressEvent(QMouseEvent* event) { * * Release event is also used to set (confirm) a note by mouse press */ -void TnoteItem::mouseReleaseEvent(QMouseEvent* event) { - if (!m_staff->score()->readOnly()) { - if (m_staff->score()->singleNote() || m_staff->score()->editMode()) { - if (event->button() == Qt::LeftButton) { - if (keepMouseGrab()) - setKeepMouseGrab(false); - if (event->pos().y() > 2.0 && event->pos().y() < height()) { - if (m_measure->score()->hoveredNote()) { // mouse - if (m_measure->score()->hoveredNote() == this) - m_measure->score()->noteClicked(m_measure->score()->activeYpos()); - m_measure->score()->setPressedNote(nullptr); - } else { // touch - if (m_touchDuration.elapsed() < 190) { // confirm note - if (m_heldNote == this) { - m_measure->score()->noteClicked(m_measure->score()->activeYpos()); // set note only when it was touched second time - } else - m_measure->score()->setSelectedItem(this); - m_heldNote = nullptr; +void TnoteItem::mouseReleaseEvent(QMouseEvent *event) +{ + if (!m_staff->score()->readOnly()) { + if (m_staff->score()->singleNote() || m_staff->score()->editMode()) { + if (event->button() == Qt::LeftButton) { + if (keepMouseGrab()) + setKeepMouseGrab(false); + if (event->pos().y() > 2.0 && event->pos().y() < height()) { + if (m_measure->score()->hoveredNote()) { // mouse + if (m_measure->score()->hoveredNote() == this) + m_measure->score()->noteClicked(m_measure->score()->activeYpos()); m_measure->score()->setPressedNote(nullptr); - m_measure->score()->changeActiveNote(nullptr); - } else { // keep cursor visible - m_measure->score()->touchHideTimer()->start(2500); - m_heldNote = this; + } else { // touch + if (m_touchDuration.elapsed() < 190) { // confirm note + if (m_heldNote == this) { + m_measure->score()->noteClicked(m_measure->score()->activeYpos()); // set note only when it was touched second time + } else + m_measure->score()->setSelectedItem(this); + m_heldNote = nullptr; + m_measure->score()->setPressedNote(nullptr); + m_measure->score()->changeActiveNote(nullptr); + } else { // keep cursor visible + m_measure->score()->touchHideTimer()->start(2500); + m_heldNote = this; + } + m_measure->score()->setTouched(false); } - m_measure->score()->setTouched(false); } - } - } else if (event->button() == Qt::RightButton) { - m_measure->score()->setSelectedItem(this); - } - } else if (!m_staff->score()->singleNote() && !m_staff->score()->editMode()) { // not edit mode but also not read only - m_measure->score()->setSelectedItem(this); // no matter what button - select the note - } - } else { - if (m_measure->score()->selectInReadOnly()) - emit m_measure->score()->readOnlyNoteClicked(index()); - } -} - - -void TnoteItem::mouseMoveEvent(QMouseEvent* event) { - if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { - if (event->pos().y() > 2.0 && event->pos().y() < height()) { - if (m_measure->score()->pressedNote() && m_touchDuration.elapsed() > 200 - && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) - { - m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + } else if (event->button() == Qt::RightButton) { + m_measure->score()->setSelectedItem(this); + } + } else if (!m_staff->score()->singleNote() && !m_staff->score()->editMode()) { // not edit mode but also not read only + m_measure->score()->setSelectedItem(this); // no matter what button - select the note + } + } else { + if (m_measure->score()->selectInReadOnly()) + emit m_measure->score()->readOnlyNoteClicked(index()); + } +} + +void TnoteItem::mouseMoveEvent(QMouseEvent *event) +{ + if (!m_staff->score()->readOnly() && (m_staff->score()->singleNote() || m_staff->score()->editMode())) { + if (event->pos().y() > 2.0 && event->pos().y() < height()) { + if (m_measure->score()->pressedNote() && m_touchDuration.elapsed() > 200 + && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) { + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + } } } - } -} - -//################################################################################################# -//################### PRIVATE ############################################ -//################################################################################################# - -QQuickItem* TnoteItem::createAddLine() { - auto line = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); - line->setParentItem(this); - line->setWidth(3.5); - line->setHeight(m_staff->height() * m_staff->scale() < 200.0 ? 0.3 : 0.2); // for smaller views, keep line thicker to avoid disappearing - line->setX(m_staff->score()->singleNote() ? 1.0 : -0.5); - line->setVisible(false); - line->setProperty("color", qApp->palette().text().color()); - return line; -} - - -void TnoteItem::updateAlter() { - auto accidText = getAccidText(); - m_alter->setProperty("text", accidText); - if (!accidText.isEmpty()) - m_alter->setX(-m_alter->width() - 0.1); -} - - -void TnoteItem::updateWidth() { - if (m_measure->score()->singleNote()) - setWidth(5.0); - else { - qreal w = m_alter->width() + m_head->width(); - if (!m_note->isRest() && !m_note->rtm.stemDown() && m_stem->isVisible() && m_flag->width() > 0.0) - w += m_flag->width() - 0.5; - setWidth(w); - updateTieScale(); - } -} - - -void TnoteItem::updateNoteHead() { - QString headText = getHeadText(); - if (m_note->hasDot()) - headText.append(QStringLiteral("\ue1e8")); - m_head->setProperty("text", headText); -} - - -void TnoteItem::updateTieScale() { - if (m_tie) { - m_tie->setProperty("xScale", tieWidth() / 2.90625); - m_tie->setProperty("stemDown", m_note->rtm.stemDown()); - } -} - - -void TnoteItem::checkStem() { - if (m_notePosY && !m_note->isRest() && m_note->rhythm() > Trhythm::Whole ) { - if (m_note->rtm.beam() == Trhythm::e_noBeam) { - m_note->rtm.setStemDown(m_notePosY < staff()->upperLine() + 4.0 - || (staff()->isPianoStaff() && m_notePosY > staff()->upperLine() + 13.0 && m_notePosY < staff()->upperLine() + 26.0)); - m_stem->setHeight(qMax(STEM_HEIGHT, qAbs(m_notePosY - - (staff()->upperLine() + (staff()->isPianoStaff() && m_notePosY > staff()->upperLine() + 13.0 ? 26.0 : 4.0))))); - QString flagText = getFlagText(); - m_flag->setProperty("text", flagText); - if (!flagText.isEmpty()) - m_flag->setY((m_note->rtm.stemDown() ? m_stem->height() : 0.0) - 15.0); - } else { - if (m_flag->width() > 0.0) - m_flag->setProperty("text", QString()); - } - m_stem->setX(m_head->x() + (m_note->rtm.stemDown() ? 0.0 : 2.0)); - m_stem->setY(m_notePosY + (m_note->rtm.stemDown() ? 0.0 : - m_stem->height())); - m_stem->setVisible(true); - } else - m_stem->setVisible(false); - bool stemHeightChanged = m_stemHeight != m_stem->height(); - m_stemHeight = m_stem->height(); - if (stemHeightChanged) - updateNamePos(); } +// ################################################################################################# +// ################### PRIVATE ############################################ +// ################################################################################################# -// TODO: name may collide with next/previous note name (16ths), octave mark out-shots too much -void TnoteItem::updateNamePos() { - if (m_name) { - if (m_note->isValid()) { - m_name->setVisible(true); - qreal yOff; - if (m_note->rtm.stemDown()) - yOff = m_notePosY > 6.0 ? (m_bowing && m_note->onUpperStaff() ? m_stemHeight - 4.0 : -9.5) : m_stemHeight - 4.0; - else - yOff = m_notePosY > height() - 6.0 && height() - m_stemHeight > 8.0 ? -m_stemHeight - 8.0 : -1.8; - m_name->setY(m_notePosY + yOff); - m_name->setProperty("text", m_note->isRest() ? QString() : m_note->styledName()); - m_name->setX(x() - m_alter->width() + (width() - m_name->width()) / 2.0); - } else { - m_name->setVisible(false); +QQuickItem *TnoteItem::createAddLine() +{ + auto line = qobject_cast<QQuickItem *>(m_staff->score()->component()->create()); + line->setParentItem(this); + line->setWidth(3.5); + line->setHeight(m_staff->height() * m_staff->scale() < 200.0 ? 0.3 : 0.2); // for smaller views, keep line thicker to avoid disappearing + line->setX(m_staff->score()->singleNote() ? 1.0 : -0.5); + line->setVisible(false); + line->setProperty("color", qApp->palette().text().color()); + return line; +} + +void TnoteItem::updateAlter() +{ + auto accidText = getAccidText(); + m_alter->setProperty("text", accidText); + if (!accidText.isEmpty()) + m_alter->setX(-m_alter->width() - 0.1); +} + +void TnoteItem::updateWidth() +{ + if (m_measure->score()->singleNote()) + setWidth(5.0); + else { + qreal w = m_alter->width() + m_head->width(); + if (!m_note->isRest() && !m_note->rtm.stemDown() && m_stem->isVisible() && m_flag->width() > 0.0) + w += m_flag->width() - 0.5; + setWidth(w); + updateTieScale(); } - } } +void TnoteItem::updateNoteHead() +{ + QString headText = getHeadText(); + if (m_note->hasDot()) + headText.append(QStringLiteral("\ue1e8")); + m_head->setProperty("text", headText); +} + +void TnoteItem::updateTieScale() +{ + if (m_tie) { + m_tie->setProperty("xScale", tieWidth() / 2.90625); + m_tie->setProperty("stemDown", m_note->rtm.stemDown()); + } +} + +void TnoteItem::checkStem() +{ + if (m_notePosY && !m_note->isRest() && m_note->rhythm() > Trhythm::Whole) { + if (m_note->rtm.beam() == Trhythm::e_noBeam) { + m_note->rtm.setStemDown(m_notePosY < staff()->upperLine() + 4.0 + || (staff()->isPianoStaff() && m_notePosY > staff()->upperLine() + 13.0 && m_notePosY < staff()->upperLine() + 26.0)); + m_stem->setHeight( + qMax(STEM_HEIGHT, + qAbs(m_notePosY - (staff()->upperLine() + (staff()->isPianoStaff() && m_notePosY > staff()->upperLine() + 13.0 ? 26.0 : 4.0))))); + QString flagText = getFlagText(); + m_flag->setProperty("text", flagText); + if (!flagText.isEmpty()) + m_flag->setY((m_note->rtm.stemDown() ? m_stem->height() : 0.0) - 15.0); + } else { + if (m_flag->width() > 0.0) + m_flag->setProperty("text", QString()); + } + m_stem->setX(m_head->x() + (m_note->rtm.stemDown() ? 0.0 : 2.0)); + m_stem->setY(m_notePosY + (m_note->rtm.stemDown() ? 0.0 : -m_stem->height())); + m_stem->setVisible(true); + } else + m_stem->setVisible(false); + bool stemHeightChanged = m_stemHeight != m_stem->height(); + m_stemHeight = m_stem->height(); + if (stemHeightChanged) + updateNamePos(); +} + +// TODO: name may collide with next/previous note name (16ths), octave mark out-shots too much +void TnoteItem::updateNamePos() +{ + if (m_name) { + if (m_note->isValid()) { + m_name->setVisible(true); + qreal yOff; + if (m_note->rtm.stemDown()) + yOff = m_notePosY > 6.0 ? (m_bowing && m_note->onUpperStaff() ? m_stemHeight - 4.0 : -9.5) : m_stemHeight - 4.0; + else + yOff = m_notePosY > height() - 6.0 && height() - m_stemHeight > 8.0 ? -m_stemHeight - 8.0 : -1.8; + m_name->setY(m_notePosY + yOff); + m_name->setProperty("text", m_note->isRest() ? QString() : m_note->styledName()); + m_name->setX(x() - m_alter->width() + (width() - m_name->width()) / 2.0); + } else { + m_name->setVisible(false); + } + } +} /** * @p m_loLines (lower lines) are displayed always below upper staff (either single one or that upper of grand staff), @@ -919,22 +924,23 @@ void TnoteItem::updateNamePos() { * It covers all scale of the bandoneon notation * @p m_underLoLines are used only when grand staff */ -void TnoteItem::checkAddLinesVisibility() { - bool v = m_notePosY != 0.0 && !m_note->isRest(); - bool betweenStaves = staff()->isPianoStaff() && m_notePosY >= staff()->upperLine() + 10.0 && m_notePosY < staff()->upperLine() + 21.0; - for (int i = 0; i < 7; ++i) { - m_upLines[i]->setVisible(v && m_notePosY > 0.0 && i >= qFloor((m_notePosY - 1.0) / 2.0) && (i != 6 || !staff()->isPianoStaff())); - qreal upp1 = staff()->upperLine() + 10.0 + i * 2; - if (staff()->isPianoStaff()) { - if (m_notePosY < staff()->upperLine() + 14.0) - m_loLines[i]->setVisible(v && betweenStaves && m_notePosY >= upp1 && m_notePosY < staff()->upperLine() + 14.0); - else - m_loLines[i]->setVisible(v && betweenStaves && m_notePosY <= upp1 && m_notePosY <= staff()->upperLine() + 21.0 && i != 6); - } else - m_loLines[i]->setVisible(v && m_notePosY >= upp1); - } - if (!m_underLoLines.isEmpty()) { - m_underLoLines[0]->setVisible(v && m_notePosY >= staff()->upperLine() + 32.0); - m_underLoLines[1]->setVisible(v && m_notePosY >= staff()->upperLine() + 34.0); - } +void TnoteItem::checkAddLinesVisibility() +{ + bool v = m_notePosY != 0.0 && !m_note->isRest(); + bool betweenStaves = staff()->isPianoStaff() && m_notePosY >= staff()->upperLine() + 10.0 && m_notePosY < staff()->upperLine() + 21.0; + for (int i = 0; i < 7; ++i) { + m_upLines[i]->setVisible(v && m_notePosY > 0.0 && i >= qFloor((m_notePosY - 1.0) / 2.0) && (i != 6 || !staff()->isPianoStaff())); + qreal upp1 = staff()->upperLine() + 10.0 + i * 2; + if (staff()->isPianoStaff()) { + if (m_notePosY < staff()->upperLine() + 14.0) + m_loLines[i]->setVisible(v && betweenStaves && m_notePosY >= upp1 && m_notePosY < staff()->upperLine() + 14.0); + else + m_loLines[i]->setVisible(v && betweenStaves && m_notePosY <= upp1 && m_notePosY <= staff()->upperLine() + 21.0 && i != 6); + } else + m_loLines[i]->setVisible(v && m_notePosY >= upp1); + } + if (!m_underLoLines.isEmpty()) { + m_underLoLines[0]->setVisible(v && m_notePosY >= staff()->upperLine() + 32.0); + m_underLoLines[1]->setVisible(v && m_notePosY >= staff()->upperLine() + 34.0); + } } diff --git a/src/libs/core/score/tnoteitem.h b/src/libs/core/score/tnoteitem.h index c518cbb4d4b35804ae5caa345997a1ff2e0189a1..d5763a839ad8a1d90effe01358f23e62c5ea5ea1 100644 --- a/src/libs/core/score/tnoteitem.h +++ b/src/libs/core/score/tnoteitem.h @@ -19,22 +19,18 @@ #ifndef TNOTEITEM_H #define TNOTEITEM_H - -#include "nootkacoreglobal.h" #include "music/trhythm.h" -#include <QtQuick/qquickitem.h> +#include "nootkacoreglobal.h" #include <QtCore/qelapsedtimer.h> - +#include <QtQuick/qquickitem.h> class TstaffItem; class TmeasureObject; class TnotePair; class Tnote; - #define STEM_HEIGHT (6.0) // default height of a note stem - /** * @class TnoteItem is @class QQuickItem derivative representing single note on the score. * It dynamically creates QML items: note head, alter (accidental) text, stem rectangle and rhythm flag. @@ -42,211 +38,208 @@ class Tnote; */ class NOOTKACORE_EXPORT TnoteItem : public QQuickItem { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(qreal notePosY READ notePosY NOTIFY notePosYchanged) - Q_PROPERTY(qreal alterWidth READ alterWidth NOTIFY alterWidthChanged) - Q_PROPERTY(int index READ index) - Q_PROPERTY(TstaffItem* staffItem READ staff) - Q_PROPERTY(qreal rightX READ rightX NOTIFY rightXChanged) - Q_PROPERTY(bool hasTie READ hasTie NOTIFY hasTieChanged) + Q_PROPERTY(qreal notePosY READ notePosY NOTIFY notePosYchanged) + Q_PROPERTY(qreal alterWidth READ alterWidth NOTIFY alterWidthChanged) + Q_PROPERTY(int index READ index) + Q_PROPERTY(TstaffItem *staffItem READ staff) + Q_PROPERTY(qreal rightX READ rightX NOTIFY rightXChanged) + Q_PROPERTY(bool hasTie READ hasTie NOTIFY hasTieChanged) - friend class TscoreObject; - friend class TstaffItem; - friend class TmeasureObject; - friend class TbeamObject; - friend class TnotePair; + friend class TscoreObject; + friend class TstaffItem; + friend class TmeasureObject; + friend class TbeamObject; + friend class TnotePair; public: - explicit TnoteItem(TstaffItem* staffObj = nullptr, TnotePair* wrapper = nullptr); - ~TnoteItem() override; + explicit TnoteItem(TstaffItem *staffObj = nullptr, TnotePair *wrapper = nullptr); + ~TnoteItem() override; - TstaffItem* staff() const { return m_staff; } - void setStaff(TstaffItem* staffObj); + TstaffItem *staff() const { return m_staff; } + void setStaff(TstaffItem *staffObj); - TmeasureObject* measure() { return m_measure; } - void setMeasure(TmeasureObject* m); + TmeasureObject *measure() { return m_measure; } + void setMeasure(TmeasureObject *m); - Tnote* note() { return m_note; } - void setNote(const Tnote& n); + Tnote *note() { return m_note; } + void setNote(const Tnote &n); - quint32 technical() const; - void setTechnical(quint32 tech); + quint32 technical() const; + void setTechnical(quint32 tech); - qreal notePosY() const { return m_notePosY; } + qreal notePosY() const { return m_notePosY; } - Q_INVOKABLE Trhythm rhythm() const; + Q_INVOKABLE Trhythm rhythm() const; - /** - * Note number in the staff - */ - int index() const; + /** + * Note number in the staff + */ + int index() const; - qreal stemHeight() const { return m_stemHeight; } - void setStemHeight(qreal sh); + qreal stemHeight() const { return m_stemHeight; } + void setStemHeight(qreal sh); - qreal alterWidth() { return m_alter->width(); } + qreal alterWidth() { return m_alter->width(); } + QColor color() { return m_head->property("color").value<QColor>(); } + void setColor(const QColor &c); - QColor color() { return m_head->property("color").value<QColor>(); } - void setColor(const QColor& c); + /** + * Overrides standard @p setX() method to shift note segment about accidental symbol width (if it is set). + * It also updates tie (if any) and beam (for the last note in a beam group) + */ + void setX(qreal xx); - /** - * Overrides standard @p setX() method to shift note segment about accidental symbol width (if it is set). - * It also updates tie (if any) and beam (for the last note in a beam group) - */ - void setX(qreal xx); + /** + * shortcut to X coordinate of right note corner plus gap related to rhythm and staff gap factor + */ + qreal rightX() const; - /** - * shortcut to X coordinate of right note corner plus gap related to rhythm and staff gap factor - */ - qreal rightX() const; + bool hasTie() const; - bool hasTie() const; + void setHeight(qreal hh); - void setHeight(qreal hh); + /** + * Returns gap factor after this note item depends on current rhythm value + */ + qreal rhythmFactor() const; - /** - * Returns gap factor after this note item depends on current rhythm value - */ - qreal rhythmFactor() const; + /** + * Returns position of the top of this note stem in staff coordinates + */ + QPointF stemTop(); - /** - * Returns position of the top of this note stem in staff coordinates - */ - QPointF stemTop(); + /** + * Prints to std out debug info about this note: [NOTE number] in color + */ + char debug(); - /** - * Prints to std out debug info about this note: [NOTE number] in color - */ - char debug(); + qreal tieWidth(); - qreal tieWidth(); + void setNoteNameVisible(bool nameVisible); - void setNoteNameVisible(bool nameVisible); + /** + * Static method that converts given rhythm into text of a note head + */ + static QString getHeadText(const Trhythm &r); - /** - * Static method that converts given rhythm into text of a note head - */ - static QString getHeadText(const Trhythm& r); + /** + * Unicode numbers of accidentals in Scorek font. + * Takes @p Tnote::alter() [-2 to 3] parameter. + * @p 3 is neutral symbol + */ + static QString unicodeGlyphArray(int alter); - /** - * Unicode numbers of accidentals in Scorek font. - * Takes @p Tnote::alter() [-2 to 3] parameter. - * @p 3 is neutral symbol - */ - static QString unicodeGlyphArray(int alter); + /** + * Returns given accidental @p alter glyph + * wrapped in parenthesis, i.e.: (#) or (x). + * Glyphs are smaller then usual # or so. + */ + static QString extraAccidString(int alter); - /** - * Returns given accidental @p alter glyph - * wrapped in parenthesis, i.e.: (#) or (x). - * Glyphs are smaller then usual # or so. - */ - static QString extraAccidString(int alter); + void setFingerNumber(int fiNr); + Q_INVOKABLE void setStringNumber(int strNr); - void setFingerNumber(int fiNr); - Q_INVOKABLE void setStringNumber(int strNr); + enum EbowDirection { + BowUndefined = 0, + BowDown = 2, /**< For bandoneon it is bellow opening */ + BowUp = 4 /**< For bandoneon it is bellow closing */ + }; + Q_ENUM(EbowDirection) - enum EbowDirection { - BowUndefined = 0, - BowDown = 2, /**< For bandoneon it is bellow opening */ - BowUp = 4 /**< For bandoneon it is bellow closing */ - }; - Q_ENUM(EbowDirection) + Q_INVOKABLE TnoteItem::EbowDirection bowing() const; + Q_INVOKABLE void setBowing(TnoteItem::EbowDirection bowDir); - Q_INVOKABLE TnoteItem::EbowDirection bowing() const; - Q_INVOKABLE void setBowing(TnoteItem::EbowDirection bowDir); + /** + * If @p outLineColor differs from note color, outline is set over note head, + * but when it is the same, outline is unset (what means reset the mark) + */ + void markNoteHead(const QColor &outlineColor); - /** - * If @p outLineColor differs from note color, outline is set over note head, - * but when it is the same, outline is unset (what means reset the mark) - */ - void markNoteHead(const QColor& outlineColor); - - /** - * Returns Y coordinate of the note head for given note @p n. - * But it doesn't take care of Scorek font overlay, - * so to get proper text item position @p 15.0 has to be subtracted - */ - qreal getHeadY(const Tnote& n); + /** + * Returns Y coordinate of the note head for given note @p n. + * But it doesn't take care of Scorek font overlay, + * so to get proper text item position @p 15.0 has to be subtracted + */ + qreal getHeadY(const Tnote &n); signals: - void noteChanged(); - void notePosYchanged(); - void alterWidthChanged(); - void rightXChanged(); - void hasTieChanged(); + void noteChanged(); + void notePosYchanged(); + void alterWidthChanged(); + void rightXChanged(); + void hasTieChanged(); protected: - QString getAccidText(); - QString getHeadText() const; - QString getFlagText(); - - void keySignatureChanged(); - - QQuickItem* tie() { return m_tie; } - QQuickItem* head() { return m_head; } - QQuickItem* nameItem() { return m_name; } - TnotePair* wrapper() { return m_wrapper; } - - /** - * Moves note head X position by @p shift. - * It is used in single note mode when note head has to centered in highlight - */ - void shiftHead(qreal shift); - - /** - * @p TnoteItem manages tie itself whenever tie state changes, by calling exactly this method. - * Tie is simple Text QML item of tie symbol with horizontal scaling that determines tie width, - * but tie is not aware about next note position as long as next note X coordinate - * depends on this note width and rhythm factor. - * So @p setX() method usually called when staff factor is changing, updates tie width. - * @p tieWidth() returns desired tie width. - * Also @p TnoteItem objects takes care about breaking tie among staves. - * It sets tie glyph text to @p m_accidText when note with tie begins a staff - */ - void checkTie(); - - void updateDebug(); - - void hoverEnterEvent(QHoverEvent* event) override; - void hoverLeaveEvent(QHoverEvent*) override; - void hoverMoveEvent(QHoverEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; + QString getAccidText(); + QString getHeadText() const; + QString getFlagText(); + + void keySignatureChanged(); + + QQuickItem *tie() { return m_tie; } + QQuickItem *head() { return m_head; } + QQuickItem *nameItem() { return m_name; } + TnotePair *wrapper() { return m_wrapper; } + + /** + * Moves note head X position by @p shift. + * It is used in single note mode when note head has to centered in highlight + */ + void shiftHead(qreal shift); + + /** + * @p TnoteItem manages tie itself whenever tie state changes, by calling exactly this method. + * Tie is simple Text QML item of tie symbol with horizontal scaling that determines tie width, + * but tie is not aware about next note position as long as next note X coordinate + * depends on this note width and rhythm factor. + * So @p setX() method usually called when staff factor is changing, updates tie width. + * @p tieWidth() returns desired tie width. + * Also @p TnoteItem objects takes care about breaking tie among staves. + * It sets tie glyph text to @p m_accidText when note with tie begins a staff + */ + void checkTie(); + + void updateDebug(); + + void hoverEnterEvent(QHoverEvent *event) override; + void hoverLeaveEvent(QHoverEvent *) override; + void hoverMoveEvent(QHoverEvent *event) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; private: - - TstaffItem *m_staff; - TnotePair *m_wrapper; - TmeasureObject *m_measure; - Tnote *m_note; - qreal m_notePosY; - QQuickItem *m_head, *m_alter, *m_stem, *m_flag; - QVector<QQuickItem*> m_upLines, m_loLines, m_underLoLines; - qreal m_stemHeight; - QQuickItem *m_tie = nullptr; - QQuickItem *m_name = nullptr; - QQuickItem *m_stringNumber = nullptr; - QQuickItem *m_bowing = nullptr; - QQuickItem *m_fingerNumber = nullptr; - static QElapsedTimer m_touchDuration; - static TnoteItem *m_heldNote; - - QQuickItem *m_debug; + TstaffItem *m_staff; + TnotePair *m_wrapper; + TmeasureObject *m_measure; + Tnote *m_note; + qreal m_notePosY; + QQuickItem *m_head, *m_alter, *m_stem, *m_flag; + QVector<QQuickItem *> m_upLines, m_loLines, m_underLoLines; + qreal m_stemHeight; + QQuickItem *m_tie = nullptr; + QQuickItem *m_name = nullptr; + QQuickItem *m_stringNumber = nullptr; + QQuickItem *m_bowing = nullptr; + QQuickItem *m_fingerNumber = nullptr; + static QElapsedTimer m_touchDuration; + static TnoteItem *m_heldNote; + + QQuickItem *m_debug; private: - QQuickItem* createAddLine(); - void updateAlter(); - void updateWidth(); - void updateNoteHead(); - void updateTieScale(); - void checkStem(); - void updateNamePos(); - void checkAddLinesVisibility(); + QQuickItem *createAddLine(); + void updateAlter(); + void updateWidth(); + void updateNoteHead(); + void updateTieScale(); + void checkStem(); + void updateNamePos(); + void checkAddLinesVisibility(); }; #endif // TNOTEITEM_H diff --git a/src/libs/core/score/tnotepair.cpp b/src/libs/core/score/tnotepair.cpp index d605eb483c4d755d8c4fd22a22f064af7309ba9a..72b284cae038641fed8b3b9f9dcdc836e26df5ae 100644 --- a/src/libs/core/score/tnotepair.cpp +++ b/src/libs/core/score/tnotepair.cpp @@ -18,115 +18,112 @@ #include "tnotepair.h" #include "music/tnote.h" +#include "tbeamobject.h" #include "tnoteitem.h" #include "tstaffitem.h" -#include "tbeamobject.h" - -TnotePair::TnotePair(int index, Tnote* n, TnoteItem* ob) : - m_note(n), - m_noteItem(ob), - m_index(static_cast<quint16>(index)) +TnotePair::TnotePair(int index, Tnote *n, TnoteItem *ob) + : m_note(n) + , m_noteItem(ob) + , m_index(static_cast<quint16>(index)) { - } - TnotePair::~TnotePair() { - delete m_noteItem; + delete m_noteItem; } - // void TnotePair::setIndex(int i) { // m_index = static_cast<quint32>(i); // if (m_noteItem) // m_noteItem->updateDebug(); // } -// -// +// +// // void TnotePair::setRhythmGroup(qint8 g) { // m_group = g; // if (m_noteItem) // m_noteItem->updateDebug(); // } - -void TnotePair::setNoteItem(TnoteItem* ob) { - m_noteItem = ob; +void TnotePair::setNoteItem(TnoteItem *ob) +{ + m_noteItem = ob; } - -void TnotePair::setNote(const Tnote& n) { - *m_note = n; - m_noteItem->setNote(n); +void TnotePair::setNote(const Tnote &n) +{ + *m_note = n; + m_noteItem->setNote(n); } - -void TnotePair::setTechnical(quint32 tech) { - if (tech != m_technical.data()) { - Ttechnical newTechn(tech); - if (newTechn.fingerPos().str() != m_technical.fingerPos().str()) - m_noteItem->setStringNumber(newTechn.fingerPos().str()); - if (newTechn.bowing() != m_technical.bowing()) - m_noteItem->setBowing(static_cast<TnoteItem::EbowDirection>(newTechn.bowing())); - m_technical.setData(tech); - } +void TnotePair::setTechnical(quint32 tech) +{ + if (tech != m_technical.data()) { + Ttechnical newTechn(tech); + if (newTechn.fingerPos().str() != m_technical.fingerPos().str()) + m_noteItem->setStringNumber(newTechn.fingerPos().str()); + if (newTechn.bowing() != m_technical.bowing()) + m_noteItem->setBowing(static_cast<TnoteItem::EbowDirection>(newTechn.bowing())); + m_technical.setData(tech); + } } - -void TnotePair::approve() { - if (m_changes) { - if (m_changes & e_beamChanged || m_changes & e_stemDirChanged) - m_noteItem->setNote(*m_note); - m_changes = 0; - } +void TnotePair::approve() +{ + if (m_changes) { + if (m_changes & e_beamChanged || m_changes & e_stemDirChanged) + m_noteItem->setNote(*m_note); + m_changes = 0; + } } - -int TnotePair::pairsDuration(const QList<TnotePair*>& pairs) { - int dur = 0; - for (TnotePair* np : qAsConst(pairs)) - dur += np->note()->duration(); - return dur; +int TnotePair::pairsDuration(const QList<TnotePair *> &pairs) +{ + int dur = 0; + for (TnotePair *np : qAsConst(pairs)) + dur += np->note()->duration(); + return dur; } -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -void TnotePair::setBeam(TbeamObject* b) { - m_beam = b; +void TnotePair::setBeam(TbeamObject *b) +{ + m_beam = b; } - -void TnotePair::disconnectTie(Euntie untie) { - Trhythm::Etie t; - if (untie == e_untieNext) - t = m_note->rtm.tie() == Trhythm::e_tieCont ? Trhythm::e_tieStart : Trhythm::e_noTie; - else // e_untiePrev - t = m_note->rtm.tie() == Trhythm::e_tieCont ? Trhythm::e_tieEnd : Trhythm::e_noTie; - m_note->rtm.setTie(t); - m_noteItem->note()->rtm.setTie(t); - m_noteItem->checkTie(); - if (this == m_noteItem->staff()->firstNote() && (t == Trhythm::e_noTie || t == Trhythm::e_tieStart)) - m_noteItem->staff()->deleteExtraTie(); +void TnotePair::disconnectTie(Euntie untie) +{ + Trhythm::Etie t; + if (untie == e_untieNext) + t = m_note->rtm.tie() == Trhythm::e_tieCont ? Trhythm::e_tieStart : Trhythm::e_noTie; + else // e_untiePrev + t = m_note->rtm.tie() == Trhythm::e_tieCont ? Trhythm::e_tieEnd : Trhythm::e_noTie; + m_note->rtm.setTie(t); + m_noteItem->note()->rtm.setTie(t); + m_noteItem->checkTie(); + if (this == m_noteItem->staff()->firstNote() && (t == Trhythm::e_noTie || t == Trhythm::e_tieStart)) + m_noteItem->staff()->deleteExtraTie(); } - -void TnotePair::flush() { - m_noteItem->markNoteHead(Qt::transparent); - m_noteItem->setNoteNameVisible(false); - if (m_beam && this == m_beam->last()) - m_beam->deleteBeam(); // it will reset this beam to nullptr - if (this == m_noteItem->staff()->firstNote()) - m_noteItem->staff()->deleteExtraTie(); - if (m_noteItem->tie()) { - m_noteItem->note()->rtm.setTie(Trhythm::e_noTie); - m_noteItem->checkTie(); - } - m_noteItem->setStaff(nullptr); - m_noteItem->setBowing(TnoteItem::BowUndefined); - m_noteItem->setStringNumber(0); - m_technical.reset(); +void TnotePair::flush() +{ + m_noteItem->markNoteHead(Qt::transparent); + m_noteItem->setNoteNameVisible(false); + if (m_beam && this == m_beam->last()) + m_beam->deleteBeam(); // it will reset this beam to nullptr + if (this == m_noteItem->staff()->firstNote()) + m_noteItem->staff()->deleteExtraTie(); + if (m_noteItem->tie()) { + m_noteItem->note()->rtm.setTie(Trhythm::e_noTie); + m_noteItem->checkTie(); + } + m_noteItem->setStaff(nullptr); + m_noteItem->setBowing(TnoteItem::BowUndefined); + m_noteItem->setStringNumber(0); + m_technical.reset(); } diff --git a/src/libs/core/score/tnotepair.h b/src/libs/core/score/tnotepair.h index 5313d256c5e1b79ff2fd130bc3e6f15c502c2f98..f67f9dd72e32fcb1f5bbc5edfbf324036b4572c7 100644 --- a/src/libs/core/score/tnotepair.h +++ b/src/libs/core/score/tnotepair.h @@ -19,122 +19,115 @@ #ifndef TNOTEPAIR_H #define TNOTEPAIR_H - -#include <QtCore/qobject.h> #include "music/ttechnical.h" - +#include <QtCore/qobject.h> class Tnote; class TnoteItem; class TbeamObject; - /** * It wraps @p Tnote and its graphical representation @p TnoteItem. * It also handles rhythmical group of a note: @p rhythmGroup() and a beam @p beam() */ class TnotePair { - Q_GADGET + Q_GADGET - friend class TscoreObject; - friend class TmeasureObject; - friend class TbeamObject; - friend class TnoteItem; + friend class TscoreObject; + friend class TmeasureObject; + friend class TbeamObject; + friend class TnoteItem; public: - TnotePair(int index = -1, Tnote* n = nullptr, TnoteItem* ob = nullptr); - ~TnotePair(); + TnotePair(int index = -1, Tnote *n = nullptr, TnoteItem *ob = nullptr); + ~TnotePair(); - Tnote* note() { return m_note; } - TnoteItem* item() { return m_noteItem; } + Tnote *note() { return m_note; } + TnoteItem *item() { return m_noteItem; } - void setNoteItem(TnoteItem* ob); + void setNoteItem(TnoteItem *ob); - /** - * Sets note to @p n for both @p note() and then @p item()->setNote() - */ - void setNote(const Tnote& n); + /** + * Sets note to @p n for both @p note() and then @p item()->setNote() + */ + void setNote(const Tnote &n); - /** - * Sets note pointer to another @p Tnote, doesn't update note of the @p item() - */ - void setNote(Tnote* n) { m_note = n; } + /** + * Sets note pointer to another @p Tnote, doesn't update note of the @p item() + */ + void setNote(Tnote *n) { m_note = n; } - /** - * Bowing, fingering, string number and etc... - */ - quint32 technical() const { return m_technical.data(); } - void setTechnical(quint32 tech); + /** + * Bowing, fingering, string number and etc... + */ + quint32 technical() const { return m_technical.data(); } + void setTechnical(quint32 tech); - Ttechnical& techicalData() { return m_technical; } + Ttechnical &techicalData() { return m_technical; } /** * Number of rhythmical group in the measure, -1 (undefined) by default */ - qint8 rhythmGroup() { return m_group; } - void setRhythmGroup(qint8 g) { m_group = g; } - - /** - * Number of note in the score - */ - quint32 index() { return m_index; } - void setIndex(int i) { m_index = static_cast<quint32>(i); } - - /** - * Describes what changed in note to be approved into its item - */ - enum Echanges : quint8 { - e_noChanges = 0, - e_stemDirChanged = 1, - e_beamChanged = 2 - }; - Q_ENUM(Echanges) - - enum Euntie { e_untieNext, e_untiePrev }; - Q_ENUM(Euntie) - - int changes() { return m_changes; } - void addChange(Echanges ch) { m_changes |= ch;} - - /** - * Approves @p changes() (if any) to note @p item() - */ - void approve(); - - /** - * Duration of given list of notes (segments) - */ - static int pairsDuration(const QList<TnotePair*>& pairs); + qint8 rhythmGroup() { return m_group; } + void setRhythmGroup(qint8 g) { m_group = g; } + + /** + * Number of note in the score + */ + quint32 index() { return m_index; } + void setIndex(int i) { m_index = static_cast<quint32>(i); } + + /** + * Describes what changed in note to be approved into its item + */ + enum Echanges : quint8 { e_noChanges = 0, e_stemDirChanged = 1, e_beamChanged = 2 }; + Q_ENUM(Echanges) + + enum Euntie { e_untieNext, e_untiePrev }; + Q_ENUM(Euntie) + + int changes() { return m_changes; } + void addChange(Echanges ch) { m_changes |= ch; } + + /** + * Approves @p changes() (if any) to note @p item() + */ + void approve(); + + /** + * Duration of given list of notes (segments) + */ + static int pairsDuration(const QList<TnotePair *> &pairs); protected: - TbeamObject* beam() { return m_beam; } - void setBeam(TbeamObject* b); - - /** - * Disconnects tie. @p untie parameter of @p Euntie enumerator determines: - * is this note before tie break (@p e_untiePrev) or after (@p e_untieNext) - * This method doesn't check tie existence it asserts note has tie already. - * It also takes care about removing of extra tie at the staff beginning - */ - void disconnectTie(Euntie untie); - - /** - * Resets this note segment: - * - sets parent staff of item to null - * - deletes beam - * - removes tie, also this extra one at the staff beginning - */ - void flush(); + TbeamObject *beam() { return m_beam; } + void setBeam(TbeamObject *b); + + /** + * Disconnects tie. @p untie parameter of @p Euntie enumerator determines: + * is this note before tie break (@p e_untiePrev) or after (@p e_untieNext) + * This method doesn't check tie existence it asserts note has tie already. + * It also takes care about removing of extra tie at the staff beginning + */ + void disconnectTie(Euntie untie); + + /** + * Resets this note segment: + * - sets parent staff of item to null + * - deletes beam + * - removes tie, also this extra one at the staff beginning + */ + void flush(); private: - Tnote *m_note; - TnoteItem *m_noteItem; - qint8 m_group = -1; - quint16 m_index; - int m_changes = 0; - TbeamObject *m_beam = nullptr; - Ttechnical m_technical; + Tnote *m_note; + TnoteItem *m_noteItem; + qint8 m_group = -1; + quint16 m_index; + int m_changes = 0; + TbeamObject *m_beam = nullptr; + Ttechnical m_technical; }; #endif // TNOTEPAIR_H diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index 5c3d5e231a0734fef39705e7d812ff26bc0bc5de..17b5352a175f75f7c7fe8e98d4f54bde6f1df009 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -17,1628 +17,1678 @@ ***************************************************************************/ #include "tscoreobject.h" -#include "tstaffitem.h" +#include "music/tmelody.h" +#include "music/tmeter.h" +#include "taction.h" +#include "tbeamobject.h" #include "tmeasureobject.h" #include "tnoteitem.h" -#include "tbeamobject.h" #include "tnotepair.h" -#include "music/tmeter.h" -#include "music/tmelody.h" -#include "taction.h" +#include "tstaffitem.h" +#include <QtCore/qtimer.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> -#include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> -#include <QtCore/qtimer.h> +#include <QtQml/qqmlengine.h> #include <QtCore/qdebug.h> - #define WIDTH_CHANGE_DELAY (50) // when score width changes, give 50 ms before staves will be resized +TscoreObject::TscoreObject(QObject *parent) + : QObject(parent) + , m_keySignEnabled(false) + , m_showExtraAccids(false) + , m_remindAccids(false) + , m_enableDoubleAccids(false) + , m_showNoteNames(false) + , m_clefOffset(TclefOffset(3, 2)) + , m_width(0.0) + , m_adjustInProgress(false) + , m_nameStyle(static_cast<int>(Tnote::defaultStyle)) + , m_workRhythm(new Trhythm()) // quarter by default +{ + m_meter = new Tmeter(); + setMeter(4); // Tmeter::Meter_4_4 + m_measures << getMeasure(0); // new TmeasureObject(0, this); + + m_widthTimer = new QTimer(this); + m_widthTimer->setSingleShot(true); + connect(m_widthTimer, &QTimer::timeout, this, &TscoreObject::adjustScoreWidthSlot); + + for (int i = 0; i < 7; i++) // reset accidentals array + m_accidInKeyArray[i] = 0; + + m_touchHideTimer = new QTimer(this); + connect(m_touchHideTimer, &QTimer::timeout, this, [=] { + changeActiveNote(nullptr); + setPressedNote(nullptr); + m_touchHideTimer->stop(); + }); + m_enterTimer = new QTimer(this); + m_enterTimer->setSingleShot(true); + connect(m_enterTimer, &QTimer::timeout, this, &TscoreObject::enterTimeElapsed); + m_leaveTimer = new QTimer(this); + m_leaveTimer->setSingleShot(true); + connect(m_leaveTimer, &QTimer::timeout, this, &TscoreObject::leaveTimeElapsed); + m_bgColor = qApp->palette().base().color(); +} -TscoreObject::TscoreObject(QObject* parent) : - QObject(parent), - m_keySignEnabled(false), - m_showExtraAccids(false), - m_remindAccids(false), - m_enableDoubleAccids(false), - m_showNoteNames(false), - m_clefOffset(TclefOffset(3, 2)), - m_width(0.0), m_adjustInProgress(false), - m_nameStyle(static_cast<int>(Tnote::defaultStyle)), - m_workRhythm(new Trhythm()) // quarter by default -{ - m_meter = new Tmeter(); - setMeter(4); // Tmeter::Meter_4_4 - m_measures << getMeasure(0); // new TmeasureObject(0, this); - - m_widthTimer = new QTimer(this); - m_widthTimer->setSingleShot(true); - connect(m_widthTimer, &QTimer::timeout, this, &TscoreObject::adjustScoreWidthSlot); - - for (int i = 0; i < 7; i++) // reset accidentals array - m_accidInKeyArray[i] = 0; - - m_touchHideTimer = new QTimer(this); - connect(m_touchHideTimer, &QTimer::timeout, this, [=]{ - changeActiveNote(nullptr); - setPressedNote(nullptr); - m_touchHideTimer->stop(); - }); - m_enterTimer = new QTimer(this); - m_enterTimer->setSingleShot(true); - connect(m_enterTimer, &QTimer::timeout, this, &TscoreObject::enterTimeElapsed); - m_leaveTimer = new QTimer(this); - m_leaveTimer->setSingleShot(true); - connect(m_leaveTimer, &QTimer::timeout, this, &TscoreObject::leaveTimeElapsed); - m_bgColor = qApp->palette().base().color(); +TscoreObject::~TscoreObject() +{ + delete m_meter; + delete m_qmlComponent; + delete m_workRhythm; + qDeleteAll(m_segments); + qDeleteAll(m_spareSegments); } +// ################################################################################################# +// ################### Musical parameters ############################################ +// ################################################################################################# -TscoreObject::~TscoreObject() +void TscoreObject::setClefType(Tclef::EclefType ct) { - delete m_meter; - delete m_qmlComponent; - delete m_workRhythm; - qDeleteAll(m_segments); - qDeleteAll(m_spareSegments); -} - -//################################################################################################# -//################### Musical parameters ############################################ -//################################################################################################# - -void TscoreObject::setClefType(Tclef::EclefType ct) { - if (m_clefType != ct) { - auto oldClef = m_clefType; - m_clefType = ct; - updateClefOffset(); - emit clefTypeChanged(); - - if (notesCount() > 0) { - bool pianoChanged = (oldClef == Tclef::PianoStaffClefs && m_clefType != Tclef::PianoStaffClefs) - || (oldClef != Tclef::PianoStaffClefs && m_clefType == Tclef::PianoStaffClefs); - bool fixBeam = false; - int rtmGrToCheck = 0; - for (int n = 0; n < notesCount(); ++n) { - auto noteSeg = m_segments[n]; - if (pianoChanged) - noteSeg->item()->setHeight(m_clefType == Tclef::PianoStaffClefs ? 49.0 : 38.0); - if (m_clefType == Tclef::NoClef) { - Tnote newNote(Tnote(), noteSeg->note()->rtm); - newNote.rtm.setStemDown(false); - noteSeg->item()->setStemHeight(STEM_HEIGHT); - noteSeg->setNote(newNote); - } else { - Tnote newNote(*noteSeg->note()); - if (oldClef == Tclef::NoClef) { - int globalNr = m_clefOffset.octave * 7 - (7 - m_clefOffset.note); - newNote.setNote(static_cast<char>(56 + globalNr) % 7 + 1); - newNote.setOctave(static_cast<char>(56 + globalNr) / 7 - 8); - } else - fitToRange(newNote); - - if (m_clefType == Tclef::PianoStaffClefs - && ((newNote.chromatic() < 8 && newNote.onUpperStaff()) || !newNote.onUpperStaff())) - { // find when to fix beaming due to note went to/from grand staff - newNote.setOnUpperStaff(false); - if (newNote.rhythm() > Trhythm::Quarter) - fixBeam = true; - } else if (pianoChanged && m_clefType != Tclef::PianoStaffClefs) { - // or merge beams when note from the same rhythmic group where on different staves - if (!newNote.onUpperStaff() && newNote.rhythm() > Trhythm::Quarter) - fixBeam = true; - } - - noteSeg->setNote(newNote); - - if (pianoChanged) { - int nextRtmGr = (n == notesCount() - 1 ? -1 : m_segments[n + 1]->rhythmGroup()); - bool lastInBar = (noteSeg == noteSeg->item()->measure()->last()); - if (fixBeam && (nextRtmGr != rtmGrToCheck || lastInBar)) // summarize beaming at the end of group or measure - noteSeg->item()->measure()->resolveBeaming(rtmGrToCheck, rtmGrToCheck); - if (nextRtmGr != rtmGrToCheck || lastInBar) { - fixBeam = false; // reset beam fix for next group checking - rtmGrToCheck = nextRtmGr; + if (m_clefType != ct) { + auto oldClef = m_clefType; + m_clefType = ct; + updateClefOffset(); + emit clefTypeChanged(); + + if (notesCount() > 0) { + bool pianoChanged = (oldClef == Tclef::PianoStaffClefs && m_clefType != Tclef::PianoStaffClefs) + || (oldClef != Tclef::PianoStaffClefs && m_clefType == Tclef::PianoStaffClefs); + bool fixBeam = false; + int rtmGrToCheck = 0; + for (int n = 0; n < notesCount(); ++n) { + auto noteSeg = m_segments[n]; + if (pianoChanged) + noteSeg->item()->setHeight(m_clefType == Tclef::PianoStaffClefs ? 49.0 : 38.0); + if (m_clefType == Tclef::NoClef) { + Tnote newNote(Tnote(), noteSeg->note()->rtm); + newNote.rtm.setStemDown(false); + noteSeg->item()->setStemHeight(STEM_HEIGHT); + noteSeg->setNote(newNote); + } else { + Tnote newNote(*noteSeg->note()); + if (oldClef == Tclef::NoClef) { + int globalNr = m_clefOffset.octave * 7 - (7 - m_clefOffset.note); + newNote.setNote(static_cast<char>(56 + globalNr) % 7 + 1); + newNote.setOctave(static_cast<char>(56 + globalNr) / 7 - 8); + } else + fitToRange(newNote); + + if (m_clefType == Tclef::PianoStaffClefs + && ((newNote.chromatic() < 8 && newNote.onUpperStaff()) + || !newNote.onUpperStaff())) { // find when to fix beaming due to note went to/from grand staff + newNote.setOnUpperStaff(false); + if (newNote.rhythm() > Trhythm::Quarter) + fixBeam = true; + } else if (pianoChanged && m_clefType != Tclef::PianoStaffClefs) { + // or merge beams when note from the same rhythmic group where on different staves + if (!newNote.onUpperStaff() && newNote.rhythm() > Trhythm::Quarter) + fixBeam = true; + } + + noteSeg->setNote(newNote); + + if (pianoChanged) { + int nextRtmGr = (n == notesCount() - 1 ? -1 : m_segments[n + 1]->rhythmGroup()); + bool lastInBar = (noteSeg == noteSeg->item()->measure()->last()); + if (fixBeam && (nextRtmGr != rtmGrToCheck || lastInBar)) // summarize beaming at the end of group or measure + noteSeg->item()->measure()->resolveBeaming(rtmGrToCheck, rtmGrToCheck); + if (nextRtmGr != rtmGrToCheck || lastInBar) { + fixBeam = false; // reset beam fix for next group checking + rtmGrToCheck = nextRtmGr; + } + } } - } - } + } + for (int m = 0; m < m_measures.count(); ++m) + m_measures[m]->refresh(); + if (!pianoChanged) // otherwise adjustScoreWidth() will be called due to score scale changes + adjustScoreWidth(); } - for (int m = 0; m < m_measures.count(); ++m) - m_measures[m]->refresh(); - if (!pianoChanged) // otherwise adjustScoreWidth() will be called due to score scale changes - adjustScoreWidth(); } - } } - /** * When meter is changed and there are notes on the score, all segments are flushed and stored * then measures and staves are deleted, * then all notes are added from scratch, but stored segments are reused to improve single segment creation time */ -void TscoreObject::setMeter(int m) { - Tmeter::Emeter newMeter = static_cast<Tmeter::Emeter>(m); - Tmeter::Emeter prevMeter = m_meter->meter(); - if (m_meter->meter() != newMeter) { - // set notes grouping - m_meter->setMeter(newMeter); - m_meter->fillMeterGroups(m_meterGroups); - if (measuresCount()) - firstMeasure()->meterChanged(); - emit meterChanged(); - setWorkRhythm(Trhythm(newMeter == Tmeter::NoMeter ? Trhythm::NoRhythm : (newMeter <= Tmeter::Meter_7_4 ? Trhythm::Quarter : Trhythm::Eighth))); - - if (!m_singleNote && measuresCount() && firstMeasure()->noteCount() > 0) { - clearScorePrivate(); - QList<Tnote> oldList = m_notes; - m_notes.clear(); - for (int n = 0; n < oldList.size(); ++n) { - // TODO fold (merge) all ties - if (m_meter->meter() == Tmeter::NoMeter) // remove rhythm then - oldList[n].setRhythm(Trhythm(Trhythm::NoRhythm)); - if (prevMeter == Tmeter::NoMeter) // initialize empty rhythm - oldList[n].setRhythm(Trhythm()); // quarter by default - addNote(oldList[n]); - } - m_activeBarNr = 0; - adjustScoreWidth(); +void TscoreObject::setMeter(int m) +{ + Tmeter::Emeter newMeter = static_cast<Tmeter::Emeter>(m); + Tmeter::Emeter prevMeter = m_meter->meter(); + if (m_meter->meter() != newMeter) { + // set notes grouping + m_meter->setMeter(newMeter); + m_meter->fillMeterGroups(m_meterGroups); + if (measuresCount()) + firstMeasure()->meterChanged(); + emit meterChanged(); + setWorkRhythm(Trhythm(newMeter == Tmeter::NoMeter ? Trhythm::NoRhythm : (newMeter <= Tmeter::Meter_7_4 ? Trhythm::Quarter : Trhythm::Eighth))); + + if (!m_singleNote && measuresCount() && firstMeasure()->noteCount() > 0) { + clearScorePrivate(); + QList<Tnote> oldList = m_notes; + m_notes.clear(); + for (int n = 0; n < oldList.size(); ++n) { + // TODO fold (merge) all ties + if (m_meter->meter() == Tmeter::NoMeter) // remove rhythm then + oldList[n].setRhythm(Trhythm(Trhythm::NoRhythm)); + if (prevMeter == Tmeter::NoMeter) // initialize empty rhythm + oldList[n].setRhythm(Trhythm()); // quarter by default + addNote(oldList[n]); + } + m_activeBarNr = 0; + adjustScoreWidth(); + } + emitLastNote(); } - emitLastNote(); - } } +int TscoreObject::meterToInt() const +{ + return static_cast<int>(m_meter->meter()); +} -int TscoreObject::meterToInt() const { return static_cast<int>(m_meter->meter()); } - - -void TscoreObject::setKeySignature(int k) { - qint8 key = static_cast<qint8>(k); - if (m_keySignEnabled && key != m_keySignature) { - if (key != m_keySignature) { - m_keySignature = key; - for (int i = 1; i < 8; i++) { - qint8 sign = k < 0 ? -1 : 1; - int startVal = k < 0 ? 38 : 48; - if (i <= qAbs(k)) - m_accidInKeyArray[(startVal + sign * (i * 4)) % 7] = sign; - else - m_accidInKeyArray[(startVal + sign * (i * 4)) % 7] = 0; - } - m_keyChanged = true; - for (TmeasureObject* m : qAsConst(m_measures)) - m->keySignatureChanged(); - if (notesCount() > 0) - adjustScoreWidth(); - emit keySignatureChanged(); +void TscoreObject::setKeySignature(int k) +{ + qint8 key = static_cast<qint8>(k); + if (m_keySignEnabled && key != m_keySignature) { + if (key != m_keySignature) { + m_keySignature = key; + for (int i = 1; i < 8; i++) { + qint8 sign = k < 0 ? -1 : 1; + int startVal = k < 0 ? 38 : 48; + if (i <= qAbs(k)) + m_accidInKeyArray[(startVal + sign * (i * 4)) % 7] = sign; + else + m_accidInKeyArray[(startVal + sign * (i * 4)) % 7] = 0; + } + m_keyChanged = true; + for (TmeasureObject *m : qAsConst(m_measures)) + m->keySignatureChanged(); + if (notesCount() > 0) + adjustScoreWidth(); + emit keySignatureChanged(); + } } - } } - /** @p static * This method creates @p outList of notes that have pitch of @p n note * but splits @p dur duration into possible rhythmic values. */ -void solveList(const Tnote& n, int dur, QList<Tnote>& outList) { - Trhythm rtm(dur); // try to obtain rhythm value - if (!rtm.isValid()) { // if it is not possible to express the duration in single rhythm - resolve it in multiple values - TrhythmList solvList; - Trhythm::resolve(dur, solvList); - for (int r = 0; r < solvList.count(); ++r) { - outList << Tnote(n, Trhythm(solvList[r].rhythm(), n.isRest(), solvList[r].hasDot(), solvList[r].isTriplet())); - } - } else { // just single note in the list - rtm.setRest(n.isRest()); - outList << Tnote(n, rtm); - } -} - - -void TscoreObject::addNote(const Tnote& newNote, bool fromQML) { - if (m_singleNote) { - qDebug() << "[TscoreObject] FIXME! Trying to add note in single mode"; - return; - } - - auto lastMeasure = m_measures.last(); - if (lastMeasure->free() == 0) { // new measure is needed - lastMeasure = getMeasure(m_measures.count()); - m_measures << lastMeasure; - lastStaff()->appendMeasure(lastMeasure); - } - - Tnote n = newNote; - fitToRange(n); - int noteDur = n.rhythm() == Trhythm::NoRhythm || m_meter->meter() == Tmeter::NoMeter ? 1 : n.duration(); - if (noteDur > lastMeasure->free()) { // split note that is adding - int leftDuration = noteDur - lastMeasure->free(); - int lastNoteId = m_segments.count(); - - QList<Tnote> notesToCurrent; - solveList(n, lastMeasure->free(), notesToCurrent); // solve free duration in current measure - if (notesToCurrent.isEmpty()) - qDebug() << "[TscoreObject] can't resolve duration of" << lastMeasure->free(); - else { - if (!n.isRest()) { - notesToCurrent.first().rtm.setTie(newNote.rtm.tie() > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); - if (notesToCurrent.count() == 2) - notesToCurrent.last().rtm.setTie(Trhythm::e_tieCont); - } - appendToNoteList(notesToCurrent); - lastMeasure->appendNewNotes(lastNoteId, notesToCurrent.count()); - } - - QList<Tnote> notesToNext; - solveList(n, leftDuration, notesToNext); // solve remaining duration for the next measure - lastNoteId = m_segments.count(); // update id of the last note segment - if (notesToNext.isEmpty()) - qDebug() << "[TscoreObject] can't resolve duration" << leftDuration; - else { - if (!n.isRest()) { - if (notesToNext.count() == 1) - notesToNext.first().rtm.setTie(Trhythm::e_tieEnd); - else { - notesToNext.first().rtm.setTie(Trhythm::e_tieCont); - notesToNext.last().rtm.setTie(Trhythm::e_tieEnd); - } - } - appendToNoteList(notesToNext); - auto newLastMeasure = getMeasure(m_measures.count()); // new TmeasureObject(m_measures.count(), this); // add a new measure - m_measures << newLastMeasure; - lastStaff()->appendMeasure(newLastMeasure); - newLastMeasure->appendNewNotes(lastNoteId, notesToNext.count()); - } - } else { // just add new note to the last measure - m_notes << n; - int lastNoteId = m_segments.count(); - m_segments << getSegment(lastNoteId, &m_notes.last()); - lastMeasure->appendNewNotes(lastNoteId, 1); - } - emitLastNote(); - if (fromQML) { - emit noteWasAdded(); - } -} - - -void TscoreObject::setNote(TnoteItem* no, const Tnote& n) { - if (no) { - if (m_allowAdding && m_meter->meter() != Tmeter::NoMeter && no == lastNote() && no->note()->rtm != n.rtm) { - deleteLastNote(); - addNote(n); - emitLastNote(); - return; - } - - int durDiff = no->note()->duration() - n.duration(); - - auto oldNote = *no->wrapper()->note(); - auto newNote = n; - if (!durDiff) { - newNote.rtm.setBeam(oldNote.rtm.beam()); - newNote.rtm.setTie(oldNote.rtm.tie()); - } - fitToRange(newNote); - bool fitStaff = false; - // disconnect tie (if any) if note pitch changed - QPoint notesForAlterCheck;// x is first note and y is the last note to check - if (oldNote.rtm.tie() && newNote.chromatic() != oldNote.chromatic()) { - // alters of notes has to be checked due to note changed - // and all measures contained note with the tie are affected. Find them then - notesForAlterCheck = tieRange(no); - notesForAlterCheck.setX(m_segments[notesForAlterCheck.x()]->item()->measure()->firstNoteId()); - notesForAlterCheck.setY(m_segments[notesForAlterCheck.y()]->item()->measure()->lastNoteId()); - if (oldNote.rtm.tie() == Trhythm::e_tieStart) { - m_segments[no->index() + 1]->disconnectTie(TnotePair::e_untieNext); - } else { // tie continue or end - if (oldNote.rtm.tie() == Trhythm::e_tieCont) - m_segments[no->index() + 1]->disconnectTie(TnotePair::e_untieNext); - m_segments[no->index() - 1]->disconnectTie(TnotePair::e_untiePrev); - } - newNote.rtm.setTie(Trhythm::e_noTie); - if (no->wrapper() == no->staff()->firstNote()) - no->staff()->deleteExtraTie(); - fitStaff = true; - } - - if (durDiff) { - no->measure()->changeNoteDuration(no->wrapper(), newNote); - if (lastMeasure()->isEmpty()) - removeLastMeasure(); - adjustScoreWidth(); - } else { - no->wrapper()->setNote(newNote); - // When note or alter are different - check accidentals in whole measure and fit staff if necessary - if (!notesForAlterCheck.isNull() || oldNote.note() != newNote.note() || oldNote.alter() != newNote.alter()) { - if (notesForAlterCheck.isNull()) - notesForAlterCheck = QPoint(no->measure()->firstNoteId(), no->measure()->lastNoteId()); - auto measureToRefresh = m_segments[notesForAlterCheck.x()]->item()->measure(); - for (int n = notesForAlterCheck.x(); n <= notesForAlterCheck.y(); ++n) { - if (m_segments[n]->note()->note() == oldNote.note() || m_segments[n]->note()->note() == newNote.note()) { - fitStaff = true; - m_segments[n]->item()->updateAlter(); - } - // Update measure sums (notes width) - if (m_segments[n]->item()->measure() != measureToRefresh) { - measureToRefresh->refresh(); - measureToRefresh = m_segments[n]->item()->measure(); - } - } - measureToRefresh->refresh(); - } - // update note range on current staff - if (oldNote.note() != newNote.note() || oldNote.octave() != newNote.octave()) - no->staff()->checkNotesRange(); - // If there is a beam - prepare it again then draw - if (no->wrapper()->beam()) { - no->wrapper()->beam()->prepareBeam(); - if (!fitStaff) - no->wrapper()->beam()->drawBeam(); - } - if (fitStaff) { - m_segments[notesForAlterCheck.x()]->item()->staff()->fit(); - if (m_segments[notesForAlterCheck.y()]->item()->staff() != m_segments[notesForAlterCheck.x()]->item()->staff()) - m_segments[notesForAlterCheck.y()]->item()->staff()->fit(); +void solveList(const Tnote &n, int dur, QList<Tnote> &outList) +{ + Trhythm rtm(dur); // try to obtain rhythm value + if (!rtm.isValid()) { // if it is not possible to express the duration in single rhythm - resolve it in multiple values + TrhythmList solvList; + Trhythm::resolve(dur, solvList); + for (int r = 0; r < solvList.count(); ++r) { + outList << Tnote(n, Trhythm(solvList[r].rhythm(), n.isRest(), solvList[r].hasDot(), solvList[r].isTriplet())); } + } else { // just single note in the list + rtm.setRest(n.isRest()); + outList << Tnote(n, rtm); } +} - if (no == m_selectedItem) - emit selectedNoteChanged(); - if (m_singleNote && m_enharmNotesEnabled && n.isValid()) { - TnotesList enharmList = newNote.getTheSameNotes(m_enableDoubleAccids); - TnotesList::iterator it = enharmList.begin(); - ++it; - if (it != enharmList.end()) { - note(1)->setVisible(true); - m_segments[1]->setNote(*(it)); - } else - note(1)->setVisible(false); - ++it; - if (it != enharmList.end()) { - note(2)->setVisible(true); - m_segments[2]->setNote(*(it)); - } else - note(2)->setVisible(false); +void TscoreObject::addNote(const Tnote &newNote, bool fromQML) +{ + if (m_singleNote) { + qDebug() << "[TscoreObject] FIXME! Trying to add note in single mode"; + return; } - } -} + auto lastMeasure = m_measures.last(); + if (lastMeasure->free() == 0) { // new measure is needed + lastMeasure = getMeasure(m_measures.count()); + m_measures << lastMeasure; + lastStaff()->appendMeasure(lastMeasure); + } -void TscoreObject::setNote(int noteNr, const Tnote& n) { - if (noteNr >= 0 && noteNr < notesCount()) - setNote(note(noteNr), n); - else - qDebug() << "[TscoreObject FIXME] Trying to set note of item that doesn't exist!" << noteNr; + Tnote n = newNote; + fitToRange(n); + int noteDur = n.rhythm() == Trhythm::NoRhythm || m_meter->meter() == Tmeter::NoMeter ? 1 : n.duration(); + if (noteDur > lastMeasure->free()) { // split note that is adding + int leftDuration = noteDur - lastMeasure->free(); + int lastNoteId = m_segments.count(); + + QList<Tnote> notesToCurrent; + solveList(n, lastMeasure->free(), notesToCurrent); // solve free duration in current measure + if (notesToCurrent.isEmpty()) + qDebug() << "[TscoreObject] can't resolve duration of" << lastMeasure->free(); + else { + if (!n.isRest()) { + notesToCurrent.first().rtm.setTie(newNote.rtm.tie() > Trhythm::e_tieStart ? Trhythm::e_tieCont : Trhythm::e_tieStart); + if (notesToCurrent.count() == 2) + notesToCurrent.last().rtm.setTie(Trhythm::e_tieCont); + } + appendToNoteList(notesToCurrent); + lastMeasure->appendNewNotes(lastNoteId, notesToCurrent.count()); + } + + QList<Tnote> notesToNext; + solveList(n, leftDuration, notesToNext); // solve remaining duration for the next measure + lastNoteId = m_segments.count(); // update id of the last note segment + if (notesToNext.isEmpty()) + qDebug() << "[TscoreObject] can't resolve duration" << leftDuration; + else { + if (!n.isRest()) { + if (notesToNext.count() == 1) + notesToNext.first().rtm.setTie(Trhythm::e_tieEnd); + else { + notesToNext.first().rtm.setTie(Trhythm::e_tieCont); + notesToNext.last().rtm.setTie(Trhythm::e_tieEnd); + } + } + appendToNoteList(notesToNext); + auto newLastMeasure = getMeasure(m_measures.count()); // new TmeasureObject(m_measures.count(), this); // add a new measure + m_measures << newLastMeasure; + lastStaff()->appendMeasure(newLastMeasure); + newLastMeasure->appendNewNotes(lastNoteId, notesToNext.count()); + } + } else { // just add new note to the last measure + m_notes << n; + int lastNoteId = m_segments.count(); + m_segments << getSegment(lastNoteId, &m_notes.last()); + lastMeasure->appendNewNotes(lastNoteId, 1); + } + emitLastNote(); + if (fromQML) { + emit noteWasAdded(); + } } +void TscoreObject::setNote(TnoteItem *no, const Tnote &n) +{ + if (no) { + if (m_allowAdding && m_meter->meter() != Tmeter::NoMeter && no == lastNote() && no->note()->rtm != n.rtm) { + deleteLastNote(); + addNote(n); + emitLastNote(); + return; + } + + int durDiff = no->note()->duration() - n.duration(); -void TscoreObject::setTechnical(int noteId, quint32 tech) { - if (noteId >= 0 && noteId < notesCount()) - noteSegment(noteId)->setTechnical(tech); -} + auto oldNote = *no->wrapper()->note(); + auto newNote = n; + if (!durDiff) { + newNote.rtm.setBeam(oldNote.rtm.beam()); + newNote.rtm.setTie(oldNote.rtm.tie()); + } + fitToRange(newNote); + bool fitStaff = false; + // disconnect tie (if any) if note pitch changed + QPoint notesForAlterCheck; // x is first note and y is the last note to check + if (oldNote.rtm.tie() && newNote.chromatic() != oldNote.chromatic()) { + // alters of notes has to be checked due to note changed + // and all measures contained note with the tie are affected. Find them then + notesForAlterCheck = tieRange(no); + notesForAlterCheck.setX(m_segments[notesForAlterCheck.x()]->item()->measure()->firstNoteId()); + notesForAlterCheck.setY(m_segments[notesForAlterCheck.y()]->item()->measure()->lastNoteId()); + if (oldNote.rtm.tie() == Trhythm::e_tieStart) { + m_segments[no->index() + 1]->disconnectTie(TnotePair::e_untieNext); + } else { // tie continue or end + if (oldNote.rtm.tie() == Trhythm::e_tieCont) + m_segments[no->index() + 1]->disconnectTie(TnotePair::e_untieNext); + m_segments[no->index() - 1]->disconnectTie(TnotePair::e_untiePrev); + } + newNote.rtm.setTie(Trhythm::e_noTie); + if (no->wrapper() == no->staff()->firstNote()) + no->staff()->deleteExtraTie(); + fitStaff = true; + } + if (durDiff) { + no->measure()->changeNoteDuration(no->wrapper(), newNote); + if (lastMeasure()->isEmpty()) + removeLastMeasure(); + adjustScoreWidth(); + } else { + no->wrapper()->setNote(newNote); + // When note or alter are different - check accidentals in whole measure and fit staff if necessary + if (!notesForAlterCheck.isNull() || oldNote.note() != newNote.note() || oldNote.alter() != newNote.alter()) { + if (notesForAlterCheck.isNull()) + notesForAlterCheck = QPoint(no->measure()->firstNoteId(), no->measure()->lastNoteId()); + auto measureToRefresh = m_segments[notesForAlterCheck.x()]->item()->measure(); + for (int n = notesForAlterCheck.x(); n <= notesForAlterCheck.y(); ++n) { + if (m_segments[n]->note()->note() == oldNote.note() || m_segments[n]->note()->note() == newNote.note()) { + fitStaff = true; + m_segments[n]->item()->updateAlter(); + } + // Update measure sums (notes width) + if (m_segments[n]->item()->measure() != measureToRefresh) { + measureToRefresh->refresh(); + measureToRefresh = m_segments[n]->item()->measure(); + } + } + measureToRefresh->refresh(); + } + // update note range on current staff + if (oldNote.note() != newNote.note() || oldNote.octave() != newNote.octave()) + no->staff()->checkNotesRange(); + // If there is a beam - prepare it again then draw + if (no->wrapper()->beam()) { + no->wrapper()->beam()->prepareBeam(); + if (!fitStaff) + no->wrapper()->beam()->drawBeam(); + } + if (fitStaff) { + m_segments[notesForAlterCheck.x()]->item()->staff()->fit(); + if (m_segments[notesForAlterCheck.y()]->item()->staff() != m_segments[notesForAlterCheck.x()]->item()->staff()) + m_segments[notesForAlterCheck.y()]->item()->staff()->fit(); + } + } -TnoteItem* TscoreObject::note(int noteId) { - return noteId > -1 && noteId < notesCount() ? m_segments[noteId]->item() : nullptr; + if (no == m_selectedItem) + emit selectedNoteChanged(); + if (m_singleNote && m_enharmNotesEnabled && n.isValid()) { + TnotesList enharmList = newNote.getTheSameNotes(m_enableDoubleAccids); + TnotesList::iterator it = enharmList.begin(); + ++it; + if (it != enharmList.end()) { + note(1)->setVisible(true); + m_segments[1]->setNote(*(it)); + } else + note(1)->setVisible(false); + ++it; + if (it != enharmList.end()) { + note(2)->setVisible(true); + m_segments[2]->setNote(*(it)); + } else + note(2)->setVisible(false); + } + } } - -QQuickItem* TscoreObject::noteHead(int noteId) { - return noteId > -1 && noteId < notesCount() ? m_segments[noteId]->item()->head() : nullptr; +void TscoreObject::setNote(int noteNr, const Tnote &n) +{ + if (noteNr >= 0 && noteNr < notesCount()) + setNote(note(noteNr), n); + else + qDebug() << "[TscoreObject FIXME] Trying to set note of item that doesn't exist!" << noteNr; } +void TscoreObject::setTechnical(int noteId, quint32 tech) +{ + if (noteId >= 0 && noteId < notesCount()) + noteSegment(noteId)->setTechnical(tech); +} -Tnote TscoreObject::noteOfItem(TnoteItem* item) const { - return item ? *item->note() : Tnote(); +TnoteItem *TscoreObject::note(int noteId) +{ + return noteId > -1 && noteId < notesCount() ? m_segments[noteId]->item() : nullptr; } +QQuickItem *TscoreObject::noteHead(int noteId) +{ + return noteId > -1 && noteId < notesCount() ? m_segments[noteId]->item()->head() : nullptr; +} -Tnote TscoreObject::noteAt(int index) const { - return index >= 0 && index < m_notes.count() ? m_notes[index] : Tnote(); +Tnote TscoreObject::noteOfItem(TnoteItem *item) const +{ + return item ? *item->note() : Tnote(); } +Tnote TscoreObject::noteAt(int index) const +{ + return index >= 0 && index < m_notes.count() ? m_notes[index] : Tnote(); +} -void TscoreObject::noteClicked(qreal yPos) { - if (!activeNote()) - return; +void TscoreObject::noteClicked(qreal yPos) +{ + if (!activeNote()) + return; - Trhythm newRhythm = m_meter->meter() == Tmeter::NoMeter ? Trhythm(Trhythm::NoRhythm) : *m_workRhythm; + Trhythm newRhythm = m_meter->meter() == Tmeter::NoMeter ? Trhythm(Trhythm::NoRhythm) : *m_workRhythm; - int globalNr = globalNoteNr(yPos); - Tnote newNote(static_cast<char>(56 + globalNr) % 7 + 1, static_cast<char>(56 + globalNr) / 7 - 8, - static_cast<char>(m_cursorAlter), newRhythm); - newNote.setOnUpperStaff(!(isPianoStaff() && yPos > upperLine() + 13.0)); - if (m_workRhythm->isRest() || m_clefType == Tclef::NoClef) - newNote.setNote(0); + int globalNr = globalNoteNr(yPos); + Tnote newNote(static_cast<char>(56 + globalNr) % 7 + 1, static_cast<char>(56 + globalNr) / 7 - 8, static_cast<char>(m_cursorAlter), newRhythm); + newNote.setOnUpperStaff(!(isPianoStaff() && yPos > upperLine() + 13.0)); + if (m_workRhythm->isRest() || m_clefType == Tclef::NoClef) + newNote.setNote(0); - // when note changed staff upper/lower but rhythm is the same - // we have to resolve beaming and fit staff here, setNote() will not preform that - bool fixBeam = isPianoStaff() && newNote.rhythm() > Trhythm::Quarter - && m_activeNote && m_activeNote->note()->onUpperStaff() != newNote.onUpperStaff() - && newNote.isValid() && m_activeNote->note()->rtm == newRhythm; - bool selectLastNoteAgain = m_activeNote == lastNote() && m_activeNote->note()->rtm != newRhythm; + // when note changed staff upper/lower but rhythm is the same + // we have to resolve beaming and fit staff here, setNote() will not preform that + bool fixBeam = isPianoStaff() && newNote.rhythm() > Trhythm::Quarter && m_activeNote && m_activeNote->note()->onUpperStaff() != newNote.onUpperStaff() + && newNote.isValid() && m_activeNote->note()->rtm == newRhythm; + bool selectLastNoteAgain = m_activeNote == lastNote() && m_activeNote->note()->rtm != newRhythm; - setNote(m_activeNote, newNote); - setSelectedItem(m_activeNote); + setNote(m_activeNote, newNote); + setSelectedItem(m_activeNote); - if (fixBeam) { - m_activeNote->measure()->resolveBeaming(m_activeNote->wrapper()->rhythmGroup(), m_activeNote->wrapper()->rhythmGroup()); - m_activeNote->staff()->fit(); - } + if (fixBeam) { + m_activeNote->measure()->resolveBeaming(m_activeNote->wrapper()->rhythmGroup(), m_activeNote->wrapper()->rhythmGroup()); + m_activeNote->staff()->fit(); + } - if (selectLastNoteAgain) { // clicked note is the last one and changed its rhythm, so it was deleted and added again - m_activeNote = lastNote(); - setSelectedItem(lastNote()); - emit activeNoteChanged(); - } - - emit clicked(); -} - - -void TscoreObject::setCursorAlter(int curAlt) { - curAlt = qBound(m_enableDoubleAccids ? -2 : -1, curAlt, m_enableDoubleAccids ? 2 : 1); - if (curAlt != m_cursorAlter) { - m_cursorAlter = curAlt; - emit cursorAlterChanged(); - } -} - - -QString TscoreObject::alterText() { - static const QString accids = QStringLiteral("\ue264\ue260 \ue262\ue263"); - return accids.mid(m_cursorAlter + 2, 1); -} - - -void TscoreObject::openMusicXml(const QString& musicFile, Tmelody* melody, bool ignoreTechnical) { - if (!musicFile.isEmpty()) { - bool melodyCreated = false; - if (!melody) { - melody = new Tmelody(); - melodyCreated = true; + if (selectLastNoteAgain) { // clicked note is the last one and changed its rhythm, so it was deleted and added again + m_activeNote = lastNote(); + setSelectedItem(lastNote()); + emit activeNoteChanged(); } - if (melody->grabFromMusicXml(musicFile)) - setMelody(melody, ignoreTechnical); - if (melodyCreated) - delete melody; - } -} + emit clicked(); +} -void TscoreObject::saveMusicXml(const QString& musicFile, const QString& title, - const QString& composer, int transposition) { - if (!musicFile.isEmpty()) { - QString fileName = musicFile; - if (musicFile.right(4) != QLatin1String(".xml") && musicFile.right(9) != QLatin1String(".musicxml") && musicFile.right(4) != QLatin1String(".mxl")) - fileName += QLatin1String(".musicxml"); // prefer musicxml extension - auto melody = new Tmelody(title, TkeySignature(static_cast<char>(keySignature()))); - getMelody(melody); - melody->setComposer(composer); - melody->saveToMusicXml(fileName, transposition); - delete melody; - } +void TscoreObject::setCursorAlter(int curAlt) +{ + curAlt = qBound(m_enableDoubleAccids ? -2 : -1, curAlt, m_enableDoubleAccids ? 2 : 1); + if (curAlt != m_cursorAlter) { + m_cursorAlter = curAlt; + emit cursorAlterChanged(); + } } +QString TscoreObject::alterText() +{ + static const QString accids = QStringLiteral("\ue264\ue260 \ue262\ue263"); + return accids.mid(m_cursorAlter + 2, 1); +} -void TscoreObject::setMelody(Tmelody* melody, bool ignoreTechnical, int notesAmount, int transposition) { - clearScorePrivate(); - m_notes.clear(); - setMeter(static_cast<int>(melody->meter()->meter())); - setClefType(melody->clef()); - int newKey = static_cast<int>(melody->key().value()); - if (newKey != keySignature()) { - if (!m_keySignEnabled && qAbs(newKey) != 0) - setKeySignatureEnabled(true); - setKeySignature(newKey); - } - int notesToCopy = notesAmount == 0 ? melody->length() : qMin(notesAmount, melody->length()); - for (int n = 0; n < notesToCopy; ++n) { - Tnote& note = melody->note(n)->p(); - // when note is lower than 7 - 'g small' (the lowest supported note on the upper staff) - // set it to lower staff (upper to false) to beam notes correctly - if (melody->clef() == Tclef::PianoStaffClefs && note.chromatic() < 8 && !note.isRest() && note.onUpperStaff()) - note.setOnUpperStaff(false); - if (transposition) { - Tnote nn = note; - nn.transpose(transposition); - if (m_keySignature < 0 && nn.alter() == Tnote::e_Sharp) - nn = nn.showWithFlat(); - addNote(nn); - } else - addNote(note); - if (!ignoreTechnical) - lastSegment()->setTechnical(melody->note(n)->technical()); - } - adjustScoreWidth(); - emitLastNote(); +void TscoreObject::openMusicXml(const QString &musicFile, Tmelody *melody, bool ignoreTechnical) +{ + if (!musicFile.isEmpty()) { + bool melodyCreated = false; + if (!melody) { + melody = new Tmelody(); + melodyCreated = true; + } + if (melody->grabFromMusicXml(musicFile)) + setMelody(melody, ignoreTechnical); + if (melodyCreated) + delete melody; + } } +void TscoreObject::saveMusicXml(const QString &musicFile, const QString &title, const QString &composer, int transposition) +{ + if (!musicFile.isEmpty()) { + QString fileName = musicFile; + if (musicFile.right(4) != QLatin1String(".xml") && musicFile.right(9) != QLatin1String(".musicxml") && musicFile.right(4) != QLatin1String(".mxl")) + fileName += QLatin1String(".musicxml"); // prefer musicxml extension + auto melody = new Tmelody(title, TkeySignature(static_cast<char>(keySignature()))); + getMelody(melody); + melody->setComposer(composer); + melody->saveToMusicXml(fileName, transposition); + delete melody; + } +} -void TscoreObject::getMelody(Tmelody* melody) { - melody->setClef(clefType()); - melody->setMeter(m_meter->meter()); - if (m_keySignEnabled) - melody->setKey(TkeySignature(static_cast<char>(m_keySignature))); - for (int n = 0; n < notesCount(); ++n) { - Ttechnical technical(noteSegment(n)->techicalData()); - melody->addNote(Tchunk(m_notes[n], technical)); - } +void TscoreObject::setMelody(Tmelody *melody, bool ignoreTechnical, int notesAmount, int transposition) +{ + clearScorePrivate(); + m_notes.clear(); + setMeter(static_cast<int>(melody->meter()->meter())); + setClefType(melody->clef()); + int newKey = static_cast<int>(melody->key().value()); + if (newKey != keySignature()) { + if (!m_keySignEnabled && qAbs(newKey) != 0) + setKeySignatureEnabled(true); + setKeySignature(newKey); + } + int notesToCopy = notesAmount == 0 ? melody->length() : qMin(notesAmount, melody->length()); + for (int n = 0; n < notesToCopy; ++n) { + Tnote ¬e = melody->note(n)->p(); + // when note is lower than 7 - 'g small' (the lowest supported note on the upper staff) + // set it to lower staff (upper to false) to beam notes correctly + if (melody->clef() == Tclef::PianoStaffClefs && note.chromatic() < 8 && !note.isRest() && note.onUpperStaff()) + note.setOnUpperStaff(false); + if (transposition) { + Tnote nn = note; + nn.transpose(transposition); + if (m_keySignature < 0 && nn.alter() == Tnote::e_Sharp) + nn = nn.showWithFlat(); + addNote(nn); + } else + addNote(note); + if (!ignoreTechnical) + lastSegment()->setTechnical(melody->note(n)->technical()); + } + adjustScoreWidth(); + emitLastNote(); } +void TscoreObject::getMelody(Tmelody *melody) +{ + melody->setClef(clefType()); + melody->setMeter(m_meter->meter()); + if (m_keySignEnabled) + melody->setKey(TkeySignature(static_cast<char>(m_keySignature))); + for (int n = 0; n < notesCount(); ++n) { + Ttechnical technical(noteSegment(n)->techicalData()); + melody->addNote(Tchunk(m_notes[n], technical)); + } +} -//################################################################################################# -//################### Score switches ############################################ -//################################################################################################# +// ################################################################################################# +// ################### Score switches ############################################ +// ################################################################################################# -void TscoreObject::setKeySignatureEnabled(bool enKey) { - if (enKey != m_keySignEnabled) { - if (!enKey) - m_keySignature = 0; - m_keySignEnabled = enKey; - emit keySignatureEnabledChanged(); - if (notesCount() > 0) - adjustScoreWidth(); - } -} - - -void TscoreObject::setKeyReadOnly(bool ro) { - if (m_keySignEnabled) { - if (ro != m_keyReadOnly) { - m_keyReadOnly = ro; - emit keyReadOnlyChanged(); - } - } -} - - -void TscoreObject::setShowExtraAccids(bool accShow) { - if (m_showExtraAccids != accShow) { - m_showExtraAccids = accShow; - if (notesCount()) { - for (int n = 0; n < notesCount(); ++n) - m_segments[n]->item()->keySignatureChanged(); // HACK: it will force refresh accidental symbol - adjustScoreWidth(); +void TscoreObject::setKeySignatureEnabled(bool enKey) +{ + if (enKey != m_keySignEnabled) { + if (!enKey) + m_keySignature = 0; + m_keySignEnabled = enKey; + emit keySignatureEnabledChanged(); + if (notesCount() > 0) + adjustScoreWidth(); } - } } +void TscoreObject::setKeyReadOnly(bool ro) +{ + if (m_keySignEnabled) { + if (ro != m_keyReadOnly) { + m_keyReadOnly = ro; + emit keyReadOnlyChanged(); + } + } +} -void TscoreObject::setEnableDoubleAccids(bool dblEnabled) { - if (m_enableDoubleAccids != dblEnabled) { - m_enableDoubleAccids = dblEnabled; - if (!m_enableDoubleAccids) { - // TODO: convert notes with double accidentals into single-ones +void TscoreObject::setShowExtraAccids(bool accShow) +{ + if (m_showExtraAccids != accShow) { + m_showExtraAccids = accShow; + if (notesCount()) { + for (int n = 0; n < notesCount(); ++n) + m_segments[n]->item()->keySignatureChanged(); // HACK: it will force refresh accidental symbol + adjustScoreWidth(); + } } - } } +void TscoreObject::setEnableDoubleAccids(bool dblEnabled) +{ + if (m_enableDoubleAccids != dblEnabled) { + m_enableDoubleAccids = dblEnabled; + if (!m_enableDoubleAccids) { + // TODO: convert notes with double accidentals into single-ones + } + } +} /** * When @p m_showNoteNames is set to @p TRUE: * @p TmeasureObject during adding a note item calls @p TnoteItem::setNoteNameVisible() to create note name * This method iterates all notes and switches its state of displaying note name */ -void TscoreObject::setShowNoteNames(bool showNames) { - if (m_showNoteNames != showNames) { - m_showNoteNames = showNames; - if (notesCount()) { - for (int n = 0; n < notesCount(); ++n) - m_segments[n]->item()->setNoteNameVisible(m_showNoteNames && m_clefType != Tclef::NoClef && !m_singleNote); +void TscoreObject::setShowNoteNames(bool showNames) +{ + if (m_showNoteNames != showNames) { + m_showNoteNames = showNames; + if (notesCount()) { + for (int n = 0; n < notesCount(); ++n) + m_segments[n]->item()->setNoteNameVisible(m_showNoteNames && m_clefType != Tclef::NoClef && !m_singleNote); + } } - } } - -void TscoreObject::setNameColor(const QColor& nameC) { - if (m_nameColor != nameC) { - m_nameColor = nameC; - if (m_showNoteNames) { - if (notesCount()) { - for (int n = 0; n < notesCount(); ++n) // with hope that all items have name item created - m_segments[n]->item()->nameItem()->setProperty("styleColor", m_nameColor); - } +void TscoreObject::setNameColor(const QColor &nameC) +{ + if (m_nameColor != nameC) { + m_nameColor = nameC; + if (m_showNoteNames) { + if (notesCount()) { + for (int n = 0; n < notesCount(); ++n) // with hope that all items have name item created + m_segments[n]->item()->nameItem()->setProperty("styleColor", m_nameColor); + } + } } - } } - -void TscoreObject::setNameStyle(int nameS) { - m_nameStyle = nameS; - if (m_showNoteNames) { - if (notesCount()) { - for (int n = 0; n < notesCount(); ++n) // with hope that all items have name item created - m_segments[n]->item()->nameItem()->setProperty("text", m_notes[n].styledName()); +void TscoreObject::setNameStyle(int nameS) +{ + m_nameStyle = nameS; + if (m_showNoteNames) { + if (notesCount()) { + for (int n = 0; n < notesCount(); ++n) // with hope that all items have name item created + m_segments[n]->item()->nameItem()->setProperty("text", m_notes[n].styledName()); + } } - } } - -void TscoreObject::setReadOnly(bool ro) { - if (m_readOnly != ro) { - m_readOnly = ro; - emit readOnlyChanged(); - if (m_deleteNoteAct && !m_singleNote) { - m_deleteNoteAct->setEnabled(!ro); - m_clearScoreAct->setEnabled(!ro); - m_editModeAct->setEnabled(!ro); - m_insertNoteAct->setEnabled(!ro); - } - setKeyReadOnly(ro); - if (!m_readOnly) { - setEditMode(true); +void TscoreObject::setReadOnly(bool ro) +{ + if (m_readOnly != ro) { + m_readOnly = ro; + emit readOnlyChanged(); + if (m_deleteNoteAct && !m_singleNote) { + m_deleteNoteAct->setEnabled(!ro); + m_clearScoreAct->setEnabled(!ro); + m_editModeAct->setEnabled(!ro); + m_insertNoteAct->setEnabled(!ro); + } + setKeyReadOnly(ro); + if (!m_readOnly) { + setEditMode(true); + } } - } } - -void TscoreObject::setEditMode(bool isEdit) { - if (isEdit != m_editMode) { - m_editMode = isEdit; - if (m_editModeAct) - m_editModeAct->setChecked(m_editMode); - emit editModeChanged(); - if (!m_editMode && m_activeNote) { - m_activeNote = nullptr; - emit activeNoteChanged(); - setActiveNotePos(0.0); - setHoveredNote(nullptr); +void TscoreObject::setEditMode(bool isEdit) +{ + if (isEdit != m_editMode) { + m_editMode = isEdit; + if (m_editModeAct) + m_editModeAct->setChecked(m_editMode); + emit editModeChanged(); + if (!m_editMode && m_activeNote) { + m_activeNote = nullptr; + emit activeNoteChanged(); + setActiveNotePos(0.0); + setHoveredNote(nullptr); + } } - } } - /** * MainScore.qml also handles single note mode change, * but this method is (AND HAS TO BE) invoked first */ -void TscoreObject::setSingleNote(bool singleN) { - if (singleN != m_singleNote) { - clearScore(); // In single note mode this call is ignored - if (singleN) { - addNote(Tnote()); // it is avoided in single note mode - addNote(Tnote()); - addNote(Tnote()); - setShowNoteNames(false); - m_singleNote = true; - setNote(0, Tnote()); // reset it (hide) because addNote was performed above in multi notes mode - setNote(1, Tnote()); - setNote(2, Tnote()); - note(0)->shiftHead(1.5); - note(1)->shiftHead(1.5); - note(2)->shiftHead(1.5); - note(1)->setEnabled(false); - note(2)->setEnabled(false); - m_selectedItem = note(0); - } else { - m_singleNote = false; - resetNoteItem(note(0)); - resetNoteItem(note(1)); - resetNoteItem(note(2)); - clearScore(); // call it again when transitioning from single note mode +void TscoreObject::setSingleNote(bool singleN) +{ + if (singleN != m_singleNote) { + clearScore(); // In single note mode this call is ignored + if (singleN) { + addNote(Tnote()); // it is avoided in single note mode + addNote(Tnote()); + addNote(Tnote()); + setShowNoteNames(false); + m_singleNote = true; + setNote(0, Tnote()); // reset it (hide) because addNote was performed above in multi notes mode + setNote(1, Tnote()); + setNote(2, Tnote()); + note(0)->shiftHead(1.5); + note(1)->shiftHead(1.5); + note(2)->shiftHead(1.5); + note(1)->setEnabled(false); + note(2)->setEnabled(false); + m_selectedItem = note(0); + } else { + m_singleNote = false; + resetNoteItem(note(0)); + resetNoteItem(note(1)); + resetNoteItem(note(2)); + clearScore(); // call it again when transitioning from single note mode + } + emit singleNoteChanged(); } - emit singleNoteChanged(); - } } - -void TscoreObject::setScaleFactor(qreal factor) { - if (factor != m_scaleFactor) { - m_scaleFactor = factor; - emit scaleFactorChanged(); - } +void TscoreObject::setScaleFactor(qreal factor) +{ + if (factor != m_scaleFactor) { + m_scaleFactor = factor; + emit scaleFactorChanged(); + } } - -void TscoreObject::setEnableTechnical(bool enTech) { - if (enTech != m_enableTechnControl) { - m_enableTechnControl = enTech; - emit enableTechnicalChanged(); - } +void TscoreObject::setEnableTechnical(bool enTech) +{ + if (enTech != m_enableTechnControl) { + m_enableTechnControl = enTech; + emit enableTechnicalChanged(); + } } - -void TscoreObject::transpose(int semis, bool outScaleToRest, const Tnote& loNote, const Tnote& hiNote) { - if (semis == 0 || notesCount() == 0) - return; // nothing to transpose - - auto lo = loNote.isValid() ? loNote.chromatic() : lowestNote().chromatic(); - auto hi = hiNote.isValid() ? hiNote.chromatic() : highestNote().chromatic(); - int rtmGrToCheck = 0; - bool fixBeam = false; - for (int n = 0; n < notesCount(); ++n) { - auto noteSeg = m_segments[n]; - int transOff = 0; - Trhythm transRtm(noteSeg->note()->rtm); - auto transChrom = noteSeg->note()->chromatic() + semis; - if (outScaleToRest) { - if (transChrom > hi || transChrom < lo) { - transRtm.setRest(true); - transRtm.setTie(Trhythm::e_noTie); +void TscoreObject::transpose(int semis, bool outScaleToRest, const Tnote &loNote, const Tnote &hiNote) +{ + if (semis == 0 || notesCount() == 0) + return; // nothing to transpose + + auto lo = loNote.isValid() ? loNote.chromatic() : lowestNote().chromatic(); + auto hi = hiNote.isValid() ? hiNote.chromatic() : highestNote().chromatic(); + int rtmGrToCheck = 0; + bool fixBeam = false; + for (int n = 0; n < notesCount(); ++n) { + auto noteSeg = m_segments[n]; + int transOff = 0; + Trhythm transRtm(noteSeg->note()->rtm); + auto transChrom = noteSeg->note()->chromatic() + semis; + if (outScaleToRest) { + if (transChrom > hi || transChrom < lo) { + transRtm.setRest(true); + transRtm.setTie(Trhythm::e_noTie); + } + } else { + if (transChrom > hi) + transOff = -12; // when too high drop octave down + else if (transChrom < lo) + transOff = 12; // when too low raise octave up + } + Tnote transposed(*noteSeg->note(), transRtm); + if (transRtm.isRest()) + transposed.setNote(0); + else + transposed.transpose(semis + transOff); + if (m_keySignEnabled) { + auto inKeyNote = TkeySignature(static_cast<char>(m_keySignature)).inKey(transposed); + if (inKeyNote.isValid()) { + transposed.setNote(inKeyNote.note()); + transposed.setOctave(inKeyNote.octave()); + transposed.setAlter(inKeyNote.alter()); + } } - } else { - if (transChrom > hi) - transOff = -12; // when too high drop octave down - else if (transChrom < lo) - transOff = 12; // when too low raise octave up - } - Tnote transposed(*noteSeg->note(), transRtm); - if (transRtm.isRest()) - transposed.setNote(0); - else - transposed.transpose(semis + transOff); - if (m_keySignEnabled) { - auto inKeyNote = TkeySignature(static_cast<char>(m_keySignature)).inKey(transposed); - if (inKeyNote.isValid()) { - transposed.setNote(inKeyNote.note()); - transposed.setOctave(inKeyNote.octave()); - transposed.setAlter(inKeyNote.alter()); - } - } - noteSeg->setNote(transposed); + noteSeg->setNote(transposed); - if (noteSeg->beam() && !transRtm.isRest()) - fixBeam = true; + if (noteSeg->beam() && !transRtm.isRest()) + fixBeam = true; - if (fixBeam) { - int nextRtmGr = (n == notesCount() - 1 ? -1 : m_segments[n + 1]->rhythmGroup()); - bool lastInBar = (noteSeg == noteSeg->item()->measure()->last()); - if (nextRtmGr != rtmGrToCheck || lastInBar) { // summarize beaming at the end of group or measure - noteSeg->item()->measure()->resolveBeaming(rtmGrToCheck, rtmGrToCheck); - fixBeam = false; // reset beam fix for next group checking - rtmGrToCheck = nextRtmGr; - } + if (fixBeam) { + int nextRtmGr = (n == notesCount() - 1 ? -1 : m_segments[n + 1]->rhythmGroup()); + bool lastInBar = (noteSeg == noteSeg->item()->measure()->last()); + if (nextRtmGr != rtmGrToCheck || lastInBar) { // summarize beaming at the end of group or measure + noteSeg->item()->measure()->resolveBeaming(rtmGrToCheck, rtmGrToCheck); + fixBeam = false; // reset beam fix for next group checking + rtmGrToCheck = nextRtmGr; + } + } } - } - for (int m = 0; m < m_measures.count(); ++m) - m_measures[m]->refresh(); - adjustScoreWidth(); + for (int m = 0; m < m_measures.count(); ++m) + m_measures[m]->refresh(); + adjustScoreWidth(); } - -qreal TscoreObject::stavesHeight() { - if (m_staves.isEmpty()) - return 0.0; - auto last = lastStaff(); - return last->y() + last->height() * last->scale(); +qreal TscoreObject::stavesHeight() +{ + if (m_staves.isEmpty()) + return 0.0; + auto last = lastStaff(); + return last->y() + last->height() * last->scale(); } - -void TscoreObject::changeActiveNote(TnoteItem* aNote) { - if (aNote != m_activeNote) { - if (m_activeNote && m_activeNote->staff()) - m_activeNote->staff()->setZ(0); // reset staff z order (set it down) - auto prevActive = m_activeNote; - m_activeNote = aNote; - if (m_activeNote == nullptr) { - m_leaveTimer->start(600); - } else { - if (prevActive == nullptr) - m_enterTimer->start(300); - else { - enterTimeElapsed(); - emit activeYposChanged(); +void TscoreObject::changeActiveNote(TnoteItem *aNote) +{ + if (aNote != m_activeNote) { + if (m_activeNote && m_activeNote->staff()) + m_activeNote->staff()->setZ(0); // reset staff z order (set it down) + auto prevActive = m_activeNote; + m_activeNote = aNote; + if (m_activeNote == nullptr) { + m_leaveTimer->start(600); + } else { + if (prevActive == nullptr) + m_enterTimer->start(300); + else { + enterTimeElapsed(); + emit activeYposChanged(); + } + if (m_activeNote->staff()) // raise staff to overlap other staves and keep note cursor stick to this staff + m_activeNote->staff()->setZ(1); } - if (m_activeNote->staff()) // raise staff to overlap other staves and keep note cursor stick to this staff - m_activeNote->staff()->setZ(1); } - } } - -void TscoreObject::setActiveNotePos(qreal yPos) { - if (!m_enterTimer->isActive() && yPos != m_activeYpos) { - m_activeYpos = yPos; - emit activeYposChanged(); - } +void TscoreObject::setActiveNotePos(qreal yPos) +{ + if (!m_enterTimer->isActive() && yPos != m_activeYpos) { + m_activeYpos = yPos; + emit activeYposChanged(); + } } - /** * Returns X coordinate of the first note in active measure (if any). * or (if score is empty yet) - returns staff indent (position after meter) * So far it is used for positioning accidental pane on the active measure left. */ -qreal TscoreObject::xFirstInActivBar() { - if (m_activeBarNr < 0) - return (firstStaff()->notesIndent() - 2.0) * firstStaff()->scale(); - else - return (m_measures[m_activeBarNr]->first()->item()->x() - m_measures[m_activeBarNr]->first()->item()->alterWidth() - 1.0) * firstStaff()->scale(); +qreal TscoreObject::xFirstInActivBar() +{ + if (m_activeBarNr < 0) + return (firstStaff()->notesIndent() - 2.0) * firstStaff()->scale(); + else + return (m_measures[m_activeBarNr]->first()->item()->x() - m_measures[m_activeBarNr]->first()->item()->alterWidth() - 1.0) * firstStaff()->scale(); } - /** * Returns X coordinate of the last note in active measure (if any), * but if that note is too far on the right (near a staff end), coordinate of first note is returned, * subtracted with 11.2 units of score scale which is width of the rhythm pane - to keep it visible. * So far it is used for positioning rhythm pane on the active measure right */ -qreal TscoreObject::xLastInActivBar() { - if (m_activeBarNr > -1) { - qreal xx = m_measures[m_activeBarNr]->last()->item()->rightX(); - if (xx > m_width - 12.0) - return xFirstInActivBar() - 11.2 * firstStaff()->scale(); - else - return (xx + 7.0) * firstStaff()->scale(); - } - return (firstStaff()->notesIndent() + 7.0) * firstStaff()->scale(); +qreal TscoreObject::xLastInActivBar() +{ + if (m_activeBarNr > -1) { + qreal xx = m_measures[m_activeBarNr]->last()->item()->rightX(); + if (xx > m_width - 12.0) + return xFirstInActivBar() - 11.2 * firstStaff()->scale(); + else + return (xx + 7.0) * firstStaff()->scale(); + } + return (firstStaff()->notesIndent() + 7.0) * firstStaff()->scale(); } -Trhythm TscoreObject::workRhythm() const { - return *m_workRhythm; +Trhythm TscoreObject::workRhythm() const +{ + return *m_workRhythm; } - -void TscoreObject::setWorkRhythm(const Trhythm& r) { - if (r != *m_workRhythm) { - m_workRhythm->setRhythm(r); - emit workRhythmChanged(); - } +void TscoreObject::setWorkRhythm(const Trhythm &r) +{ + if (r != *m_workRhythm) { + m_workRhythm->setRhythm(r); + emit workRhythmChanged(); + } } - -QString TscoreObject::workRtmText() const { - return TnoteItem::getHeadText(workRhythm()); +QString TscoreObject::workRtmText() const +{ + return TnoteItem::getHeadText(workRhythm()); } - -int TscoreObject::workRtmValue() const { return static_cast<int>(m_workRhythm->rhythm()); } - -void TscoreObject::setWorkRtmValue(int rtmV) { - if (static_cast<Trhythm::Erhythm>(rtmV) != m_workRhythm->rhythm()) { - m_workRhythm->setRhythmValue(static_cast<Trhythm::Erhythm>(rtmV)); - emit workRhythmChanged(); - } +int TscoreObject::workRtmValue() const +{ + return static_cast<int>(m_workRhythm->rhythm()); } - -bool TscoreObject::workRtmRest() const { return m_workRhythm->isRest(); } - -void TscoreObject::setWorkRtmRest(bool hasRest) { - if (hasRest != m_workRhythm->isRest()) { - m_workRhythm->setRest(hasRest); - emit workRhythmChanged(); - } +void TscoreObject::setWorkRtmValue(int rtmV) +{ + if (static_cast<Trhythm::Erhythm>(rtmV) != m_workRhythm->rhythm()) { + m_workRhythm->setRhythmValue(static_cast<Trhythm::Erhythm>(rtmV)); + emit workRhythmChanged(); + } } - -bool TscoreObject::workRtmDot() const { return m_workRhythm->hasDot(); } - -void TscoreObject::setWorkRtmDot(bool hasDot) { - if (hasDot != m_workRhythm->hasDot()) { - m_workRhythm->setDot(hasDot); - emit workRhythmChanged(); - } +bool TscoreObject::workRtmRest() const +{ + return m_workRhythm->isRest(); } - - -QString TscoreObject::activeRtmText() { - if (m_activeNote) - return TnoteItem::getHeadText(Trhythm(m_activeNote == lastSegment()->item() ? m_workRhythm->rhythm() : m_activeNote->note()->rhythm(), m_workRhythm->isRest())); - return QString(); +void TscoreObject::setWorkRtmRest(bool hasRest) +{ + if (hasRest != m_workRhythm->isRest()) { + m_workRhythm->setRest(hasRest); + emit workRhythmChanged(); + } } - -Tnote TscoreObject::posToNote(qreal yPos) { - int globalNr = globalNoteNr(yPos); - return Tnote(m_workRhythm->isRest() || m_clefType == Tclef::NoClef ? 0 : static_cast<char>(56 + globalNr) % 7 + 1, - static_cast<char>(56 + globalNr) / 7 - 8, static_cast<char>(m_cursorAlter), workRhythm()); +bool TscoreObject::workRtmDot() const +{ + return m_workRhythm->hasDot(); } - -void TscoreObject::setAllowAdding(bool allow) { - if (m_singleNote) - allow = false; - if (allow != m_allowAdding) { - m_allowAdding = allow; - adjustScoreWidth(); - emit allowAddingChanged(); - } +void TscoreObject::setWorkRtmDot(bool hasDot) +{ + if (hasDot != m_workRhythm->hasDot()) { + m_workRhythm->setDot(hasDot); + emit workRhythmChanged(); + } } - -TnoteItem* TscoreObject::lastNote() { - return m_segments.isEmpty() ? nullptr : lastSegment()->item(); +QString TscoreObject::activeRtmText() +{ + if (m_activeNote) + return TnoteItem::getHeadText( + Trhythm(m_activeNote == lastSegment()->item() ? m_workRhythm->rhythm() : m_activeNote->note()->rhythm(), m_workRhythm->isRest())); + return QString(); } +Tnote TscoreObject::posToNote(qreal yPos) +{ + int globalNr = globalNoteNr(yPos); + return Tnote(m_workRhythm->isRest() || m_clefType == Tclef::NoClef ? 0 : static_cast<char>(56 + globalNr) % 7 + 1, + static_cast<char>(56 + globalNr) / 7 - 8, + static_cast<char>(m_cursorAlter), + workRhythm()); +} -qreal TscoreObject::midLine(TnoteItem* actNote) { - if (stavesCount() == 0) - return 0.0; - if (actNote && m_activeNote) - return activeNote()->y() + (upperLine() + 4.0) * lastStaff()->scale(); - else - return lastStaff()->y() + (upperLine() + 4.0) * lastStaff()->scale(); +void TscoreObject::setAllowAdding(bool allow) +{ + if (m_singleNote) + allow = false; + if (allow != m_allowAdding) { + m_allowAdding = allow; + adjustScoreWidth(); + emit allowAddingChanged(); + } } +TnoteItem *TscoreObject::lastNote() +{ + return m_segments.isEmpty() ? nullptr : lastSegment()->item(); +} -void TscoreObject::deleteNote(TnoteItem* n) { - if (n == lastNote()) { - deleteLastNote(); - return; - } - if (n) { - int rmId = n->index(); - if (n->note()->rtm.tie()) { - if (n->note()->rtm.tie() == Trhythm::e_tieStart) { - m_segments[rmId + 1]->disconnectTie(TnotePair::e_untieNext); - } else { // tie continue or end - if (n->note()->rtm.tie() == Trhythm::e_tieCont) - m_segments[rmId + 1]->disconnectTie(TnotePair::e_untieNext); - m_segments[rmId - 1]->disconnectTie(TnotePair::e_untiePrev); - } - } - auto segToRemove = m_segments.takeAt(rmId); - auto m = n->measure(); - int staffNr = m->staff()->number(); - segToRemove->flush(); - m_spareSegments << segToRemove; - m_notes.removeAt(rmId); - for (int s = rmId; s < m_segments.count(); ++s) - m_segments[s]->setIndex(s); - m->removeNote(n->wrapper()); - auto lastBar = lastMeasure(); - if (lastBar->isEmpty()) - removeLastMeasure(); - adjustScoreWidth(staffNr); - } -} - - - -void TscoreObject::deleteLastNote() { - if (notesCount()) { - if (lastNote()->note()->rtm.tie() && notesCount() > 1) - noteSegment(notesCount() - 2)->disconnectTie(TnotePair::e_untiePrev); - bool adjust = false; - auto lastBar = lastMeasure(); - int tempActiveBar = m_activeBarNr; - if (lastBar->noteCount() == 1 && measuresCount() > 1) - adjust = removeLastMeasure(); +qreal TscoreObject::midLine(TnoteItem *actNote) +{ + if (stavesCount() == 0) + return 0.0; + if (actNote && m_activeNote) + return activeNote()->y() + (upperLine() + 4.0) * lastStaff()->scale(); else - lastBar->removeLastNote(); - auto segToRemove = m_segments.takeLast(); - segToRemove->flush(); - m_spareSegments << segToRemove; - m_notes.removeLast(); - m_activeNote = nullptr; - if (notesCount() == 0) - m_activeBarNr = -1; - if (adjust) - adjustScoreWidth(); - - emit activeNoteChanged(); - emitLastNote(); - if (tempActiveBar != m_activeBarNr) - emitActiveBarChanged(); - setSelectedItem(nullptr); - } + return lastStaff()->y() + (upperLine() + 4.0) * lastStaff()->scale(); } - -void TscoreObject::insertNote(TnoteItem* afterItem) { - if (afterItem) { - afterItem->measure()->insertNote(afterItem); - adjustScoreWidth(afterItem->staff()->number()); - } +void TscoreObject::deleteNote(TnoteItem *n) +{ + if (n == lastNote()) { + deleteLastNote(); + return; + } + if (n) { + int rmId = n->index(); + if (n->note()->rtm.tie()) { + if (n->note()->rtm.tie() == Trhythm::e_tieStart) { + m_segments[rmId + 1]->disconnectTie(TnotePair::e_untieNext); + } else { // tie continue or end + if (n->note()->rtm.tie() == Trhythm::e_tieCont) + m_segments[rmId + 1]->disconnectTie(TnotePair::e_untieNext); + m_segments[rmId - 1]->disconnectTie(TnotePair::e_untiePrev); + } + } + auto segToRemove = m_segments.takeAt(rmId); + auto m = n->measure(); + int staffNr = m->staff()->number(); + segToRemove->flush(); + m_spareSegments << segToRemove; + m_notes.removeAt(rmId); + for (int s = rmId; s < m_segments.count(); ++s) + m_segments[s]->setIndex(s); + m->removeNote(n->wrapper()); + auto lastBar = lastMeasure(); + if (lastBar->isEmpty()) + removeLastMeasure(); + adjustScoreWidth(staffNr); + } } - -void TscoreObject::clearScore() { - if (notesCount() == 0) { - setKeySignature(0); - return; +void TscoreObject::deleteLastNote() +{ + if (notesCount()) { + if (lastNote()->note()->rtm.tie() && notesCount() > 1) + noteSegment(notesCount() - 2)->disconnectTie(TnotePair::e_untiePrev); + bool adjust = false; + auto lastBar = lastMeasure(); + int tempActiveBar = m_activeBarNr; + if (lastBar->noteCount() == 1 && measuresCount() > 1) + adjust = removeLastMeasure(); + else + lastBar->removeLastNote(); + auto segToRemove = m_segments.takeLast(); + segToRemove->flush(); + m_spareSegments << segToRemove; + m_notes.removeLast(); + m_activeNote = nullptr; + if (notesCount() == 0) + m_activeBarNr = -1; + if (adjust) + adjustScoreWidth(); + + emit activeNoteChanged(); + emitLastNote(); + if (tempActiveBar != m_activeBarNr) + emitActiveBarChanged(); + setSelectedItem(nullptr); } - - if (m_singleNote) { - setNote(note(0), Tnote()); - setNote(note(1), Tnote()); - setNote(note(2), Tnote()); - } else { - clearScorePrivate(); - m_notes.clear(); - m_activeBarNr = -1; - m_activeNote = nullptr; - adjustScoreWidth(); - emitLastNote(); - setSelectedItem(nullptr); - emit scoreWasCleared(); - } - setKeySignature(0); - emit scoreWasCleared(); } - -qreal TscoreObject::upperLine() { return m_staves.empty() ? 16.0 : firstStaff()->upperLine(); } - - -void TscoreObject::setWidth(qreal w) { - if (w != m_width) { - m_width = w; - if (m_widthTimer->isActive()) - m_widthTimer->stop(); - m_widthTimer->start(WIDTH_CHANGE_DELAY); - } +void TscoreObject::insertNote(TnoteItem *afterItem) +{ + if (afterItem) { + afterItem->measure()->insertNote(afterItem); + adjustScoreWidth(afterItem->staff()->number()); + } } +void TscoreObject::clearScore() +{ + if (notesCount() == 0) { + setKeySignature(0); + return; + } -Tnote TscoreObject::highestNote() { - switch (m_clefType) { - case Tclef::Treble_G: return Tnote(4, 4); - case Tclef::Treble_G_8down: return Tnote(4, 3); - case Tclef::Bass_F: return Tnote(6, 2); - case Tclef::Alto_C: return Tnote(5, 3); - case Tclef::Tenor_C: return Tnote(3, 3); - case Tclef::PianoStaffClefs: return Tnote(1, 4); - default: return Tnote(); // percussion clef (no clef) and unsupported dropped bass - } + if (m_singleNote) { + setNote(note(0), Tnote()); + setNote(note(1), Tnote()); + setNote(note(2), Tnote()); + } else { + clearScorePrivate(); + m_notes.clear(); + m_activeBarNr = -1; + m_activeNote = nullptr; + adjustScoreWidth(); + emitLastNote(); + setSelectedItem(nullptr); + emit scoreWasCleared(); + } + setKeySignature(0); + emit scoreWasCleared(); } - -Tnote TscoreObject::lowestNote() { - switch (m_clefType) { - case Tclef::Treble_G: return Tnote(7, -1); - case Tclef::Treble_G_8down: return Tnote(7, -2); - case Tclef::Bass_F: return Tnote(7, -2); - case Tclef::Alto_C: return Tnote(1, -1); - case Tclef::Tenor_C: return Tnote(6, -2); - case Tclef::PianoStaffClefs: return Tnote(1, -1); - default: return Tnote(); // percussion clef (no clef) and unsupported dropped bass - } +qreal TscoreObject::upperLine() +{ + return m_staves.empty() ? 16.0 : firstStaff()->upperLine(); } - -TnoteItem* TscoreObject::getNext(TnoteItem* someNote) { - if (someNote) { - if (someNote->index() < notesCount() - 1) - return noteSegment(someNote->index() + 1)->item(); - } - return nullptr; +void TscoreObject::setWidth(qreal w) +{ + if (w != m_width) { + m_width = w; + if (m_widthTimer->isActive()) + m_widthTimer->stop(); + m_widthTimer->start(WIDTH_CHANGE_DELAY); + } } - -TnoteItem * TscoreObject::getPrev(TnoteItem* someNote) { - if (someNote) { - if (someNote->index() > 0) - return noteSegment(someNote->index() - 1)->item(); - } - return nullptr; +Tnote TscoreObject::highestNote() +{ + switch (m_clefType) { + case Tclef::Treble_G: + return Tnote(4, 4); + case Tclef::Treble_G_8down: + return Tnote(4, 3); + case Tclef::Bass_F: + return Tnote(6, 2); + case Tclef::Alto_C: + return Tnote(5, 3); + case Tclef::Tenor_C: + return Tnote(3, 3); + case Tclef::PianoStaffClefs: + return Tnote(1, 4); + default: + return Tnote(); // percussion clef (no clef) and unsupported dropped bass + } } - -void TscoreObject::setSelectedItem(TnoteItem* item) { - if (item != m_selectedItem) { - m_selectedItem = item; - emit selectedItemChanged(); - emit selectedNoteChanged(); - } +Tnote TscoreObject::lowestNote() +{ + switch (m_clefType) { + case Tclef::Treble_G: + return Tnote(7, -1); + case Tclef::Treble_G_8down: + return Tnote(7, -2); + case Tclef::Bass_F: + return Tnote(7, -2); + case Tclef::Alto_C: + return Tnote(1, -1); + case Tclef::Tenor_C: + return Tnote(6, -2); + case Tclef::PianoStaffClefs: + return Tnote(1, -1); + default: + return Tnote(); // percussion clef (no clef) and unsupported dropped bass + } } - -void TscoreObject::selectNext(bool keep, bool skipTies) { - if (m_selectedItem) { - TnoteItem* next = nullptr; - if (skipTies && m_selectedItem->note()->rtm.tie() < Trhythm::e_tieEnd) { - auto tr = tieRange(m_selectedItem); - next = note(tr.y()); - } else - next = getNext(m_selectedItem); - if (next) - setSelectedItem(next); - else if (!keep) - setSelectedItem(nullptr); - } +TnoteItem *TscoreObject::getNext(TnoteItem *someNote) +{ + if (someNote) { + if (someNote->index() < notesCount() - 1) + return noteSegment(someNote->index() + 1)->item(); + } + return nullptr; } - -void TscoreObject::setBgColor(const QColor& bg) { - if (bg != m_bgColor) { - m_bgColor = bg; - emit bgColorChanged(); - } +TnoteItem *TscoreObject::getPrev(TnoteItem *someNote) +{ + if (someNote) { + if (someNote->index() > 0) + return noteSegment(someNote->index() - 1)->item(); + } + return nullptr; } +void TscoreObject::setSelectedItem(TnoteItem *item) +{ + if (item != m_selectedItem) { + m_selectedItem = item; + emit selectedItemChanged(); + emit selectedNoteChanged(); + } +} -void TscoreObject::enableActions() { - if (!m_deleteNoteAct) { - QString tipbg = QStringLiteral("tipbg"); - m_deleteNoteAct = new Taction(tr("Delete note"), QStringLiteral("delete"), this); - connect(m_deleteNoteAct, &Taction::triggered, this, [=]{ if (!m_readOnly && !m_singleNote && m_allowAdding) deleteNote(m_activeNote); }); - m_deleteNoteAct->createQmlShortcut(m_qmlComponent, "\"del\"; enabled: !score.singleNote && !score.readOnly"); - - m_insertNoteAct = new Taction(tr("Insert note"), QStringLiteral("fingerpoint"), this); - connect(m_insertNoteAct, &Taction::triggered, this, [=]{ if (!m_readOnly && !m_singleNote && m_allowAdding) insertNote(m_activeNote); }); - m_insertNoteAct->createQmlShortcut(m_qmlComponent, "\"ins\"; enabled: !score.singleNote && !score.readOnly"); - - m_clearScoreAct = new Taction(tr("Delete all notes"), QStringLiteral("clear-score"), this); - connect(m_clearScoreAct, &Taction::triggered, this, [=]{ if (!m_readOnly) clearScore(); }); - m_clearScoreAct->createQmlShortcut(m_qmlComponent, "\"Shift+del\"; enabled: !score.singleNote && !score.readOnly"); - - m_editModeAct = new Taction(tr("Edit score"), QString(), this); - m_editModeAct->setCheckable(true); - connect(m_editModeAct, &Taction::triggered, this, [=]{ if (!m_readOnly && !m_singleNote) setEditMode(!editMode()); }); - m_editModeAct->createQmlShortcut(m_qmlComponent, "\"E\"; enabled: !score.singleNote && !score.readOnly"); - - m_wholeNoteAct = new Taction(tr("whole note"), tipbg, this); - connect(m_wholeNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_wholeNoteAct->createQmlShortcut(m_qmlComponent, "\"1\""); - - m_halfNoteAct = new Taction(tr("half note"), tipbg, this); - connect(m_halfNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_halfNoteAct->createQmlShortcut(m_qmlComponent, "\"2\""); - - m_quarterNoteAct = new Taction(tr("quarter note"), tipbg, this); - connect(m_quarterNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_quarterNoteAct->createQmlShortcut(m_qmlComponent, "\"4\""); - - m_eighthNoteAct= new Taction(tr("eighth note"), tipbg, this); - connect(m_eighthNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_eighthNoteAct->createQmlShortcut(m_qmlComponent, "\"8\""); - - m_sixteenthNoteAct = new Taction(tr("sixteenth note"), tipbg, this); - connect(m_sixteenthNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_sixteenthNoteAct->createQmlShortcut(m_qmlComponent, "\"6\""); - - m_restNoteAct = new Taction(tr("rest"), tipbg, this); - connect(m_restNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_restNoteAct->createQmlShortcut(m_qmlComponent, "\"R\""); - - m_dotNoteAct = new Taction(tr("dot"), tipbg, this); - connect(m_dotNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_dotNoteAct->createQmlShortcut(m_qmlComponent, "\".\""); - - m_riseAct = new Taction(tr("rise", "as such as sharps rise note"), tipbg, this); - connect(m_riseAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_riseAct->createQmlShortcut(m_qmlComponent, "\"#\""); - - m_lowerAct = new Taction(tr("lower", "as such as flats lower note"), tipbg, this); - connect(m_lowerAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); - m_lowerAct->createQmlShortcut(m_qmlComponent, "\"@\""); +void TscoreObject::selectNext(bool keep, bool skipTies) +{ + if (m_selectedItem) { + TnoteItem *next = nullptr; + if (skipTies && m_selectedItem->note()->rtm.tie() < Trhythm::e_tieEnd) { + auto tr = tieRange(m_selectedItem); + next = note(tr.y()); + } else + next = getNext(m_selectedItem); + if (next) + setSelectedItem(next); + else if (!keep) + setSelectedItem(nullptr); + } +} - m_tieAct = new Taction(QGuiApplication::translate("ScoreToolbox", "tie", - "To translate it properly, check please meaning of 'tie' in musical context."), - tipbg, this); - connect(m_tieAct, &Taction::triggered, this, &TscoreObject::checkTieOfSelected); - m_tieAct->createQmlShortcut(m_qmlComponent, "\"l\""); - } +void TscoreObject::setBgColor(const QColor &bg) +{ + if (bg != m_bgColor) { + m_bgColor = bg; + emit bgColorChanged(); + } } +void TscoreObject::enableActions() +{ + if (!m_deleteNoteAct) { + QString tipbg = QStringLiteral("tipbg"); + m_deleteNoteAct = new Taction(tr("Delete note"), QStringLiteral("delete"), this); + connect(m_deleteNoteAct, &Taction::triggered, this, [=] { + if (!m_readOnly && !m_singleNote && m_allowAdding) + deleteNote(m_activeNote); + }); + m_deleteNoteAct->createQmlShortcut(m_qmlComponent, "\"del\"; enabled: !score.singleNote && !score.readOnly"); + + m_insertNoteAct = new Taction(tr("Insert note"), QStringLiteral("fingerpoint"), this); + connect(m_insertNoteAct, &Taction::triggered, this, [=] { + if (!m_readOnly && !m_singleNote && m_allowAdding) + insertNote(m_activeNote); + }); + m_insertNoteAct->createQmlShortcut(m_qmlComponent, "\"ins\"; enabled: !score.singleNote && !score.readOnly"); + + m_clearScoreAct = new Taction(tr("Delete all notes"), QStringLiteral("clear-score"), this); + connect(m_clearScoreAct, &Taction::triggered, this, [=] { + if (!m_readOnly) + clearScore(); + }); + m_clearScoreAct->createQmlShortcut(m_qmlComponent, "\"Shift+del\"; enabled: !score.singleNote && !score.readOnly"); + + m_editModeAct = new Taction(tr("Edit score"), QString(), this); + m_editModeAct->setCheckable(true); + connect(m_editModeAct, &Taction::triggered, this, [=] { + if (!m_readOnly && !m_singleNote) + setEditMode(!editMode()); + }); + m_editModeAct->createQmlShortcut(m_qmlComponent, "\"E\"; enabled: !score.singleNote && !score.readOnly"); + + m_wholeNoteAct = new Taction(tr("whole note"), tipbg, this); + connect(m_wholeNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_wholeNoteAct->createQmlShortcut(m_qmlComponent, "\"1\""); + + m_halfNoteAct = new Taction(tr("half note"), tipbg, this); + connect(m_halfNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_halfNoteAct->createQmlShortcut(m_qmlComponent, "\"2\""); + + m_quarterNoteAct = new Taction(tr("quarter note"), tipbg, this); + connect(m_quarterNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_quarterNoteAct->createQmlShortcut(m_qmlComponent, "\"4\""); + + m_eighthNoteAct = new Taction(tr("eighth note"), tipbg, this); + connect(m_eighthNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_eighthNoteAct->createQmlShortcut(m_qmlComponent, "\"8\""); + + m_sixteenthNoteAct = new Taction(tr("sixteenth note"), tipbg, this); + connect(m_sixteenthNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_sixteenthNoteAct->createQmlShortcut(m_qmlComponent, "\"6\""); + + m_restNoteAct = new Taction(tr("rest"), tipbg, this); + connect(m_restNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_restNoteAct->createQmlShortcut(m_qmlComponent, "\"R\""); + + m_dotNoteAct = new Taction(tr("dot"), tipbg, this); + connect(m_dotNoteAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_dotNoteAct->createQmlShortcut(m_qmlComponent, "\".\""); + + m_riseAct = new Taction(tr("rise", "as such as sharps rise note"), tipbg, this); + connect(m_riseAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_riseAct->createQmlShortcut(m_qmlComponent, "\"#\""); + + m_lowerAct = new Taction(tr("lower", "as such as flats lower note"), tipbg, this); + connect(m_lowerAct, &Taction::triggered, this, &TscoreObject::handleNoteAction); + m_lowerAct->createQmlShortcut(m_qmlComponent, "\"@\""); + + m_tieAct = new Taction(QGuiApplication::translate("ScoreToolbox", "tie", "To translate it properly, check please meaning of 'tie' in musical context."), + tipbg, + this); + connect(m_tieAct, &Taction::triggered, this, &TscoreObject::checkTieOfSelected); + m_tieAct->createQmlShortcut(m_qmlComponent, "\"l\""); + } +} /** * TODO: if there will be TnoteObject implemented with all note/rhythm props * It would replace @p m_cursorAlter && @p m_workRhythm */ -void TscoreObject::handleNoteAction() { - if (!m_readOnly) { - if (!m_singleNote && m_meter->meter() != Tmeter::NoMeter) { // For full score (melodies) only - bool wasWorkRhythmChanged = false; - if (sender() == m_dotNoteAct && m_workRhythm->rhythm() != Trhythm::Sixteenth) { - m_workRhythm->setDot(!m_workRhythm->hasDot()); - wasWorkRhythmChanged = true; - } else if (sender() == m_restNoteAct) { - m_workRhythm->setRest(!m_workRhythm->isRest()); - if (m_workRhythm->rhythm() == Trhythm::Sixteenth) - m_workRhythm->setDot(false); - wasWorkRhythmChanged = true; - } else if (sender() == m_sixteenthNoteAct) { - if (m_workRhythm->rhythm() != Trhythm::Sixteenth) { - m_workRhythm->setRhythmValue(Trhythm::Sixteenth); - m_workRhythm->setDot(false); // 16th with dot not supported (yet) - wasWorkRhythmChanged = true; - } - } else if (sender() == m_eighthNoteAct) { - if (m_workRhythm->rhythm() != Trhythm::Eighth) { - m_workRhythm->setRhythmValue(Trhythm::Eighth); - wasWorkRhythmChanged = true; - } - } else if (sender() == m_quarterNoteAct) { - if (m_workRhythm->rhythm() != Trhythm::Quarter) { - m_workRhythm->setRhythmValue(Trhythm::Quarter); - wasWorkRhythmChanged = true; - } - } else if (sender() == m_halfNoteAct) { - if (m_workRhythm->rhythm() != Trhythm::Half) { - m_workRhythm->setRhythmValue(Trhythm::Half); - wasWorkRhythmChanged = true; - } - } else if (sender() == m_wholeNoteAct) { - if (m_workRhythm->rhythm() != Trhythm::Whole) { - m_workRhythm->setRhythmValue(Trhythm::Whole); - wasWorkRhythmChanged = true; - } - } - if (wasWorkRhythmChanged) { - emit workRhythmChanged(); - return; - } - } - if (sender() == m_riseAct) { - if (m_cursorAlter < 1) // flats or none - setCursorAlter(1); // set sharp - else if (m_cursorAlter == 1 && m_enableDoubleAccids) // single sharp - setCursorAlter(2); // set double sharp - else - setCursorAlter(0); // or none - - } else if (sender() == m_lowerAct) { - if (m_cursorAlter > -1) // sharps of none - setCursorAlter(-1); // set flat - else if (m_cursorAlter == -1 && m_enableDoubleAccids) // single flat - setCursorAlter(-2); // set double flat - else - setCursorAlter(0); // or none +void TscoreObject::handleNoteAction() +{ + if (!m_readOnly) { + if (!m_singleNote && m_meter->meter() != Tmeter::NoMeter) { // For full score (melodies) only + bool wasWorkRhythmChanged = false; + if (sender() == m_dotNoteAct && m_workRhythm->rhythm() != Trhythm::Sixteenth) { + m_workRhythm->setDot(!m_workRhythm->hasDot()); + wasWorkRhythmChanged = true; + } else if (sender() == m_restNoteAct) { + m_workRhythm->setRest(!m_workRhythm->isRest()); + if (m_workRhythm->rhythm() == Trhythm::Sixteenth) + m_workRhythm->setDot(false); + wasWorkRhythmChanged = true; + } else if (sender() == m_sixteenthNoteAct) { + if (m_workRhythm->rhythm() != Trhythm::Sixteenth) { + m_workRhythm->setRhythmValue(Trhythm::Sixteenth); + m_workRhythm->setDot(false); // 16th with dot not supported (yet) + wasWorkRhythmChanged = true; + } + } else if (sender() == m_eighthNoteAct) { + if (m_workRhythm->rhythm() != Trhythm::Eighth) { + m_workRhythm->setRhythmValue(Trhythm::Eighth); + wasWorkRhythmChanged = true; + } + } else if (sender() == m_quarterNoteAct) { + if (m_workRhythm->rhythm() != Trhythm::Quarter) { + m_workRhythm->setRhythmValue(Trhythm::Quarter); + wasWorkRhythmChanged = true; + } + } else if (sender() == m_halfNoteAct) { + if (m_workRhythm->rhythm() != Trhythm::Half) { + m_workRhythm->setRhythmValue(Trhythm::Half); + wasWorkRhythmChanged = true; + } + } else if (sender() == m_wholeNoteAct) { + if (m_workRhythm->rhythm() != Trhythm::Whole) { + m_workRhythm->setRhythmValue(Trhythm::Whole); + wasWorkRhythmChanged = true; + } + } + if (wasWorkRhythmChanged) { + emit workRhythmChanged(); + return; + } + } + if (sender() == m_riseAct) { + if (m_cursorAlter < 1) // flats or none + setCursorAlter(1); // set sharp + else if (m_cursorAlter == 1 && m_enableDoubleAccids) // single sharp + setCursorAlter(2); // set double sharp + else + setCursorAlter(0); // or none + + } else if (sender() == m_lowerAct) { + if (m_cursorAlter > -1) // sharps of none + setCursorAlter(-1); // set flat + else if (m_cursorAlter == -1 && m_enableDoubleAccids) // single flat + setCursorAlter(-2); // set double flat + else + setCursorAlter(0); // or none + } } - } } - -void TscoreObject::checkTieOfSelected() { - if (m_selectedItem && m_selectedItem->index() > 0) { - auto prevNote = m_segments[m_selectedItem->index() - 1]; - auto n = *m_selectedItem->note(); - if (m_selectedItem->note()->rtm.tie() > Trhythm::e_tieStart) { // disconnect - prevNote->disconnectTie(TnotePair::e_untiePrev); - n.rtm.setTie(n.rtm.tie() == Trhythm::e_tieEnd ? Trhythm::e_noTie : Trhythm::e_tieStart); - m_selectedItem->wrapper()->setNote(n); - emit m_selectedItem->hasTieChanged(); - if (m_selectedItem->staff()->firstNote()->item() == m_selectedItem) - m_selectedItem->staff()->deleteExtraTie(); - } else { - if (!m_selectedItem->note()->isRest() && m_selectedItem->note()->chromatic() == prevNote->note()->chromatic()) { - n.rtm.setTie(n.rtm.tie() == Trhythm::e_noTie ? Trhythm::e_tieEnd : Trhythm::e_tieCont); - m_selectedItem->wrapper()->setNote(n); - auto pn = *prevNote->note(); - pn.rtm.setTie(pn.rtm.tie() == Trhythm::e_noTie ? Trhythm::e_tieStart : Trhythm::e_tieCont); - prevNote->setNote(pn); - emit m_selectedItem->hasTieChanged(); - if (m_selectedItem->staff()->firstNote()->item() == m_selectedItem) - m_selectedItem->staff()->createExtraTie(m_selectedItem); +void TscoreObject::checkTieOfSelected() +{ + if (m_selectedItem && m_selectedItem->index() > 0) { + auto prevNote = m_segments[m_selectedItem->index() - 1]; + auto n = *m_selectedItem->note(); + if (m_selectedItem->note()->rtm.tie() > Trhythm::e_tieStart) { // disconnect + prevNote->disconnectTie(TnotePair::e_untiePrev); + n.rtm.setTie(n.rtm.tie() == Trhythm::e_tieEnd ? Trhythm::e_noTie : Trhythm::e_tieStart); + m_selectedItem->wrapper()->setNote(n); + emit m_selectedItem->hasTieChanged(); + if (m_selectedItem->staff()->firstNote()->item() == m_selectedItem) + m_selectedItem->staff()->deleteExtraTie(); + } else { + if (!m_selectedItem->note()->isRest() && m_selectedItem->note()->chromatic() == prevNote->note()->chromatic()) { + n.rtm.setTie(n.rtm.tie() == Trhythm::e_noTie ? Trhythm::e_tieEnd : Trhythm::e_tieCont); + m_selectedItem->wrapper()->setNote(n); + auto pn = *prevNote->note(); + pn.rtm.setTie(pn.rtm.tie() == Trhythm::e_noTie ? Trhythm::e_tieStart : Trhythm::e_tieCont); + prevNote->setNote(pn); + emit m_selectedItem->hasTieChanged(); + if (m_selectedItem->staff()->firstNote()->item() == m_selectedItem) + m_selectedItem->staff()->createExtraTie(m_selectedItem); + } + } + auto notesForAlterCheck = tieRange(prevNote->item()); + bool fitStaff = false; + auto measureToRefresh = m_segments[notesForAlterCheck.x()]->item()->measure(); + notesForAlterCheck.setX(m_segments[notesForAlterCheck.x()]->item()->measure()->firstNoteId()); + notesForAlterCheck.setY(m_segments[notesForAlterCheck.y()]->item()->measure()->lastNoteId()); + for (int i = notesForAlterCheck.x(); i <= notesForAlterCheck.y(); ++i) { + if (m_segments[i]->note()->note() == n.note()) { + fitStaff = true; + m_segments[i]->item()->updateAlter(); + } + if (m_segments[i]->item()->measure() != measureToRefresh) { + measureToRefresh->refresh(); + measureToRefresh = m_segments[i]->item()->measure(); + } } - } - auto notesForAlterCheck = tieRange(prevNote->item()); - bool fitStaff = false; - auto measureToRefresh = m_segments[notesForAlterCheck.x()]->item()->measure(); - notesForAlterCheck.setX(m_segments[notesForAlterCheck.x()]->item()->measure()->firstNoteId()); - notesForAlterCheck.setY(m_segments[notesForAlterCheck.y()]->item()->measure()->lastNoteId()); - for (int i = notesForAlterCheck.x(); i <= notesForAlterCheck.y(); ++i) { - if (m_segments[i]->note()->note() == n.note()) { - fitStaff = true; - m_segments[i]->item()->updateAlter(); - } - if (m_segments[i]->item()->measure() != measureToRefresh) { measureToRefresh->refresh(); - measureToRefresh = m_segments[i]->item()->measure(); - } - } - measureToRefresh->refresh(); - if (fitStaff) { - m_segments[notesForAlterCheck.x()]->item()->staff()->fit(); - if (m_segments[notesForAlterCheck.y()]->item()->staff() != m_segments[notesForAlterCheck.x()]->item()->staff()) - m_segments[notesForAlterCheck.y()]->item()->staff()->fit(); - } - } -} - -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -void TscoreObject::addStaff(TstaffItem* st) { - m_qmlEngine = QQmlEngine::contextForObject(st)->engine(); - m_qmlComponent = new QQmlComponent(m_qmlEngine, this); - st->setNumber(stavesCount()); - m_staves.append(st); - if (m_staves.count() == 1) { // initialize first measure of first staff - st->appendMeasure(m_measures.first()); - connect(st, &TstaffItem::upperLineChanged, this, &TscoreObject::upperLineChanged); - } else { // redirect destroyed signal to QML score - connect(st, &TstaffItem::destroyed, this, [=]{ emit staffDestroying(st->number()); }); - } - - connect(st, &TstaffItem::hiNotePosChanged, this, [=](int staffNr, qreal offset){ - for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset - m_staves[i]->setY(m_staves[i]->y() + offset); - emit stavesHeightChanged(); - }); - connect(st, &TstaffItem::loNotePosChanged, this, [=](int staffNr, qreal offset){ - if (staffNr == 0) // never change Y position of first staff - it is always 0 - staffNr = 1; - if (m_staves.size() > 1 && staffNr < m_staves.size() - 1) { // ignore change of the last staff - for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset - m_staves[i]->setY(m_staves[i]->y() + offset); + if (fitStaff) { + m_segments[notesForAlterCheck.x()]->item()->staff()->fit(); + if (m_segments[notesForAlterCheck.y()]->item()->staff() != m_segments[notesForAlterCheck.x()]->item()->staff()) + m_segments[notesForAlterCheck.y()]->item()->staff()->fit(); + } } - emit stavesHeightChanged(); - }); +} + +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# + +void TscoreObject::addStaff(TstaffItem *st) +{ + m_qmlEngine = QQmlEngine::contextForObject(st)->engine(); + m_qmlComponent = new QQmlComponent(m_qmlEngine, this); + st->setNumber(stavesCount()); + m_staves.append(st); + if (m_staves.count() == 1) { // initialize first measure of first staff + st->appendMeasure(m_measures.first()); + connect(st, &TstaffItem::upperLineChanged, this, &TscoreObject::upperLineChanged); + } else { // redirect destroyed signal to QML score + connect(st, &TstaffItem::destroyed, this, [=] { + emit staffDestroying(st->number()); + }); + } + + connect(st, &TstaffItem::hiNotePosChanged, this, [=](int staffNr, qreal offset) { + for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset + m_staves[i]->setY(m_staves[i]->y() + offset); + emit stavesHeightChanged(); + }); + connect(st, &TstaffItem::loNotePosChanged, this, [=](int staffNr, qreal offset) { + if (staffNr == 0) // never change Y position of first staff - it is always 0 + staffNr = 1; + if (m_staves.size() > 1 && staffNr < m_staves.size() - 1) { // ignore change of the last staff + for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset + m_staves[i]->setY(m_staves[i]->y() + offset); + } + emit stavesHeightChanged(); + }); } /** * More-less it means that staff @p sourceStaff has no space for @p count number of measures * starting from @p measureNr, but those measures belong to it still */ -void TscoreObject::startStaffFromMeasure(TstaffItem* sourceStaff, int measureNr, int count) { - TstaffItem* targetStaff = nullptr; - if (sourceStaff == lastStaff()) { // create new staff to shift measure(s) there - emit staffCreate(); - targetStaff = lastStaff(); - } else { - targetStaff = m_staves[sourceStaff->number() + 1]; - targetStaff->deleteExtraTie(); - } - - for (int m = measureNr; m < measureNr + count; ++m) - m_measures[m]->setStaff(targetStaff); - targetStaff->setLastMeasureId(qMax(measureNr + count - 1, targetStaff->lastMeasureId())); - targetStaff->setFirstMeasureId(measureNr); -} - - -void TscoreObject::deleteStaff(TstaffItem* st) { - if (st->measuresCount() < 1) { - bool fixStaffNumbers = st != lastStaff(); - m_staves.removeAt(st->number()); - st->deleteLater(); - if (fixStaffNumbers) { - for (int s = 0; s < stavesCount(); ++s) - m_staves[s]->setNumber(s); - } - } -} - - -void TscoreObject::adjustScoreWidth(int firstStaff) { - m_adjustInProgress = true; - int refreshStaffNr = firstStaff; - while (refreshStaffNr < stavesCount()) { - m_staves[refreshStaffNr]->refresh(); - refreshStaffNr++; - } - m_adjustInProgress = false; - updateStavesPos(); -} - - -void TscoreObject::updateStavesPos() { - if (m_adjustInProgress) - return; - TstaffItem* prev = nullptr; - for (QList<TstaffItem*>::iterator s = m_staves.begin(); s != m_staves.end(); ++s) { - auto curr = *s; - if (curr->number() != 0 && curr->number() < stavesCount()) - curr->setY(prev->y() + (prev->loNotePos() - curr->hiNotePos() + 4.0) * prev->scale()); // TODO scordature! - prev = curr; - } - emit stavesHeightChanged(); -} - - -void TscoreObject::onIndentChanged() { - if (m_keyChanged) { - m_keyChanged = false; - adjustScoreWidth(); - } -} +void TscoreObject::startStaffFromMeasure(TstaffItem *sourceStaff, int measureNr, int count) +{ + TstaffItem *targetStaff = nullptr; + if (sourceStaff == lastStaff()) { // create new staff to shift measure(s) there + emit staffCreate(); + targetStaff = lastStaff(); + } else { + targetStaff = m_staves[sourceStaff->number() + 1]; + targetStaff->deleteExtraTie(); + } + for (int m = measureNr; m < measureNr + count; ++m) + m_measures[m]->setStaff(targetStaff); + targetStaff->setLastMeasureId(qMax(measureNr + count - 1, targetStaff->lastMeasureId())); + targetStaff->setFirstMeasureId(measureNr); +} -/** FIXME: if tie starts/stops incorrectly (no start or end) it may return note number out of range */ -QPoint TscoreObject::tieRange(TnoteItem* n) { - QPoint tr; - if (n->note()->rtm.tie()) { - tr.setX(n->index()); - while (tr.x() > -1) { - if (m_notes[tr.x()].rtm.tie() == Trhythm::e_tieStart) - break; - --tr.rx(); - } - tr.setY(n->index()); - while (tr.y() < notesCount()) { - if (m_notes[tr.y()].rtm.tie() == Trhythm::e_tieEnd) - break; - ++tr.ry(); +void TscoreObject::deleteStaff(TstaffItem *st) +{ + if (st->measuresCount() < 1) { + bool fixStaffNumbers = st != lastStaff(); + m_staves.removeAt(st->number()); + st->deleteLater(); + if (fixStaffNumbers) { + for (int s = 0; s < stavesCount(); ++s) + m_staves[s]->setNumber(s); + } } - } - return tr; } - -void TscoreObject::setTouched(bool t) { - if (t != m_touched) { - m_touched = t; - emit touchedChanged(); - } +void TscoreObject::adjustScoreWidth(int firstStaff) +{ + m_adjustInProgress = true; + int refreshStaffNr = firstStaff; + while (refreshStaffNr < stavesCount()) { + m_staves[refreshStaffNr]->refresh(); + refreshStaffNr++; + } + m_adjustInProgress = false; + updateStavesPos(); } - -TmeasureObject* TscoreObject::addMeasure() { - auto lastM = m_measures.last(); - if (lastM->free()) - qDebug() << "[TscoreObject] FIXME!!! Last measure is not full but the new one is going to be added"; - lastM = getMeasure(m_measures.count()); - m_measures << lastM; - lastStaff()->appendMeasure(lastM); - return lastM; +void TscoreObject::updateStavesPos() +{ + if (m_adjustInProgress) + return; + TstaffItem *prev = nullptr; + for (QList<TstaffItem *>::iterator s = m_staves.begin(); s != m_staves.end(); ++s) { + auto curr = *s; + if (curr->number() != 0 && curr->number() < stavesCount()) + curr->setY(prev->y() + (prev->loNotePos() - curr->hiNotePos() + 4.0) * prev->scale()); // TODO scordature! + prev = curr; + } + emit stavesHeightChanged(); } - -TnotePair* TscoreObject::insertSilently(int id, const Tnote& n, TmeasureObject* m) { - m_notes.insert(id, n); - auto np = getSegment(id, &m_notes[id]); - m_segments.insert(id, np); - for (int s = id + 1; s < m_segments.count(); ++s) - m_segments[s]->setIndex(s); - if (m) - m->insertSilently(id - m->firstNoteId(), np); - return np; +void TscoreObject::onIndentChanged() +{ + if (m_keyChanged) { + m_keyChanged = false; + adjustScoreWidth(); + } } - -//################################################################################################# -//################### PRIVATE ############################################ -//################################################################################################# - -void TscoreObject::appendToNoteList(QList<Tnote>& l) { - for (Tnote& n : l) { - m_notes << n; - m_segments << getSegment(m_segments.count(), &m_notes.last()); - } +/** FIXME: if tie starts/stops incorrectly (no start or end) it may return note number out of range */ +QPoint TscoreObject::tieRange(TnoteItem *n) +{ + QPoint tr; + if (n->note()->rtm.tie()) { + tr.setX(n->index()); + while (tr.x() > -1) { + if (m_notes[tr.x()].rtm.tie() == Trhythm::e_tieStart) + break; + --tr.rx(); + } + tr.setY(n->index()); + while (tr.y() < notesCount()) { + if (m_notes[tr.y()].rtm.tie() == Trhythm::e_tieEnd) + break; + ++tr.ry(); + } + } + return tr; } - -void TscoreObject::updateClefOffset() { - switch (m_clefType) { - case Tclef::Treble_G_8down: m_clefOffset.set(3, 1); break; - case Tclef::Bass_F: m_clefOffset.set(5, 0); break; - case Tclef::Alto_C: m_clefOffset.set(4, 1); break; - case Tclef::Tenor_C: m_clefOffset.set(2, 1); break; - default: m_clefOffset.set(3, 2); break; // Treble, piano staff and no clef (rhythm only) - } +void TscoreObject::setTouched(bool t) +{ + if (t != m_touched) { + m_touched = t; + emit touchedChanged(); + } } - -TnotePair* TscoreObject::getSegment(int noteNr, Tnote* n) { - if (m_spareSegments.isEmpty()) - return new TnotePair(noteNr, n); - else { - auto np = m_spareSegments.takeLast(); - np->setNote(n); - np->setIndex(noteNr); - return np; - } +TmeasureObject *TscoreObject::addMeasure() +{ + auto lastM = m_measures.last(); + if (lastM->free()) + qDebug() << "[TscoreObject] FIXME!!! Last measure is not full but the new one is going to be added"; + lastM = getMeasure(m_measures.count()); + m_measures << lastM; + lastStaff()->appendMeasure(lastM); + return lastM; } - -TmeasureObject* TscoreObject::getMeasure(int barNr) { - if (m_spareMeasures.isEmpty()) { - return new TmeasureObject(barNr, this); - } else { - auto m = m_spareMeasures.takeLast(); - m->setNumber(barNr); - m->meterChanged(); - return m; - } +TnotePair *TscoreObject::insertSilently(int id, const Tnote &n, TmeasureObject *m) +{ + m_notes.insert(id, n); + auto np = getSegment(id, &m_notes[id]); + m_segments.insert(id, np); + for (int s = id + 1; s < m_segments.count(); ++s) + m_segments[s]->setIndex(s); + if (m) + m->insertSilently(id - m->firstNoteId(), np); + return np; } +// ################################################################################################# +// ################### PRIVATE ############################################ +// ################################################################################################# -TbeamObject* TscoreObject::getBeam(TnotePair* np, TmeasureObject* m) { - if (m_spareBeams.isEmpty()) { - return new TbeamObject(np, m); - } else { - auto b = m_spareBeams.takeLast(); - b->setMeasure(m); - b->addNote(np); - return b; - } +void TscoreObject::appendToNoteList(QList<Tnote> &l) +{ + for (Tnote &n : l) { + m_notes << n; + m_segments << getSegment(m_segments.count(), &m_notes.last()); + } } - -void TscoreObject::storeBeam(TbeamObject* b) { - m_spareBeams << b; +void TscoreObject::updateClefOffset() +{ + switch (m_clefType) { + case Tclef::Treble_G_8down: + m_clefOffset.set(3, 1); + break; + case Tclef::Bass_F: + m_clefOffset.set(5, 0); + break; + case Tclef::Alto_C: + m_clefOffset.set(4, 1); + break; + case Tclef::Tenor_C: + m_clefOffset.set(2, 1); + break; + default: + m_clefOffset.set(3, 2); + break; // Treble, piano staff and no clef (rhythm only) + } } - -int TscoreObject::globalNoteNr(qreal yPos) { - if (isPianoStaff() && yPos > firstStaff()->upperLine() + 13.0) - yPos -= 10.0; - return m_clefOffset.octave * 7 - static_cast<int>(yPos - upperLine() - m_clefOffset.note); +TnotePair *TscoreObject::getSegment(int noteNr, Tnote *n) +{ + if (m_spareSegments.isEmpty()) + return new TnotePair(noteNr, n); + else { + auto np = m_spareSegments.takeLast(); + np->setNote(n); + np->setIndex(noteNr); + return np; + } } - -void TscoreObject::clearScorePrivate() { - if (measuresCount() && firstMeasure()->noteCount() > 0) { - setSelectedItem(nullptr); - m_activeBarNr = -1; - changeActiveNote(nullptr); - for (TnotePair* s : qAsConst(m_segments)) { - s->flush(); - m_spareSegments << s; - } - for (TmeasureObject* m : qAsConst(m_measures)) { - m->flush(); - m_spareMeasures << m; - } - m_measures.clear(); - m_segments.clear(); - while (m_staves.count() > 1) { - auto ls = m_staves.takeLast(); - ls->deleteLater(); +TmeasureObject *TscoreObject::getMeasure(int barNr) +{ + if (m_spareMeasures.isEmpty()) { + return new TmeasureObject(barNr, this); + } else { + auto m = m_spareMeasures.takeLast(); + m->setNumber(barNr); + m->meterChanged(); + return m; } - m_measures << getMeasure(0); - lastStaff()->appendMeasure(firstMeasure()); - firstStaff()->setFirstMeasureId(0); - firstStaff()->setLastMeasureId(0); - } } +TbeamObject *TscoreObject::getBeam(TnotePair *np, TmeasureObject *m) +{ + if (m_spareBeams.isEmpty()) { + return new TbeamObject(np, m); + } else { + auto b = m_spareBeams.takeLast(); + b->setMeasure(m); + b->addNote(np); + return b; + } +} -void TscoreObject::enterTimeElapsed() { - bool emitBarChange = false; - if (m_activeNote && m_activeNote->measure()->number() != m_activeBarNr) { - m_activeBarNr = m_activeNote->measure()->number(); - emitBarChange = true; - } - emit activeNoteChanged(); - if (emitBarChange) - emit activeBarChanged(); - if (m_clefType == Tclef::NoClef) - setActiveNotePos(upperLine() + 7.0); +void TscoreObject::storeBeam(TbeamObject *b) +{ + m_spareBeams << b; } +int TscoreObject::globalNoteNr(qreal yPos) +{ + if (isPianoStaff() && yPos > firstStaff()->upperLine() + 13.0) + yPos -= 10.0; + return m_clefOffset.octave * 7 - static_cast<int>(yPos - upperLine() - m_clefOffset.note); +} -void TscoreObject::leaveTimeElapsed() { - emit activeNoteChanged(); +void TscoreObject::clearScorePrivate() +{ + if (measuresCount() && firstMeasure()->noteCount() > 0) { + setSelectedItem(nullptr); + m_activeBarNr = -1; + changeActiveNote(nullptr); + for (TnotePair *s : qAsConst(m_segments)) { + s->flush(); + m_spareSegments << s; + } + for (TmeasureObject *m : qAsConst(m_measures)) { + m->flush(); + m_spareMeasures << m; + } + m_measures.clear(); + m_segments.clear(); + while (m_staves.count() > 1) { + auto ls = m_staves.takeLast(); + ls->deleteLater(); + } + m_measures << getMeasure(0); + lastStaff()->appendMeasure(firstMeasure()); + firstStaff()->setFirstMeasureId(0); + firstStaff()->setLastMeasureId(0); + } } +void TscoreObject::enterTimeElapsed() +{ + bool emitBarChange = false; + if (m_activeNote && m_activeNote->measure()->number() != m_activeBarNr) { + m_activeBarNr = m_activeNote->measure()->number(); + emitBarChange = true; + } + emit activeNoteChanged(); + if (emitBarChange) + emit activeBarChanged(); + if (m_clefType == Tclef::NoClef) + setActiveNotePos(upperLine() + 7.0); +} -void TscoreObject::fitToRange(Tnote& n) { - Tnote loNote = lowestNote(); - Tnote hiNote = highestNote(); - if (!n.isRest() - && ((n.octave() > hiNote.octave() || (n.octave() == hiNote.octave() && n.note() > hiNote.note())) - || (n.octave() < loNote.octave() || (n.octave() == loNote.octave() && n.note() < loNote.note())))) - { - n.setNote(0); n.setOctave(0); // invalidate note - n.setRest(true); - n.rtm.setTie(Trhythm::e_noTie); - n.rtm.setBeam(Trhythm::e_noBeam); - } +void TscoreObject::leaveTimeElapsed() +{ + emit activeNoteChanged(); } +void TscoreObject::fitToRange(Tnote &n) +{ + Tnote loNote = lowestNote(); + Tnote hiNote = highestNote(); + if (!n.isRest() + && ((n.octave() > hiNote.octave() || (n.octave() == hiNote.octave() && n.note() > hiNote.note())) + || (n.octave() < loNote.octave() || (n.octave() == loNote.octave() && n.note() < loNote.note())))) { + n.setNote(0); + n.setOctave(0); // invalidate note + n.setRest(true); + n.rtm.setTie(Trhythm::e_noTie); + n.rtm.setBeam(Trhythm::e_noBeam); + } +} /** * Set @p TnoteItem parameters to defaults , usually they are different in single note mode */ -void TscoreObject::resetNoteItem(TnoteItem* noteItem) { - noteItem->setVisible(true); - noteItem->setEnabled(true); - noteItem->setColor(qApp->palette().text().color()); - noteItem->setNoteNameVisible(m_showNoteNames && m_clefType != Tclef::NoClef); - noteItem->shiftHead(0.0); +void TscoreObject::resetNoteItem(TnoteItem *noteItem) +{ + noteItem->setVisible(true); + noteItem->setEnabled(true); + noteItem->setColor(qApp->palette().text().color()); + noteItem->setNoteNameVisible(m_showNoteNames && m_clefType != Tclef::NoClef); + noteItem->shiftHead(0.0); } - -bool TscoreObject::removeLastMeasure() { - bool adjust = false; - if (measuresCount() > 1) { - m_spareMeasures << m_measures.takeLast(); - auto lastSt = lastStaff(); - lastSt->setLastMeasureId(lastSt->lastMeasureId() - 1); - if (lastSt->measuresCount() == 0) { - deleteStaff(lastSt); - adjust = true; +bool TscoreObject::removeLastMeasure() +{ + bool adjust = false; + if (measuresCount() > 1) { + m_spareMeasures << m_measures.takeLast(); + auto lastSt = lastStaff(); + lastSt->setLastMeasureId(lastSt->lastMeasureId() - 1); + if (lastSt->measuresCount() == 0) { + deleteStaff(lastSt); + adjust = true; + } + m_activeBarNr--; + m_spareMeasures.last()->flush(); } - m_activeBarNr--; - m_spareMeasures.last()->flush(); - } - return adjust; + return adjust; } - // Tnote dummyNote; // CHECKTIME ( // for (int n = 0; n < 200; ++n) { diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index dfbc027c0f147889531d3e3eb097458eabbfbecd..89e8b09ad12b85eb99b573209a40492149e413d9 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -19,14 +19,12 @@ #ifndef TSCOREOBJECT_H #define TSCOREOBJECT_H - +#include "music/tclef.h" +#include "music/tnote.h" #include "nootkacoreglobal.h" #include <QtCore/qobject.h> #include <QtCore/qpoint.h> #include <QtGui/qcolor.h> -#include "music/tclef.h" -#include "music/tnote.h" - /** * @class TclefOffset describes offset of a note depends on clef @@ -34,15 +32,22 @@ class TclefOffset { public: - TclefOffset(qint8 noteOff = 0, qint8 octaveOff = 0) : note(noteOff), octave(octaveOff) {} - - qint8 note; - qint8 octave; - int total() { return octave * 7 + note; } - void set(qint8 nOff, qint8 oOff) { note = nOff; octave = oOff; } + TclefOffset(qint8 noteOff = 0, qint8 octaveOff = 0) + : note(noteOff) + , octave(octaveOff) + { + } + + qint8 note; + qint8 octave; + int total() { return octave * 7 + note; } + void set(qint8 nOff, qint8 oOff) + { + note = nOff; + octave = oOff; + } }; - class QQmlEngine; class QQmlComponent; class QTimer; @@ -56,688 +61,688 @@ class Taction; class TbeamObject; class QQuickItem; - /** * Implementation of score supplementing Score.qml */ -class NOOTKACORE_EXPORT TscoreObject : public QObject +class NOOTKACORE_EXPORT TscoreObject : public QObject { - - Q_OBJECT - - /* Musical parameters */ - Q_PROPERTY(int meter READ meterToInt WRITE setMeter NOTIFY meterChanged) - Q_PROPERTY(Tclef::EclefType clefType READ clefType WRITE setClefType NOTIFY clefTypeChanged) - Q_PROPERTY(int keySignature READ keySignature WRITE setKeySignature NOTIFY keySignatureChanged) - Q_PROPERTY(int notesCount READ notesCount) - Q_PROPERTY(int cursorAlter READ cursorAlter WRITE setCursorAlter NOTIFY cursorAlterChanged) - Q_PROPERTY(QString alterText READ alterText NOTIFY cursorAlterChanged) - /* Score switches */ - Q_PROPERTY(bool keySignatureEnabled READ keySignatureEnabled WRITE setKeySignatureEnabled NOTIFY keySignatureEnabledChanged) - Q_PROPERTY(bool keyReadOnly READ keyReadOnly WRITE setKeyReadOnly NOTIFY keyReadOnlyChanged) - Q_PROPERTY(bool enableDoubleAccidentals READ enableDoubleAccidentals WRITE setEnableDoubleAccids) - Q_PROPERTY(bool showNoteNames READ showNoteNames WRITE setShowNoteNames) - Q_PROPERTY(QColor nameColor READ nameColor WRITE setNameColor) - Q_PROPERTY(int nameStyle READ nameStyle WRITE setNameStyle) - Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly NOTIFY readOnlyChanged) - Q_PROPERTY(bool editMode READ editMode WRITE setEditMode NOTIFY editModeChanged) - Q_PROPERTY(bool singleNote READ singleNote WRITE setSingleNote NOTIFY singleNoteChanged) - Q_PROPERTY(bool enharmNotesEnabled READ enharmNotesEnabled WRITE setEnharmNotesEnabled) - Q_PROPERTY(qreal scaleFactor READ scaleFactor WRITE setScaleFactor NOTIFY scaleFactorChanged) - Q_PROPERTY(bool enableTechnical READ enableTechnical WRITE setEnableTechnical NOTIFY enableTechnicalChanged) - /* Helper variables */ - Q_PROPERTY(qreal stavesHeight READ stavesHeight NOTIFY stavesHeightChanged) - Q_PROPERTY(qreal width READ width WRITE setWidth) - Q_PROPERTY(TnoteItem* selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged) - Q_PROPERTY(Tnote selectedNote READ selectedNote NOTIFY selectedNoteChanged) - Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor NOTIFY bgColorChanged) - /* Note cursor */ - Q_PROPERTY(TnoteItem* activeNote READ activeNote NOTIFY activeNoteChanged) - Q_PROPERTY(TnoteItem* lastNote READ lastNote NOTIFY lastNoteChanged) - Q_PROPERTY(qreal activeYpos READ activeYpos NOTIFY activeYposChanged) - Q_PROPERTY(qreal upperLine READ upperLine NOTIFY upperLineChanged) - Q_PROPERTY(qreal xFirstInActivBar READ xFirstInActivBar NOTIFY activeBarChanged) - Q_PROPERTY(qreal xLastInActivBar READ xLastInActivBar NOTIFY activeBarChanged) - Q_PROPERTY(bool allowAdding READ allowAdding WRITE setAllowAdding NOTIFY allowAddingChanged) - Q_PROPERTY(qreal touched READ touched NOTIFY touchedChanged) - Q_PROPERTY(Trhythm workRhythm READ workRhythm WRITE setWorkRhythm NOTIFY workRhythmChanged) - Q_PROPERTY(QString workRtmText READ workRtmText NOTIFY workRhythmChanged) - Q_PROPERTY(int workRtmValue READ workRtmValue WRITE setWorkRtmValue NOTIFY workRhythmChanged) - Q_PROPERTY(bool workRtmRest READ workRtmRest WRITE setWorkRtmRest NOTIFY workRhythmChanged) - Q_PROPERTY(bool workRtmDot READ workRtmDot WRITE setWorkRtmDot NOTIFY workRhythmChanged) - - Q_PROPERTY(Taction* insertNoteAct READ insertNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* deleteNoteAct READ deleteNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* clearScoreAct READ clearScoreAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* editModeAct READ editModeAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* wholeNoteAct READ wholeNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* halfNoteAct READ halfNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* quarterNoteAct READ quarterNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* eighthNoteAct READ eighthNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* sixteenthNoteAct READ sixteenthNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* restNoteAct READ restNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* dotNoteAct READ dotNoteAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* riseAct READ riseAct NOTIFY scoreActionsChanged) - Q_PROPERTY(Taction* lowerAct READ lowerAct NOTIFY scoreActionsChanged) - - friend class TstaffItem; - friend class TmeasureObject; - friend class TnoteItem; - friend class TaddNoteItem; - friend class TbeamObject; + Q_OBJECT + + /* Musical parameters */ + Q_PROPERTY(int meter READ meterToInt WRITE setMeter NOTIFY meterChanged) + Q_PROPERTY(Tclef::EclefType clefType READ clefType WRITE setClefType NOTIFY clefTypeChanged) + Q_PROPERTY(int keySignature READ keySignature WRITE setKeySignature NOTIFY keySignatureChanged) + Q_PROPERTY(int notesCount READ notesCount) + Q_PROPERTY(int cursorAlter READ cursorAlter WRITE setCursorAlter NOTIFY cursorAlterChanged) + Q_PROPERTY(QString alterText READ alterText NOTIFY cursorAlterChanged) + /* Score switches */ + Q_PROPERTY(bool keySignatureEnabled READ keySignatureEnabled WRITE setKeySignatureEnabled NOTIFY keySignatureEnabledChanged) + Q_PROPERTY(bool keyReadOnly READ keyReadOnly WRITE setKeyReadOnly NOTIFY keyReadOnlyChanged) + Q_PROPERTY(bool enableDoubleAccidentals READ enableDoubleAccidentals WRITE setEnableDoubleAccids) + Q_PROPERTY(bool showNoteNames READ showNoteNames WRITE setShowNoteNames) + Q_PROPERTY(QColor nameColor READ nameColor WRITE setNameColor) + Q_PROPERTY(int nameStyle READ nameStyle WRITE setNameStyle) + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly NOTIFY readOnlyChanged) + Q_PROPERTY(bool editMode READ editMode WRITE setEditMode NOTIFY editModeChanged) + Q_PROPERTY(bool singleNote READ singleNote WRITE setSingleNote NOTIFY singleNoteChanged) + Q_PROPERTY(bool enharmNotesEnabled READ enharmNotesEnabled WRITE setEnharmNotesEnabled) + Q_PROPERTY(qreal scaleFactor READ scaleFactor WRITE setScaleFactor NOTIFY scaleFactorChanged) + Q_PROPERTY(bool enableTechnical READ enableTechnical WRITE setEnableTechnical NOTIFY enableTechnicalChanged) + /* Helper variables */ + Q_PROPERTY(qreal stavesHeight READ stavesHeight NOTIFY stavesHeightChanged) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(TnoteItem *selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged) + Q_PROPERTY(Tnote selectedNote READ selectedNote NOTIFY selectedNoteChanged) + Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor NOTIFY bgColorChanged) + /* Note cursor */ + Q_PROPERTY(TnoteItem *activeNote READ activeNote NOTIFY activeNoteChanged) + Q_PROPERTY(TnoteItem *lastNote READ lastNote NOTIFY lastNoteChanged) + Q_PROPERTY(qreal activeYpos READ activeYpos NOTIFY activeYposChanged) + Q_PROPERTY(qreal upperLine READ upperLine NOTIFY upperLineChanged) + Q_PROPERTY(qreal xFirstInActivBar READ xFirstInActivBar NOTIFY activeBarChanged) + Q_PROPERTY(qreal xLastInActivBar READ xLastInActivBar NOTIFY activeBarChanged) + Q_PROPERTY(bool allowAdding READ allowAdding WRITE setAllowAdding NOTIFY allowAddingChanged) + Q_PROPERTY(qreal touched READ touched NOTIFY touchedChanged) + Q_PROPERTY(Trhythm workRhythm READ workRhythm WRITE setWorkRhythm NOTIFY workRhythmChanged) + Q_PROPERTY(QString workRtmText READ workRtmText NOTIFY workRhythmChanged) + Q_PROPERTY(int workRtmValue READ workRtmValue WRITE setWorkRtmValue NOTIFY workRhythmChanged) + Q_PROPERTY(bool workRtmRest READ workRtmRest WRITE setWorkRtmRest NOTIFY workRhythmChanged) + Q_PROPERTY(bool workRtmDot READ workRtmDot WRITE setWorkRtmDot NOTIFY workRhythmChanged) + + Q_PROPERTY(Taction *insertNoteAct READ insertNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *deleteNoteAct READ deleteNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *clearScoreAct READ clearScoreAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *editModeAct READ editModeAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *wholeNoteAct READ wholeNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *halfNoteAct READ halfNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *quarterNoteAct READ quarterNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *eighthNoteAct READ eighthNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *sixteenthNoteAct READ sixteenthNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *restNoteAct READ restNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *dotNoteAct READ dotNoteAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *riseAct READ riseAct NOTIFY scoreActionsChanged) + Q_PROPERTY(Taction *lowerAct READ lowerAct NOTIFY scoreActionsChanged) + + friend class TstaffItem; + friend class TmeasureObject; + friend class TnoteItem; + friend class TaddNoteItem; + friend class TbeamObject; public: - explicit TscoreObject(QObject* parent = nullptr); - ~TscoreObject() override; - - /* ------------------ Musical parameters ------------------ */ - - Tmeter* meter() const { return m_meter; } - Q_INVOKABLE void setMeter(int m); - int meterToInt() const; /**< Small helper for QML property that converts meter enumerator into int */ - - /** - * General switch for score clef. All related things are based on this value - * It changes note positions according to clef or converts note into rest if the note is out of score range - */ - Tclef::EclefType clefType() const { return m_clefType; } - void setClefType(Tclef::EclefType ct); - - int keySignature() const { return static_cast<int>(m_keySignature); } - void setKeySignature(int k); + explicit TscoreObject(QObject *parent = nullptr); + ~TscoreObject() override; + + /* ------------------ Musical parameters ------------------ */ + + Tmeter *meter() const { return m_meter; } + Q_INVOKABLE void setMeter(int m); + int meterToInt() const; /**< Small helper for QML property that converts meter enumerator into int */ + + /** + * General switch for score clef. All related things are based on this value + * It changes note positions according to clef or converts note into rest if the note is out of score range + */ + Tclef::EclefType clefType() const { return m_clefType; } + void setClefType(Tclef::EclefType ct); + + int keySignature() const { return static_cast<int>(m_keySignature); } + void setKeySignature(int k); - /** - * Adds note @p newNote to score. - * in some situations QML part has to take additional actions - * like scrolling score to make added note visible. - * @p fromQML has to be set to @p TRUE then - so signal @p noteWasAdded() is emitted - * and QML performs required routines. - */ - Q_INVOKABLE void addNote(const Tnote& newNote, bool fromQML = false); - Q_INVOKABLE void setNote(TnoteItem* no, const Tnote& n); - Q_INVOKABLE void setNote(int noteNr, const Tnote& n); + /** + * Adds note @p newNote to score. + * in some situations QML part has to take additional actions + * like scrolling score to make added note visible. + * @p fromQML has to be set to @p TRUE then - so signal @p noteWasAdded() is emitted + * and QML performs required routines. + */ + Q_INVOKABLE void addNote(const Tnote &newNote, bool fromQML = false); + Q_INVOKABLE void setNote(TnoteItem *no, const Tnote &n); + Q_INVOKABLE void setNote(int noteNr, const Tnote &n); - void setTechnical(int noteId, quint32 tech); - - /** - * Returns a note item of @p TnoteItem - */ - Q_INVOKABLE TnoteItem* note(int noteId); - - QQuickItem* noteHead(int noteId); - - /** - * Returns note of given @p item or invalid (empty) one if item is null - */ - Q_INVOKABLE Tnote noteOfItem(TnoteItem* item) const; - - /** - * Returns note at given @p index or invalid note if there is no a note with that index - */ - Q_INVOKABLE Tnote noteAt(int index) const; - - Q_INVOKABLE void noteClicked(qreal yPos); - - /** - * This value represents accidental of cursor note. See @p Tnote::EAlter enumerator - */ - int cursorAlter() const { return m_cursorAlter; } - void setCursorAlter(int curAlt); - QString alterText(); - - /** - * Grabs melody from file and sets it into score. - * If @p melody parameter is set - it loads XML file to it, - * so this method caller can read more from opened melody - */ - Q_INVOKABLE void openMusicXml(const QString& musicFile, Tmelody* melody = nullptr, bool ignoreTechnical = false); - - Q_INVOKABLE void saveMusicXml(const QString& musicFile, const QString& title = QString(), - const QString& composer = QString(), int transposition = 0); - - /** - * Sets given melody to score. - * Score has its own mechanizm of dividing notes into measures, - * so measures of @p melody are ignored, only its meter,clef and key are respected. - * When @p notesAmount is set, only given number of notes is displayed in score, - * otherwise entire melody is taken. - * @p ignoreTechnical set to @p FALSE skips setting the technical data to notes - */ - void setMelody(Tmelody* melody, bool ignoreTechnical = false, int notesAmount = 0, int transposition = 0); - - void getMelody(Tmelody* melody); - - /* ------------------ Score switches ------------------ */ - - bool keySignatureEnabled() const { return m_keySignEnabled; } - void setKeySignatureEnabled(bool enKey); - - bool keyReadOnly() const { return m_keyReadOnly; } - void setKeyReadOnly(bool ro); - - bool showExtraAccids() const { return m_showExtraAccids; } - void setShowExtraAccids(bool accShow); - - bool enableDoubleAccidentals() { return m_enableDoubleAccids; } - void setEnableDoubleAccids(bool dblEnabled); - - /** - * If set, reminds about accidental changes occurred in previous measure - */ - bool remindAccids() const { return m_remindAccids; } - void setRemindAccids(bool doRemaind); - - bool showNoteNames() { return m_showNoteNames; } - void setShowNoteNames(bool showNames); - - QColor nameColor() const { return m_nameColor; } - void setNameColor(const QColor& nameC); - - int nameStyle() const { return m_nameStyle; } - void setNameStyle(int nameS); - - bool readOnly() const { return m_readOnly; } - void setReadOnly(bool ro); - - bool editMode() const { return m_editMode; } - void setEditMode(bool isEdit); - - bool singleNote() const { return m_singleNote; } - void setSingleNote(bool singleN); - - bool enharmNotesEnabled() const { return m_enharmNotesEnabled; } - void setEnharmNotesEnabled(bool enEn) { m_enharmNotesEnabled = enEn; } - - qreal scaleFactor() const { return m_scaleFactor; } - void setScaleFactor(qreal factor); - - /** - * Allows selecting notes when score is in read only state. - */ - bool selectInReadOnly() const { return m_selectInReadOnly; } - void setSelectInReadOnly(bool sel) { m_selectInReadOnly = sel; } - - bool enableTechnical() const { return m_enableTechnControl; } - void setEnableTechnical(bool enTech); - - /** - * Transposes all notes in the score by @p semis semitones: - * when @p semis is positive - transposes up, when negative - down. - * @p outScaleToRest determines when notes out of scale is changed to rest - * or raised/dropped octave down/up (when set to @p FALSE). - * @p loNote and @p hiNote determines scale (ambitus) - * in which transposed notes have to fit. - * If not set - current @p lowestNote() and @p highestNote() - * of the score are used. - */ - Q_INVOKABLE void transpose(int semis, bool outScaleToRest = true, const Tnote& loNote = Tnote(), const Tnote& hiNote = Tnote()); - - /* ------------------ Lists with score content (staves, measures notes) ------------------ */ - - int notesCount() const { return m_notes.count(); } - TnotePair* noteSegment(int id) { return m_segments[id]; } - TnotePair* firstSegment() { return m_segments.first(); } - TnotePair* lastSegment() { return m_segments.last(); } - - int measuresCount() const { return m_measures.count(); } - TmeasureObject* measure(int id) { return m_measures[id]; } - TmeasureObject* firstMeasure() { return m_measures.first(); } - TmeasureObject* lastMeasure() { return m_measures.last(); } - - int stavesCount() const { return m_staves.count(); } - TstaffItem* staff(int id) { return m_staves[id]; } - TstaffItem* firstStaff() { return m_staves.first(); } - TstaffItem* lastStaff() { return m_staves.last(); } - - QList<Tnote>& noteList() { return m_notes; } - - /** - * This array keeps values (-1, 0 or 1) for accidentals in key sign. - */ - qint8 accidInKey(int k) const { return m_accidInKeyArray[k]; } - - /* ------------------ Other helpers ------------------ */ - - qreal width() { return m_width; } - void setWidth(qreal w); - - /** - * Total height of all staves - */ - qreal stavesHeight(); - - /** - * Returns duration of given @param grNr group starting from measure beginning - * Describes grouping (beaming - beam connections) of notes in a single measure for current meter. - * This is a group of a few int values - each representing duration of the one group: - * - for 3/8 it is only single 36 value - whole measure under one beam - * - for 3/4 it is 24, 48, 72) - three groups - */ - quint8 groupPos(int grNr) const { return m_meterGroups[grNr]; } - - /** - * Number of beaming groups for this meter - */ - int groupCount() const { return m_meterGroups.count(); } - - qreal upperLine(); - - /** - * Returns highest possible note on the staff in current clef - */ - Q_INVOKABLE Tnote highestNote(); - - /** - * Returns lowest possible note on the staff in current clef - */ - Q_INVOKABLE Tnote lowestNote(); - - bool isPianoStaff() { return m_clefType == Tclef::PianoStaffClefs; } - - /** - * Returns next note to given @p someNote or null if given note was the last one - */ - Q_INVOKABLE TnoteItem* getNext(TnoteItem* someNote); - - /** - * Returns previous note to given @p someNote or null if given note was the first one - */ - Q_INVOKABLE TnoteItem* getPrev(TnoteItem* someNote); - - /** - * It keeps pointer to selected note item but selections itself is managed outside. - * Changes this has influence on @p selectedNote() which is read only - */ - TnoteItem* selectedItem() { return m_selectedItem; } - void setSelectedItem(TnoteItem* item); - - /** - * Selects next note towards currently selected one or selects first note if none was selected. - * IF @p keep, keeps currently note selected when it is the last in the score, - * if @p skipTies and currently selected note is tied, it selects next note after tie. - */ - void selectNext(bool keep, bool skipTies); - - /** - * Selects the last score note or none - */ - void selectLastNote() { setSelectedItem(lastNote()); } - - Tnote selectedNote() const { return noteOfItem(m_selectedItem); } - - QColor bgColor() const { return m_bgColor; } - void setBgColor(const QColor& bg); - -/* ------------------ Note cursor ------------------ */ - TnoteItem* activeNote() { return m_activeNote; } - qreal xFirstInActivBar(); - qreal xLastInActivBar(); - qreal activeYpos() const { return m_activeYpos; } - - Trhythm workRhythm() const; - void setWorkRhythm(const Trhythm& r); - - QString workRtmText() const; - - int workRtmValue() const; - void setWorkRtmValue(int rtmV); - - bool workRtmRest() const; - void setWorkRtmRest(bool hasRest); - - bool workRtmDot() const; - void setWorkRtmDot(bool hasDot); - - /** - * Returns text of active note head or appropriate rest symbol if work rhythm is set to rest - */ - Q_INVOKABLE QString activeRtmText(); - - Q_INVOKABLE Tnote posToNote(qreal yPos); - - /** - * A switch that triggers displaying controls for adding notes to the score. - * When @p TRUE, the last staff width is smaller about 'note add' control width (4.0). - * QML uses @p lastNote() position to place that control at the score end. - */ - bool allowAdding() const { return m_allowAdding; } - void setAllowAdding(bool allow); - - /** - * Touched state occurs when score got mouse press event without hover enter event before. - * In that moment all controls have to be hidden to give user all score for touch and set note pitch - * This state is managed by @p TnoteItem and @p TaddNoteItem classes. - */ - bool touched() const { return m_touched; } - - /** - * Last note item (@p TnoteItem) or null pointer if score is empty - */ - TnoteItem* lastNote(); - - /** - * Returns staff middle line position according to given @p actNote (active note) state. - * When it is set then middle of active staff is returned, - * or middle of the last staff (note cursor is on adding item then) - */ - Q_INVOKABLE qreal midLine(TnoteItem* actNote); - - Q_INVOKABLE void deleteLastNote(); - - Q_INVOKABLE void deleteNote(TnoteItem* n); - - /** - * Inserts note before @p afterItem according to current work rhythm - */ - Q_INVOKABLE void insertNote(TnoteItem* afterItem); - - Q_INVOKABLE void clearScore(); - -/* ------------------ Score actions (have to be initialized by enableActions() ) ------------------ */ - Taction* insertNoteAct() { return m_insertNoteAct; } - Taction* deleteNoteAct() { return m_deleteNoteAct; } - Taction* clearScoreAct() { return m_clearScoreAct; } - Taction* editModeAct() { return m_editModeAct; } - Taction* wholeNoteAct() { return m_wholeNoteAct; } - Taction* halfNoteAct() { return m_halfNoteAct; } - Taction* quarterNoteAct() { return m_quarterNoteAct; } - Taction* eighthNoteAct() { return m_eighthNoteAct; } - Taction* sixteenthNoteAct() { return m_sixteenthNoteAct; } - Taction* restNoteAct() { return m_restNoteAct; } - Taction* dotNoteAct() { return m_dotNoteAct; } - Taction* riseAct() { return m_riseAct; } - Taction* lowerAct() { return m_lowerAct; } - Taction* tieAct() { return m_tieAct; } - - Q_INVOKABLE void enableActions(); - - /** - * Common method to handle rhythm keys shortcuts - */ - void handleNoteAction(); - - /** - * By keeping those objects available we are reducing QML components creation time when called from C++ - */ - QQmlComponent* component() { return m_qmlComponent; } - QQmlEngine* qmlEngine() { return m_qmlEngine; } - - TclefOffset clefOffset() const { return m_clefOffset; } - - /** - * Connects or disconnects selected note (if any) with previous one if their pitches are the same. - * Set/unset ties, emits @p hasTieChanged() to inform score toolbox. - * Checks alters of all affected notes (adding tie on measure beginning changes the rule of displaying alter) - * Fit staff if necessary. - * Adds/removes extra tie at the staff beginning when needed - */ - Q_INVOKABLE void checkTieOfSelected(); + void setTechnical(int noteId, quint32 tech); + + /** + * Returns a note item of @p TnoteItem + */ + Q_INVOKABLE TnoteItem *note(int noteId); + + QQuickItem *noteHead(int noteId); + + /** + * Returns note of given @p item or invalid (empty) one if item is null + */ + Q_INVOKABLE Tnote noteOfItem(TnoteItem *item) const; + + /** + * Returns note at given @p index or invalid note if there is no a note with that index + */ + Q_INVOKABLE Tnote noteAt(int index) const; + + Q_INVOKABLE void noteClicked(qreal yPos); + + /** + * This value represents accidental of cursor note. See @p Tnote::EAlter enumerator + */ + int cursorAlter() const { return m_cursorAlter; } + void setCursorAlter(int curAlt); + QString alterText(); + + /** + * Grabs melody from file and sets it into score. + * If @p melody parameter is set - it loads XML file to it, + * so this method caller can read more from opened melody + */ + Q_INVOKABLE void openMusicXml(const QString &musicFile, Tmelody *melody = nullptr, bool ignoreTechnical = false); + + Q_INVOKABLE void saveMusicXml(const QString &musicFile, const QString &title = QString(), const QString &composer = QString(), int transposition = 0); + + /** + * Sets given melody to score. + * Score has its own mechanizm of dividing notes into measures, + * so measures of @p melody are ignored, only its meter,clef and key are respected. + * When @p notesAmount is set, only given number of notes is displayed in score, + * otherwise entire melody is taken. + * @p ignoreTechnical set to @p FALSE skips setting the technical data to notes + */ + void setMelody(Tmelody *melody, bool ignoreTechnical = false, int notesAmount = 0, int transposition = 0); + + void getMelody(Tmelody *melody); + + /* ------------------ Score switches ------------------ */ + + bool keySignatureEnabled() const { return m_keySignEnabled; } + void setKeySignatureEnabled(bool enKey); + + bool keyReadOnly() const { return m_keyReadOnly; } + void setKeyReadOnly(bool ro); + + bool showExtraAccids() const { return m_showExtraAccids; } + void setShowExtraAccids(bool accShow); + + bool enableDoubleAccidentals() { return m_enableDoubleAccids; } + void setEnableDoubleAccids(bool dblEnabled); + + /** + * If set, reminds about accidental changes occurred in previous measure + */ + bool remindAccids() const { return m_remindAccids; } + void setRemindAccids(bool doRemaind); + + bool showNoteNames() { return m_showNoteNames; } + void setShowNoteNames(bool showNames); + + QColor nameColor() const { return m_nameColor; } + void setNameColor(const QColor &nameC); + + int nameStyle() const { return m_nameStyle; } + void setNameStyle(int nameS); + + bool readOnly() const { return m_readOnly; } + void setReadOnly(bool ro); + + bool editMode() const { return m_editMode; } + void setEditMode(bool isEdit); + + bool singleNote() const { return m_singleNote; } + void setSingleNote(bool singleN); + + bool enharmNotesEnabled() const { return m_enharmNotesEnabled; } + void setEnharmNotesEnabled(bool enEn) { m_enharmNotesEnabled = enEn; } + + qreal scaleFactor() const { return m_scaleFactor; } + void setScaleFactor(qreal factor); + + /** + * Allows selecting notes when score is in read only state. + */ + bool selectInReadOnly() const { return m_selectInReadOnly; } + void setSelectInReadOnly(bool sel) { m_selectInReadOnly = sel; } + + bool enableTechnical() const { return m_enableTechnControl; } + void setEnableTechnical(bool enTech); + + /** + * Transposes all notes in the score by @p semis semitones: + * when @p semis is positive - transposes up, when negative - down. + * @p outScaleToRest determines when notes out of scale is changed to rest + * or raised/dropped octave down/up (when set to @p FALSE). + * @p loNote and @p hiNote determines scale (ambitus) + * in which transposed notes have to fit. + * If not set - current @p lowestNote() and @p highestNote() + * of the score are used. + */ + Q_INVOKABLE void transpose(int semis, bool outScaleToRest = true, const Tnote &loNote = Tnote(), const Tnote &hiNote = Tnote()); + + /* ------------------ Lists with score content (staves, measures notes) ------------------ */ + + int notesCount() const { return m_notes.count(); } + TnotePair *noteSegment(int id) { return m_segments[id]; } + TnotePair *firstSegment() { return m_segments.first(); } + TnotePair *lastSegment() { return m_segments.last(); } + + int measuresCount() const { return m_measures.count(); } + TmeasureObject *measure(int id) { return m_measures[id]; } + TmeasureObject *firstMeasure() { return m_measures.first(); } + TmeasureObject *lastMeasure() { return m_measures.last(); } + + int stavesCount() const { return m_staves.count(); } + TstaffItem *staff(int id) { return m_staves[id]; } + TstaffItem *firstStaff() { return m_staves.first(); } + TstaffItem *lastStaff() { return m_staves.last(); } + + QList<Tnote> ¬eList() { return m_notes; } + + /** + * This array keeps values (-1, 0 or 1) for accidentals in key sign. + */ + qint8 accidInKey(int k) const { return m_accidInKeyArray[k]; } + + /* ------------------ Other helpers ------------------ */ + + qreal width() { return m_width; } + void setWidth(qreal w); + + /** + * Total height of all staves + */ + qreal stavesHeight(); + + /** + * Returns duration of given @param grNr group starting from measure beginning + * Describes grouping (beaming - beam connections) of notes in a single measure for current meter. + * This is a group of a few int values - each representing duration of the one group: + * - for 3/8 it is only single 36 value - whole measure under one beam + * - for 3/4 it is 24, 48, 72) - three groups + */ + quint8 groupPos(int grNr) const { return m_meterGroups[grNr]; } + + /** + * Number of beaming groups for this meter + */ + int groupCount() const { return m_meterGroups.count(); } + + qreal upperLine(); + + /** + * Returns highest possible note on the staff in current clef + */ + Q_INVOKABLE Tnote highestNote(); + + /** + * Returns lowest possible note on the staff in current clef + */ + Q_INVOKABLE Tnote lowestNote(); + + bool isPianoStaff() { return m_clefType == Tclef::PianoStaffClefs; } + + /** + * Returns next note to given @p someNote or null if given note was the last one + */ + Q_INVOKABLE TnoteItem *getNext(TnoteItem *someNote); + + /** + * Returns previous note to given @p someNote or null if given note was the first one + */ + Q_INVOKABLE TnoteItem *getPrev(TnoteItem *someNote); + + /** + * It keeps pointer to selected note item but selections itself is managed outside. + * Changes this has influence on @p selectedNote() which is read only + */ + TnoteItem *selectedItem() { return m_selectedItem; } + void setSelectedItem(TnoteItem *item); + + /** + * Selects next note towards currently selected one or selects first note if none was selected. + * IF @p keep, keeps currently note selected when it is the last in the score, + * if @p skipTies and currently selected note is tied, it selects next note after tie. + */ + void selectNext(bool keep, bool skipTies); + + /** + * Selects the last score note or none + */ + void selectLastNote() { setSelectedItem(lastNote()); } + + Tnote selectedNote() const { return noteOfItem(m_selectedItem); } + + QColor bgColor() const { return m_bgColor; } + void setBgColor(const QColor &bg); + + /* ------------------ Note cursor ------------------ */ + TnoteItem *activeNote() { return m_activeNote; } + qreal xFirstInActivBar(); + qreal xLastInActivBar(); + qreal activeYpos() const { return m_activeYpos; } + + Trhythm workRhythm() const; + void setWorkRhythm(const Trhythm &r); + + QString workRtmText() const; + + int workRtmValue() const; + void setWorkRtmValue(int rtmV); + + bool workRtmRest() const; + void setWorkRtmRest(bool hasRest); + + bool workRtmDot() const; + void setWorkRtmDot(bool hasDot); + + /** + * Returns text of active note head or appropriate rest symbol if work rhythm is set to rest + */ + Q_INVOKABLE QString activeRtmText(); + + Q_INVOKABLE Tnote posToNote(qreal yPos); + + /** + * A switch that triggers displaying controls for adding notes to the score. + * When @p TRUE, the last staff width is smaller about 'note add' control width (4.0). + * QML uses @p lastNote() position to place that control at the score end. + */ + bool allowAdding() const { return m_allowAdding; } + void setAllowAdding(bool allow); + + /** + * Touched state occurs when score got mouse press event without hover enter event before. + * In that moment all controls have to be hidden to give user all score for touch and set note pitch + * This state is managed by @p TnoteItem and @p TaddNoteItem classes. + */ + bool touched() const { return m_touched; } + + /** + * Last note item (@p TnoteItem) or null pointer if score is empty + */ + TnoteItem *lastNote(); + + /** + * Returns staff middle line position according to given @p actNote (active note) state. + * When it is set then middle of active staff is returned, + * or middle of the last staff (note cursor is on adding item then) + */ + Q_INVOKABLE qreal midLine(TnoteItem *actNote); + + Q_INVOKABLE void deleteLastNote(); + + Q_INVOKABLE void deleteNote(TnoteItem *n); + + /** + * Inserts note before @p afterItem according to current work rhythm + */ + Q_INVOKABLE void insertNote(TnoteItem *afterItem); + + Q_INVOKABLE void clearScore(); + + /* ------------------ Score actions (have to be initialized by enableActions() ) ------------------ */ + Taction *insertNoteAct() { return m_insertNoteAct; } + Taction *deleteNoteAct() { return m_deleteNoteAct; } + Taction *clearScoreAct() { return m_clearScoreAct; } + Taction *editModeAct() { return m_editModeAct; } + Taction *wholeNoteAct() { return m_wholeNoteAct; } + Taction *halfNoteAct() { return m_halfNoteAct; } + Taction *quarterNoteAct() { return m_quarterNoteAct; } + Taction *eighthNoteAct() { return m_eighthNoteAct; } + Taction *sixteenthNoteAct() { return m_sixteenthNoteAct; } + Taction *restNoteAct() { return m_restNoteAct; } + Taction *dotNoteAct() { return m_dotNoteAct; } + Taction *riseAct() { return m_riseAct; } + Taction *lowerAct() { return m_lowerAct; } + Taction *tieAct() { return m_tieAct; } + + Q_INVOKABLE void enableActions(); + + /** + * Common method to handle rhythm keys shortcuts + */ + void handleNoteAction(); + + /** + * By keeping those objects available we are reducing QML components creation time when called from C++ + */ + QQmlComponent *component() { return m_qmlComponent; } + QQmlEngine *qmlEngine() { return m_qmlEngine; } + + TclefOffset clefOffset() const { return m_clefOffset; } + + /** + * Connects or disconnects selected note (if any) with previous one if their pitches are the same. + * Set/unset ties, emits @p hasTieChanged() to inform score toolbox. + * Checks alters of all affected notes (adding tie on measure beginning changes the rule of displaying alter) + * Fit staff if necessary. + * Adds/removes extra tie at the staff beginning when needed + */ + Q_INVOKABLE void checkTieOfSelected(); signals: - void meterChanged(); - - void clefTypeChanged(); - - /** - * Asks Score.qml about create new staff - */ - void staffCreate(); - - void staffDestroying(int staffNr); - - /** - * Usually those props are managed outside of @p TscoreObject - by QML - * but in case @p keySignatureEnabled() and @p keySignature() are changed 'internally' - * QML has to react on that change, so @p clefTypeChanged(), @p keySignatureEnabledChanged() and @p keySignatureChanged() are emitted - */ - void keySignatureEnabledChanged(); - void keyReadOnlyChanged(); - void keySignatureChanged(); - - /** - * Informs Score.qml that content widget height has to be adjusted to all staves height - */ - void stavesHeightChanged(); - - void activeNoteChanged(); - void activeYposChanged(); - void upperLineChanged(); - - /** - * When active note was clicked - */ - void clicked(); - - /** - * Emitted when number of active measure changes, but also when measure moves (note positions are changed) - */ - void activeBarChanged(); - void allowAddingChanged(); - - void touchedChanged(); - - /** - * It is emitted whenever last note changes due to score refactoring - * (i.e. when meter changes and all notes are removed and added again) - * QML score depends on it - */ - void lastNoteChanged(); - void noteWasAdded(); - void scoreWasCleared(); - void cursorAlterChanged(); - - void workRhythmChanged(); - - void selectedItemChanged(); - void selectedNoteChanged(); - void readOnlyChanged(); - void editModeChanged(); - void singleNoteChanged(); - void scaleFactorChanged(); - void enableTechnicalChanged(); - void bgColorChanged(); - void readOnlyNoteClicked(int noteId); - - /** - * Dummy, never invoked - score actions @p Taction are created once - */ - void scoreActionsChanged(); + void meterChanged(); + + void clefTypeChanged(); + + /** + * Asks Score.qml about create new staff + */ + void staffCreate(); + + void staffDestroying(int staffNr); + + /** + * Usually those props are managed outside of @p TscoreObject - by QML + * but in case @p keySignatureEnabled() and @p keySignature() are changed 'internally' + * QML has to react on that change, so @p clefTypeChanged(), @p keySignatureEnabledChanged() and @p keySignatureChanged() are emitted + */ + void keySignatureEnabledChanged(); + void keyReadOnlyChanged(); + void keySignatureChanged(); + + /** + * Informs Score.qml that content widget height has to be adjusted to all staves height + */ + void stavesHeightChanged(); + + void activeNoteChanged(); + void activeYposChanged(); + void upperLineChanged(); + + /** + * When active note was clicked + */ + void clicked(); + + /** + * Emitted when number of active measure changes, but also when measure moves (note positions are changed) + */ + void activeBarChanged(); + void allowAddingChanged(); + + void touchedChanged(); + + /** + * It is emitted whenever last note changes due to score refactoring + * (i.e. when meter changes and all notes are removed and added again) + * QML score depends on it + */ + void lastNoteChanged(); + void noteWasAdded(); + void scoreWasCleared(); + void cursorAlterChanged(); + + void workRhythmChanged(); + + void selectedItemChanged(); + void selectedNoteChanged(); + void readOnlyChanged(); + void editModeChanged(); + void singleNoteChanged(); + void scaleFactorChanged(); + void enableTechnicalChanged(); + void bgColorChanged(); + void readOnlyNoteClicked(int noteId); + + /** + * Dummy, never invoked - score actions @p Taction are created once + */ + void scoreActionsChanged(); protected: - void addStaff(TstaffItem* st); - - /** - * Shifts @p count measures starting from @p measureNr by setting first and last ids - * of the next measure to given @p sourceStaff. - * Also it manages ownership of initial (source) staff shifted notes and sets parent of every such note to target staff. - */ - void startStaffFromMeasure(TstaffItem* sourceStaff, int measureNr, int count = 1); - - void deleteStaff(TstaffItem* st); - - /** - * Score width is handled by Score.qml, but it has to be known here. - * During window scaling width of score changes a few times before it is stabilized. - * To avoid scaling staves more times than one for width change, - * a timer guards the width change and call @p adjustScoreWidth() method only after delay - */ - void adjustScoreWidth(int firstStaff = 0); - - void adjustScoreWidthSlot() { adjustScoreWidth(); } - - bool adjustInProgress() { return m_adjustInProgress; } - - void updateStavesPos(); - - void onIndentChanged(); - - /** - * Returns number of note that starts a tie through @p x() - * and number of note that ends the tie through @p y() - */ - QPoint tieRange(TnoteItem* n); - -/* ------------------ Note cursor ------------------ */ - - /** - * Sets note over which one cursor appears or null if none - */ - void changeActiveNote(TnoteItem* aNote); - void setActiveNotePos(qreal yPos); - - /** - * Delays hiding note cursor when touch was released - */ - QTimer* touchHideTimer() { return m_touchHideTimer; } - - TnoteItem* hoveredNote() { return m_hoveredNote; } - void setHoveredNote(TnoteItem* hn) { m_hoveredNote = hn; } - - TnoteItem* pressedNote() { return m_presseddNote; } - void setPressedNote(TnoteItem* pn) { m_presseddNote = pn; } - - /** - * Positioning notes in a staff invokes it to keep position of first/last notes in a bar actual - */ - void emitActiveBarChanged() { emit activeBarChanged(); } - - /** - * emitting @p lastNoteChanged() signal updates position of 'note add' QML control, if note adding is allowed - */ - void emitLastNote() { if (m_allowAdding) emit lastNoteChanged(); } - - void setTouched(bool t); - - /** - * Common method to obtain a beam object (@p TbeamObject) - * Either created on demand or taken from @p m_spareBeams list - */ - TbeamObject* getBeam(TnotePair* np, TmeasureObject* m); - - /** - * Saves given beam to the @p m_spareBeams list - */ - void storeBeam(TbeamObject* b); - - /** - * Adds empty measure to the score and return its pointer - */ - TmeasureObject* addMeasure(); - - /** - * squeezes extra note @p np silently, without invoking visual changes - * into measure @p m (if provided) - */ - TnotePair* insertSilently(int id, const Tnote& n, TmeasureObject* m = nullptr); + void addStaff(TstaffItem *st); + + /** + * Shifts @p count measures starting from @p measureNr by setting first and last ids + * of the next measure to given @p sourceStaff. + * Also it manages ownership of initial (source) staff shifted notes and sets parent of every such note to target staff. + */ + void startStaffFromMeasure(TstaffItem *sourceStaff, int measureNr, int count = 1); + + void deleteStaff(TstaffItem *st); + + /** + * Score width is handled by Score.qml, but it has to be known here. + * During window scaling width of score changes a few times before it is stabilized. + * To avoid scaling staves more times than one for width change, + * a timer guards the width change and call @p adjustScoreWidth() method only after delay + */ + void adjustScoreWidth(int firstStaff = 0); + + void adjustScoreWidthSlot() { adjustScoreWidth(); } + + bool adjustInProgress() { return m_adjustInProgress; } + + void updateStavesPos(); + + void onIndentChanged(); + + /** + * Returns number of note that starts a tie through @p x() + * and number of note that ends the tie through @p y() + */ + QPoint tieRange(TnoteItem *n); + + /* ------------------ Note cursor ------------------ */ + + /** + * Sets note over which one cursor appears or null if none + */ + void changeActiveNote(TnoteItem *aNote); + void setActiveNotePos(qreal yPos); + + /** + * Delays hiding note cursor when touch was released + */ + QTimer *touchHideTimer() { return m_touchHideTimer; } + + TnoteItem *hoveredNote() { return m_hoveredNote; } + void setHoveredNote(TnoteItem *hn) { m_hoveredNote = hn; } + + TnoteItem *pressedNote() { return m_presseddNote; } + void setPressedNote(TnoteItem *pn) { m_presseddNote = pn; } + + /** + * Positioning notes in a staff invokes it to keep position of first/last notes in a bar actual + */ + void emitActiveBarChanged() { emit activeBarChanged(); } + + /** + * emitting @p lastNoteChanged() signal updates position of 'note add' QML control, if note adding is allowed + */ + void emitLastNote() + { + if (m_allowAdding) + emit lastNoteChanged(); + } + + void setTouched(bool t); + + /** + * Common method to obtain a beam object (@p TbeamObject) + * Either created on demand or taken from @p m_spareBeams list + */ + TbeamObject *getBeam(TnotePair *np, TmeasureObject *m); + + /** + * Saves given beam to the @p m_spareBeams list + */ + void storeBeam(TbeamObject *b); + + /** + * Adds empty measure to the score and return its pointer + */ + TmeasureObject *addMeasure(); + + /** + * squeezes extra note @p np silently, without invoking visual changes + * into measure @p m (if provided) + */ + TnotePair *insertSilently(int id, const Tnote &n, TmeasureObject *m = nullptr); private: - /** - * Appends notes to @p m_notes list, creates corresponding @p TnotePair - * and adds them to @p m_segments list - */ - void appendToNoteList(QList<Tnote>& l); - void updateClefOffset(); - - /** - * Returns @p TnotePair - a segment that can be either created or taken from @p m_spareSegments - */ - TnotePair* getSegment(int noteNr, Tnote* n); - - /** - * Returns @p TmeasureObject - a measure that can be either created or taken from @p m_spareMeasures - */ - TmeasureObject* getMeasure(int barNr = 0); - - /** - * According to actual clef, returns global note number (independent on octave) from given note position - */ - int globalNoteNr(qreal yPos); - - void clearScorePrivate(); - - void enterTimeElapsed(); - void leaveTimeElapsed(); - - /** - * Check given note is it in score range and changes it into rest if not - */ - void fitToRange(Tnote& n); - void resetNoteItem(TnoteItem* noteItem); - - /** - * Removes the last measure, returns @p TRUE if staff was also removed - */ - bool removeLastMeasure(); + /** + * Appends notes to @p m_notes list, creates corresponding @p TnotePair + * and adds them to @p m_segments list + */ + void appendToNoteList(QList<Tnote> &l); + void updateClefOffset(); + + /** + * Returns @p TnotePair - a segment that can be either created or taken from @p m_spareSegments + */ + TnotePair *getSegment(int noteNr, Tnote *n); + + /** + * Returns @p TmeasureObject - a measure that can be either created or taken from @p m_spareMeasures + */ + TmeasureObject *getMeasure(int barNr = 0); + + /** + * According to actual clef, returns global note number (independent on octave) from given note position + */ + int globalNoteNr(qreal yPos); + + void clearScorePrivate(); + + void enterTimeElapsed(); + void leaveTimeElapsed(); + + /** + * Check given note is it in score range and changes it into rest if not + */ + void fitToRange(Tnote &n); + void resetNoteItem(TnoteItem *noteItem); + + /** + * Removes the last measure, returns @p TRUE if staff was also removed + */ + bool removeLastMeasure(); private: - /* Musical parameters */ - Tclef::EclefType m_clefType = Tclef::Treble_G; - Tmeter *m_meter; - qint8 m_keySignature = 0; - /* Score switches */ - bool m_keySignEnabled; - bool m_keyReadOnly = false; - bool m_showExtraAccids; - bool m_remindAccids; - bool m_enableDoubleAccids; - bool m_showNoteNames; - bool m_readOnly = false; - bool m_editMode = false; - bool m_singleNote = false; - bool m_enharmNotesEnabled = false; - qreal m_scaleFactor = 1.0; - bool m_selectInReadOnly = false; - bool m_enableTechnControl = false; - /* Lists with notes, measures, staves, meter groups */ - QList<TnotePair*> m_segments; - QList<TnotePair*> m_spareSegments; - QList<TstaffItem*> m_staves; - QList<TmeasureObject*> m_measures; - QList<TmeasureObject*> m_spareMeasures; - QList<Tnote> m_notes; - QList<quint8> m_meterGroups; - QList<TbeamObject*> m_spareBeams; - /* Helper variables */ - TclefOffset m_clefOffset; - qreal m_width; - bool m_adjustInProgress; - bool m_keyChanged = false; - QTimer *m_widthTimer; - qint8 m_accidInKeyArray[7]; - QQmlEngine *m_qmlEngine; - QQmlComponent *m_qmlComponent; - QColor m_nameColor; - int m_nameStyle; - TnoteItem *m_selectedItem = nullptr; - QColor m_bgColor; - /* Note cursor */ - TnoteItem *m_activeNote = nullptr; - qreal m_activeYpos = 0.0; - QTimer *m_touchHideTimer; - TnoteItem *m_hoveredNote = nullptr; - TnoteItem *m_presseddNote = nullptr; - int m_cursorAlter = 0; - int m_activeBarNr = -1; - Trhythm *m_workRhythm; - bool m_allowAdding = false; - bool m_touched = false; - QTimer *m_enterTimer, *m_leaveTimer; - - Taction *m_deleteNoteAct = nullptr; - Taction *m_clearScoreAct = nullptr; - Taction *m_wholeNoteAct = nullptr; - Taction *m_editModeAct = nullptr; - Taction *m_halfNoteAct = nullptr; - Taction *m_quarterNoteAct = nullptr; - Taction *m_eighthNoteAct = nullptr; - Taction *m_sixteenthNoteAct = nullptr; - Taction *m_restNoteAct = nullptr; - Taction *m_dotNoteAct = nullptr; - Taction *m_insertNoteAct = nullptr; - Taction *m_riseAct = nullptr; - Taction *m_lowerAct = nullptr; - Taction *m_tieAct = nullptr; - + /* Musical parameters */ + Tclef::EclefType m_clefType = Tclef::Treble_G; + Tmeter *m_meter; + qint8 m_keySignature = 0; + /* Score switches */ + bool m_keySignEnabled; + bool m_keyReadOnly = false; + bool m_showExtraAccids; + bool m_remindAccids; + bool m_enableDoubleAccids; + bool m_showNoteNames; + bool m_readOnly = false; + bool m_editMode = false; + bool m_singleNote = false; + bool m_enharmNotesEnabled = false; + qreal m_scaleFactor = 1.0; + bool m_selectInReadOnly = false; + bool m_enableTechnControl = false; + /* Lists with notes, measures, staves, meter groups */ + QList<TnotePair *> m_segments; + QList<TnotePair *> m_spareSegments; + QList<TstaffItem *> m_staves; + QList<TmeasureObject *> m_measures; + QList<TmeasureObject *> m_spareMeasures; + QList<Tnote> m_notes; + QList<quint8> m_meterGroups; + QList<TbeamObject *> m_spareBeams; + /* Helper variables */ + TclefOffset m_clefOffset; + qreal m_width; + bool m_adjustInProgress; + bool m_keyChanged = false; + QTimer *m_widthTimer; + qint8 m_accidInKeyArray[7]; + QQmlEngine *m_qmlEngine; + QQmlComponent *m_qmlComponent; + QColor m_nameColor; + int m_nameStyle; + TnoteItem *m_selectedItem = nullptr; + QColor m_bgColor; + /* Note cursor */ + TnoteItem *m_activeNote = nullptr; + qreal m_activeYpos = 0.0; + QTimer *m_touchHideTimer; + TnoteItem *m_hoveredNote = nullptr; + TnoteItem *m_presseddNote = nullptr; + int m_cursorAlter = 0; + int m_activeBarNr = -1; + Trhythm *m_workRhythm; + bool m_allowAdding = false; + bool m_touched = false; + QTimer *m_enterTimer, *m_leaveTimer; + + Taction *m_deleteNoteAct = nullptr; + Taction *m_clearScoreAct = nullptr; + Taction *m_wholeNoteAct = nullptr; + Taction *m_editModeAct = nullptr; + Taction *m_halfNoteAct = nullptr; + Taction *m_quarterNoteAct = nullptr; + Taction *m_eighthNoteAct = nullptr; + Taction *m_sixteenthNoteAct = nullptr; + Taction *m_restNoteAct = nullptr; + Taction *m_dotNoteAct = nullptr; + Taction *m_insertNoteAct = nullptr; + Taction *m_riseAct = nullptr; + Taction *m_lowerAct = nullptr; + Taction *m_tieAct = nullptr; }; #endif // TSCOREOBJECT_H diff --git a/src/libs/core/score/tstaffitem.cpp b/src/libs/core/score/tstaffitem.cpp index 3b68813b9683edf5e54e29cf7dced7579f1b8b09..73eaece06a6fa69e557b045d39ee535c0fe23f70 100644 --- a/src/libs/core/score/tstaffitem.cpp +++ b/src/libs/core/score/tstaffitem.cpp @@ -17,322 +17,327 @@ ***************************************************************************/ #include "tstaffitem.h" -#include "tscoreobject.h" +#include "music/tnote.h" #include "tmeasureobject.h" #include "tnoteitem.h" #include "tnotepair.h" -#include "music/tnote.h" +#include "tscoreobject.h" +#include <QtCore/qdebug.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> #include <QtQml/qqmlengine.h> -#include <QtCore/qdebug.h> - -TstaffItem::TstaffItem(QQuickItem* parent) : - QQuickItem(parent), - m_scoreObj(nullptr), - m_upperLine(16.0), - m_loNotePos(28.0), m_hiNotePos(12.0), - m_number(-1), - m_firstMeasureId(0), m_lastMeasureId(-1) +TstaffItem::TstaffItem(QQuickItem *parent) + : QQuickItem(parent) + , m_scoreObj(nullptr) + , m_upperLine(16.0) + , m_loNotePos(28.0) + , m_hiNotePos(12.0) + , m_number(-1) + , m_firstMeasureId(0) + , m_lastMeasureId(-1) { } - -TstaffItem::~TstaffItem() { -// qDebug() << "[TstaffObject]" << m_number << " is going delete"; +TstaffItem::~TstaffItem() +{ + // qDebug() << "[TstaffObject]" << m_number << " is going delete"; } - -void TstaffItem::setScordSpace(qreal hasScord) { - if (m_scordSpace != hasScord) { - m_scordSpace = hasScord; - checkNotesRange(); - } +void TstaffItem::setScordSpace(qreal hasScord) +{ + if (m_scordSpace != hasScord) { + m_scordSpace = hasScord; + checkNotesRange(); + } } - -void TstaffItem::setScore(TscoreObject* s) { - m_scoreObj = s; - m_scoreObj->addStaff(this); - if (m_scoreObj->stavesCount() > 1) { // initial staff position, depends on lowest note in the previous staff - auto prevStaff = m_scoreObj->staff(m_scoreObj->stavesCount() - 2); - setY(prevStaff->y() + (prevStaff->loNotePos() - hiNotePos() + 4.0) * prevStaff->scale()); - } +void TstaffItem::setScore(TscoreObject *s) +{ + m_scoreObj = s; + m_scoreObj->addStaff(this); + if (m_scoreObj->stavesCount() > 1) { // initial staff position, depends on lowest note in the previous staff + auto prevStaff = m_scoreObj->staff(m_scoreObj->stavesCount() - 2); + setY(prevStaff->y() + (prevStaff->loNotePos() - hiNotePos() + 4.0) * prevStaff->scale()); + } } - -TmeasureObject* TstaffItem::firstMeasure() { return m_scoreObj->measure(m_firstMeasureId); } - -TmeasureObject* TstaffItem::lastMeasure() { return m_scoreObj->measure(m_lastMeasureId); } - - -void TstaffItem::refresh() { - fit(); - checkNotesRange(); +TmeasureObject *TstaffItem::firstMeasure() +{ + return m_scoreObj->measure(m_firstMeasureId); } - -void TstaffItem::setUpperLine(qreal upLine) { - if (m_upperLine != upLine) { - m_upperLine = upLine; - emit upperLineChanged(); - } +TmeasureObject *TstaffItem::lastMeasure() +{ + return m_scoreObj->measure(m_lastMeasureId); } +void TstaffItem::refresh() +{ + fit(); + checkNotesRange(); +} -int TstaffItem::firstMeasureNr() { - return m_lastMeasureId == -1 ? 0 : (m_firstMeasureId < m_scoreObj->measuresCount() ? m_scoreObj->measure(m_firstMeasureId)->number() : 0); +void TstaffItem::setUpperLine(qreal upLine) +{ + if (m_upperLine != upLine) { + m_upperLine = upLine; + emit upperLineChanged(); + } } +int TstaffItem::firstMeasureNr() +{ + return m_lastMeasureId == -1 ? 0 : (m_firstMeasureId < m_scoreObj->measuresCount() ? m_scoreObj->measure(m_firstMeasureId)->number() : 0); +} /** * Indent changes when key signature accidentals are added. It changes gap factor, can cause measure shifting. * For multiple staves on the score, last staff gets indent change at the end of queue. * Only then gap factor can be calculated. */ -void TstaffItem::setNotesIndent(qreal ni) { - if (m_notesIndent != ni) { - m_notesIndent = ni; - if (this == m_scoreObj->lastStaff()) - m_scoreObj->onIndentChanged(); - } +void TstaffItem::setNotesIndent(qreal ni) +{ + if (m_notesIndent != ni) { + m_notesIndent = ni; + if (this == m_scoreObj->lastStaff()) + m_scoreObj->onIndentChanged(); + } } - -bool TstaffItem::isPianoStaff() const { - return m_scoreObj->isPianoStaff(); +bool TstaffItem::isPianoStaff() const +{ + return m_scoreObj->isPianoStaff(); } - -char TstaffItem::debug() { - QTextStream o(stdout); - o << "\033[01;34m[" << number() + 1 << " STAFF]\033[01;00m"; - return 32; // fake +char TstaffItem::debug() +{ + QTextStream o(stdout); + o << "\033[01;34m[" << number() + 1 << " STAFF]\033[01;00m"; + return 32; // fake } - -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# #define BARLINE_OFFSET (2.0) -void TstaffItem::fit() { - if ((m_number == 0 && m_scoreObj->measure(m_firstMeasureId)->isEmpty()) || m_lastMeasureId == -1 || measuresCount() < 1) { -// qDebug() << debug() << "Empty staff - nothing to fit"; - return; - } - - qreal factor = 2.5; - m_gapsSum = 0.0; - m_allNotesWidth = 0.0; - qreal availableWidth; - - for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { - auto measure = m_scoreObj->measure(m); - m_allNotesWidth += measure->allNotesWidth() + (m > m_firstMeasureId ? BARLINE_OFFSET : 0.0); // add bar line space - m_gapsSum += measure->gapsSum(); - availableWidth = m_scoreObj->width() - m_notesIndent - m_allNotesWidth - 1.0 - (m_scoreObj->allowAdding() && m_scoreObj->lastStaff() == this ? 5.0 : 0.0); - factor = availableWidth / m_gapsSum; - if (factor < 0.8) { // shift current measure and the next ones - if (m == m_firstMeasureId) { // first measure in the staff - qDebug() << debug() << "!!!!!! Split this measure among staves !!!!!"; - break; - } else { - m_gapsSum -= measure->gapsSum(); - m_allNotesWidth -= measure->allNotesWidth(); - if (m > m_firstMeasureId) - m_allNotesWidth -= BARLINE_OFFSET; - m_gapFactor = (m_scoreObj->width() - m_notesIndent - m_allNotesWidth - 1.0) / m_gapsSum; // allow factor bigger than 2.5 - m_scoreObj->startStaffFromMeasure(this, m, m_lastMeasureId - (m - 1)); - m_scoreObj->staff(m_number + 1)->createExtraTie(measure->first()->item()); - m_lastMeasureId = m - 1; - updateNotesPos(); - checkNotesRange(); - if (!m_scoreObj->adjustInProgress()) - m_scoreObj->staff(m_number + 1)->refresh(); - m_scoreObj->updateStavesPos(); - return; - } +void TstaffItem::fit() +{ + if ((m_number == 0 && m_scoreObj->measure(m_firstMeasureId)->isEmpty()) || m_lastMeasureId == -1 || measuresCount() < 1) { + // qDebug() << debug() << "Empty staff - nothing to fit"; + return; } - } - if (factor > 1.5 && this != m_scoreObj->lastStaff()) { - int m = m_lastMeasureId + 1; - if (m >= m_scoreObj->measuresCount()) { // TODO delete debug message if not occurs - qDebug() << debug() << "Next staff exists but there are no more measures. IT SHOULD NEVER HAPPEN!"; - return; - } else { - auto nextMeasure = m_scoreObj->measure(m); - auto nextStaff = m_scoreObj->staff(m_number + 1); - qreal tempGapSum = m_gapsSum; - availableWidth -= nextMeasure->allNotesWidth(); - tempGapSum += nextMeasure->gapsSum(); - if (availableWidth / tempGapSum > 0.8) { - m_lastMeasureId = m; - nextMeasure->setStaff(this); - nextStaff->deleteExtraTie(); - nextStaff->setFirstMeasureId(m + 1); // if there is not next measure - next staff will be deleted - if (nextStaff->measuresCount() < 1) - m_scoreObj->deleteStaff(nextStaff); - else - nextStaff->createExtraTie(nextStaff->firstMeasure()->first()->item()); - fit(); - checkNotesRange(); - return; - } + qreal factor = 2.5; + m_gapsSum = 0.0; + m_allNotesWidth = 0.0; + qreal availableWidth; + + for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { + auto measure = m_scoreObj->measure(m); + m_allNotesWidth += measure->allNotesWidth() + (m > m_firstMeasureId ? BARLINE_OFFSET : 0.0); // add bar line space + m_gapsSum += measure->gapsSum(); + availableWidth = + m_scoreObj->width() - m_notesIndent - m_allNotesWidth - 1.0 - (m_scoreObj->allowAdding() && m_scoreObj->lastStaff() == this ? 5.0 : 0.0); + factor = availableWidth / m_gapsSum; + if (factor < 0.8) { // shift current measure and the next ones + if (m == m_firstMeasureId) { // first measure in the staff + qDebug() << debug() << "!!!!!! Split this measure among staves !!!!!"; + break; + } else { + m_gapsSum -= measure->gapsSum(); + m_allNotesWidth -= measure->allNotesWidth(); + if (m > m_firstMeasureId) + m_allNotesWidth -= BARLINE_OFFSET; + m_gapFactor = (m_scoreObj->width() - m_notesIndent - m_allNotesWidth - 1.0) / m_gapsSum; // allow factor bigger than 2.5 + m_scoreObj->startStaffFromMeasure(this, m, m_lastMeasureId - (m - 1)); + m_scoreObj->staff(m_number + 1)->createExtraTie(measure->first()->item()); + m_lastMeasureId = m - 1; + updateNotesPos(); + checkNotesRange(); + if (!m_scoreObj->adjustInProgress()) + m_scoreObj->staff(m_number + 1)->refresh(); + m_scoreObj->updateStavesPos(); + return; + } + } } - } - m_gapFactor = qBound(0.5, factor, 2.5); // notes in this staff are ready to positioning - updateNotesPos(); + if (factor > 1.5 && this != m_scoreObj->lastStaff()) { + int m = m_lastMeasureId + 1; + if (m >= m_scoreObj->measuresCount()) { // TODO delete debug message if not occurs + qDebug() << debug() << "Next staff exists but there are no more measures. IT SHOULD NEVER HAPPEN!"; + return; + } else { + auto nextMeasure = m_scoreObj->measure(m); + auto nextStaff = m_scoreObj->staff(m_number + 1); + qreal tempGapSum = m_gapsSum; + availableWidth -= nextMeasure->allNotesWidth(); + tempGapSum += nextMeasure->gapsSum(); + if (availableWidth / tempGapSum > 0.8) { + m_lastMeasureId = m; + nextMeasure->setStaff(this); + nextStaff->deleteExtraTie(); + nextStaff->setFirstMeasureId(m + 1); // if there is not next measure - next staff will be deleted + if (nextStaff->measuresCount() < 1) + m_scoreObj->deleteStaff(nextStaff); + else + nextStaff->createExtraTie(nextStaff->firstMeasure()->first()->item()); + fit(); + checkNotesRange(); + return; + } + } + } + + m_gapFactor = qBound(0.5, factor, 2.5); // notes in this staff are ready to positioning + updateNotesPos(); } +void TstaffItem::updateNotesPos(int startMeasure) +{ + auto firstMeas = firstMeasure(); + if (firstMeas->isEmpty()) + return; -void TstaffItem::updateNotesPos(int startMeasure) { - auto firstMeas = firstMeasure(); - if (firstMeas->isEmpty()) - return; - -// qDebug() << debug() << "updating notes positions from" << startMeasure << "measure among number" << measuresCount() -// << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() - firstMeasure()->first()->index() + 1; - TnoteItem* prevNote = nullptr; - if (startMeasure == 0) - firstMeas->first()->item()->setX(m_notesIndent); - else - prevNote = m_scoreObj->measure(startMeasure - 1)->last()->item(); - - for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { - auto measure = m_scoreObj->measure(m); - if (measure->staff() != this) { // TODO delete debug message if not occurs - qDebug() << debug() << "Something went wrong, measure" << measure->number() << "doesn't belong to staff" << m_number << "FIXING!"; - measure->setStaff(this); - } - qreal barOffset = m > 0 ? BARLINE_OFFSET : 0.0; // offset for first note after bar line - for (int n = 0; n < measure->noteCount(); ++n) { - auto note = measure->note(n)->item(); - if (prevNote) - note->setX(prevNote->rightX() + barOffset); - prevNote = note; - barOffset = 0.0; + // qDebug() << debug() << "updating notes positions from" << startMeasure << "measure among number" << measuresCount() + // << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() - firstMeasure()->first()->index() + 1; + TnoteItem *prevNote = nullptr; + if (startMeasure == 0) + firstMeas->first()->item()->setX(m_notesIndent); + else + prevNote = m_scoreObj->measure(startMeasure - 1)->last()->item(); + + for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { + auto measure = m_scoreObj->measure(m); + if (measure->staff() != this) { // TODO delete debug message if not occurs + qDebug() << debug() << "Something went wrong, measure" << measure->number() << "doesn't belong to staff" << m_number << "FIXING!"; + measure->setStaff(this); + } + qreal barOffset = m > 0 ? BARLINE_OFFSET : 0.0; // offset for first note after bar line + for (int n = 0; n < measure->noteCount(); ++n) { + auto note = measure->note(n)->item(); + if (prevNote) + note->setX(prevNote->rightX() + barOffset); + prevNote = note; + barOffset = 0.0; + } + measure->checkBarLine(); } - measure->checkBarLine(); - } - m_scoreObj->emitActiveBarChanged(); + m_scoreObj->emitActiveBarChanged(); } - -void TstaffItem::checkNotesRange(bool doEmit) { - qreal oldHi = m_hiNotePos, oldLo = m_loNotePos; - findHighestNote(); - findLowestNote(); - if (m_number > 0 && doEmit && oldHi != m_hiNotePos) // never do not emit for first staff - emit hiNotePosChanged(m_number, (oldHi - m_hiNotePos) * scale()); - if (doEmit && oldLo != m_loNotePos) - emit loNotePosChanged(m_number, (m_loNotePos - oldLo) * scale()); +void TstaffItem::checkNotesRange(bool doEmit) +{ + qreal oldHi = m_hiNotePos, oldLo = m_loNotePos; + findHighestNote(); + findLowestNote(); + if (m_number > 0 && doEmit && oldHi != m_hiNotePos) // never do not emit for first staff + emit hiNotePosChanged(m_number, (oldHi - m_hiNotePos) * scale()); + if (doEmit && oldLo != m_loNotePos) + emit loNotePosChanged(m_number, (m_loNotePos - oldLo) * scale()); } - -void TstaffItem::appendMeasure(TmeasureObject* m) { - m_lastMeasureId = m->number(); - m->setStaff(this); +void TstaffItem::appendMeasure(TmeasureObject *m) +{ + m_lastMeasureId = m->number(); + m->setStaff(this); } - -void TstaffItem::insertMeasure(int index, TmeasureObject* m) { - qDebug() << debug() << "Inserting measure nr" << m->number() + 1 << "at" << index; - if (index < m_firstMeasureId) { - m_firstMeasureId = index; - emit firstMeasureNrChanged(); - } - if (index > m_lastMeasureId) - m_lastMeasureId = index; - m->setStaff(this); +void TstaffItem::insertMeasure(int index, TmeasureObject *m) +{ + qDebug() << debug() << "Inserting measure nr" << m->number() + 1 << "at" << index; + if (index < m_firstMeasureId) { + m_firstMeasureId = index; + emit firstMeasureNrChanged(); + } + if (index > m_lastMeasureId) + m_lastMeasureId = index; + m->setStaff(this); } - -void TstaffItem::deleteExtraTie() { - if (m_extraTie) { - delete m_extraTie; - m_extraTie = nullptr; - } +void TstaffItem::deleteExtraTie() +{ + if (m_extraTie) { + delete m_extraTie; + m_extraTie = nullptr; + } } - -TnotePair* TstaffItem::firstNote() { - return measuresCount() ? (firstMeasure()->isEmpty() ? nullptr : firstMeasure()->first()) : nullptr; +TnotePair *TstaffItem::firstNote() +{ + return measuresCount() ? (firstMeasure()->isEmpty() ? nullptr : firstMeasure()->first()) : nullptr; } - -TnotePair* TstaffItem::lastNote() { - return m_lastMeasureId > -1 ? lastMeasure()->last() : nullptr; +TnotePair *TstaffItem::lastNote() +{ + return m_lastMeasureId > -1 ? lastMeasure()->last() : nullptr; } - -void TstaffItem::shiftToMeasure(int measureNr, QList<TnotePair*>& notesToShift) { - TmeasureObject *m; - if (measureNr == m_scoreObj->measuresCount()) - m = m_scoreObj->addMeasure(); - else - m = m_scoreObj->measure(measureNr); - if (!notesToShift.isEmpty()) - m->insertNotes(notesToShift); +void TstaffItem::shiftToMeasure(int measureNr, QList<TnotePair *> ¬esToShift) +{ + TmeasureObject *m; + if (measureNr == m_scoreObj->measuresCount()) + m = m_scoreObj->addMeasure(); + else + m = m_scoreObj->measure(measureNr); + if (!notesToShift.isEmpty()) + m->insertNotes(notesToShift); } - -void TstaffItem::shiftFromMeasure(int measureNr, int dur, QList<TnotePair*>& notesToShift) { - if (measureNr < m_scoreObj->measuresCount()) - m_scoreObj->measure(measureNr)->releaseAtStart(dur, notesToShift); +void TstaffItem::shiftFromMeasure(int measureNr, int dur, QList<TnotePair *> ¬esToShift) +{ + if (measureNr < m_scoreObj->measuresCount()) + m_scoreObj->measure(measureNr)->releaseAtStart(dur, notesToShift); } +// ################################################################################################# +// ################### PRIVATE ############################################ +// ################################################################################################# -//################################################################################################# -//################### PRIVATE ############################################ -//################################################################################################# - -void TstaffItem::findHighestNote() { - m_hiNotePos = upperLine() - 4.0; - for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { - auto measure = m_scoreObj->measure(m); - for (int n = 0; n < measure->noteCount(); ++n) { - auto noteSeg = measure->note(n); - if (noteSeg->item()->notePosY()) // is visible - m_hiNotePos = qMin(qreal(noteSeg->item()->notePosY() - (noteSeg->note()->rtm.stemDown() ? 2.0 : 4.0)), m_hiNotePos); +void TstaffItem::findHighestNote() +{ + m_hiNotePos = upperLine() - 4.0; + for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { + auto measure = m_scoreObj->measure(m); + for (int n = 0; n < measure->noteCount(); ++n) { + auto noteSeg = measure->note(n); + if (noteSeg->item()->notePosY()) // is visible + m_hiNotePos = qMin(qreal(noteSeg->item()->notePosY() - (noteSeg->note()->rtm.stemDown() ? 2.0 : 4.0)), m_hiNotePos); + } } - } } - -void TstaffItem::findLowestNote() { - m_loNotePos = m_scordSpace + upperLine() + (m_scoreObj->isPianoStaff() ? 29.0 : 14.0); - for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { - auto measure = m_scoreObj->measure(m); - for (int n = 0; n < measure->noteCount(); ++n) { - auto noteSeg = measure->note(n); - m_loNotePos = qMax(qreal(noteSeg->item()->notePosY() + (noteSeg->note()->rtm.stemDown() ? 4 : 2)), m_loNotePos); +void TstaffItem::findLowestNote() +{ + m_loNotePos = m_scordSpace + upperLine() + (m_scoreObj->isPianoStaff() ? 29.0 : 14.0); + for (int m = m_firstMeasureId; m <= m_lastMeasureId; ++m) { + auto measure = m_scoreObj->measure(m); + for (int n = 0; n < measure->noteCount(); ++n) { + auto noteSeg = measure->note(n); + m_loNotePos = qMax(qreal(noteSeg->item()->notePosY() + (noteSeg->note()->rtm.stemDown() ? 4 : 2)), m_loNotePos); + } } - } } - -void TstaffItem::createExtraTie(TnoteItem* parent) { - if (parent->note()->rtm.tie() == Trhythm::e_tieCont || parent->note()->rtm.tie() == Trhythm::e_tieEnd) { - if (!m_extraTie) { - QQmlEngine engine; - QQmlComponent comp(&engine, this); - comp.setData("import QtQuick 2.9; Text { font { family: \"Scorek\"; pixelSize: 7 }}", QUrl()); - m_extraTie = qobject_cast<QQuickItem*>(comp.create()); - m_extraTie->setX(-2.446875); // 2.546875 tie glyph width - m_extraTie->setProperty("color", qApp->palette().text().color()); - } - m_extraTie->setParentItem(parent->head()); - m_extraTie->setProperty("text", parent->note()->rtm.stemDown() ? QStringLiteral("\ue204") : QStringLiteral("\ue1fd")); - m_extraTie->setY(parent->note()->rtm.stemDown() ? -1.0 : 0.0); - } else { - deleteExtraTie(); - } +void TstaffItem::createExtraTie(TnoteItem *parent) +{ + if (parent->note()->rtm.tie() == Trhythm::e_tieCont || parent->note()->rtm.tie() == Trhythm::e_tieEnd) { + if (!m_extraTie) { + QQmlEngine engine; + QQmlComponent comp(&engine, this); + comp.setData("import QtQuick 2.9; Text { font { family: \"Scorek\"; pixelSize: 7 }}", QUrl()); + m_extraTie = qobject_cast<QQuickItem *>(comp.create()); + m_extraTie->setX(-2.446875); // 2.546875 tie glyph width + m_extraTie->setProperty("color", qApp->palette().text().color()); + } + m_extraTie->setParentItem(parent->head()); + m_extraTie->setProperty("text", parent->note()->rtm.stemDown() ? QStringLiteral("\ue204") : QStringLiteral("\ue1fd")); + m_extraTie->setY(parent->note()->rtm.stemDown() ? -1.0 : 0.0); + } else { + deleteExtraTie(); + } } - - diff --git a/src/libs/core/score/tstaffitem.h b/src/libs/core/score/tstaffitem.h index d06899a2acddafb629b6d0ccaed5561ecafe5f24..5057761089b2d087aedd9de660abdc6ffebc75aa 100644 --- a/src/libs/core/score/tstaffitem.h +++ b/src/libs/core/score/tstaffitem.h @@ -19,11 +19,9 @@ #ifndef TSTAFFITEM_H #define TSTAFFITEM_H - #include "nootkacoreglobal.h" #include <QtQuick/qquickitem.h> - class QQuickItem; class QTimer; class TscoreObject; @@ -32,162 +30,168 @@ class TnoteItem; class TnotePair; class Tnote; - /** * @class TstaffItem is C++ logic of Staff.qml */ -class NOOTKACORE_EXPORT TstaffItem : public QQuickItem +class NOOTKACORE_EXPORT TstaffItem : public QQuickItem { + Q_OBJECT - Q_OBJECT + Q_PROPERTY(TscoreObject *scoreObject READ score WRITE setScore) + Q_PROPERTY(qreal upperLine READ upperLine WRITE setUpperLine NOTIFY upperLineChanged) + Q_PROPERTY(qreal notesIndent READ notesIndent WRITE setNotesIndent) + Q_PROPERTY(int firstMeasureNr READ firstMeasureNr NOTIFY firstMeasureNrChanged) + Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged) + Q_PROPERTY(qreal scordSpace READ scordSpace WRITE setScordSpace) - Q_PROPERTY(TscoreObject* scoreObject READ score WRITE setScore) - Q_PROPERTY(qreal upperLine READ upperLine WRITE setUpperLine NOTIFY upperLineChanged) - Q_PROPERTY(qreal notesIndent READ notesIndent WRITE setNotesIndent) - Q_PROPERTY(int firstMeasureNr READ firstMeasureNr NOTIFY firstMeasureNrChanged) - Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged) - Q_PROPERTY(qreal scordSpace READ scordSpace WRITE setScordSpace) - - friend class TscoreObject; - friend class TnotePair; - friend class TmeasureObject; + friend class TscoreObject; + friend class TnotePair; + friend class TmeasureObject; public: - explicit TstaffItem(QQuickItem* parent = nullptr); - ~TstaffItem() override; - - int number() const { return m_number; } - void setNumber(int nr) { m_number = nr; emit numberChanged(); } - - /** - * @p 0 - means no scordature (standard tuning) - * positive value determines additional space below first staff for scordature marks - */ - qreal scordSpace() const { return m_scordSpace; } - void setScordSpace(qreal hasScord); - - TscoreObject* score() { return m_scoreObj; } - void setScore(TscoreObject* s); - - /** - * Refreshes gap factor and note positions - */ - void refresh(); - - /** - * Y coordinate of upper staff line - */ - qreal upperLine() const { return m_upperLine; } - void setUpperLine(qreal upLine); - - /** - * X coordinate of first note (or width of clef + key sign. + meter) - */ - qreal notesIndent() const { return m_notesIndent; } - void setNotesIndent(qreal ni); - - int firstMeasureNr(); - - int measuresCount() { return m_lastMeasureId - m_firstMeasureId + 1; } - TmeasureObject* firstMeasure(); - TmeasureObject* lastMeasure(); - - TnotePair* firstNote(); - TnotePair* lastNote(); - - /** - * Multiplexer of rhythm gaps between notes. - * It changes to place all notes nicely over entire staff width - */ - qreal gapFactor() { return m_gapFactor; } - - /** - * Y position of lowest note on the staff - */ - qreal loNotePos() const { return m_loNotePos; } - - /** - * Y position of highest note on the staff - */ - qreal hiNotePos() const { return m_hiNotePos; } - - /** - * Minimal height of the staff to display all its notes. - */ - qreal minHeight() const { return m_loNotePos - (number() == 0 ? 0.0 : m_hiNotePos); } - - /** - * Width of all notes on the staff - */ - qreal allNotesWidth() const { return m_allNotesWidth; } - - qreal gapsSum() const { return m_gapsSum; } - - bool isPianoStaff() const; - - /** - * Prints to std out debug info about this staff: [nr STAFF] in color - */ - char debug(); + explicit TstaffItem(QQuickItem *parent = nullptr); + ~TstaffItem() override; + + int number() const { return m_number; } + void setNumber(int nr) + { + m_number = nr; + emit numberChanged(); + } + + /** + * @p 0 - means no scordature (standard tuning) + * positive value determines additional space below first staff for scordature marks + */ + qreal scordSpace() const { return m_scordSpace; } + void setScordSpace(qreal hasScord); + + TscoreObject *score() { return m_scoreObj; } + void setScore(TscoreObject *s); + + /** + * Refreshes gap factor and note positions + */ + void refresh(); + + /** + * Y coordinate of upper staff line + */ + qreal upperLine() const { return m_upperLine; } + void setUpperLine(qreal upLine); + + /** + * X coordinate of first note (or width of clef + key sign. + meter) + */ + qreal notesIndent() const { return m_notesIndent; } + void setNotesIndent(qreal ni); + + int firstMeasureNr(); + + int measuresCount() { return m_lastMeasureId - m_firstMeasureId + 1; } + TmeasureObject *firstMeasure(); + TmeasureObject *lastMeasure(); + + TnotePair *firstNote(); + TnotePair *lastNote(); + + /** + * Multiplexer of rhythm gaps between notes. + * It changes to place all notes nicely over entire staff width + */ + qreal gapFactor() { return m_gapFactor; } + + /** + * Y position of lowest note on the staff + */ + qreal loNotePos() const { return m_loNotePos; } + + /** + * Y position of highest note on the staff + */ + qreal hiNotePos() const { return m_hiNotePos; } + + /** + * Minimal height of the staff to display all its notes. + */ + qreal minHeight() const { return m_loNotePos - (number() == 0 ? 0.0 : m_hiNotePos); } + + /** + * Width of all notes on the staff + */ + qreal allNotesWidth() const { return m_allNotesWidth; } + + qreal gapsSum() const { return m_gapsSum; } + + bool isPianoStaff() const; + + /** + * Prints to std out debug info about this staff: [nr STAFF] in color + */ + char debug(); signals: - void upperLineChanged(); - void firstMeasureNrChanged(); - void numberChanged(); - void loNotePosChanged(int staffNr, qreal offset); - void hiNotePosChanged(int staffNr, qreal offset); + void upperLineChanged(); + void firstMeasureNrChanged(); + void numberChanged(); + void loNotePosChanged(int staffNr, qreal offset); + void hiNotePosChanged(int staffNr, qreal offset); protected: - void fit(); - void updateNotesPos(int startMeasure = 0); - - int firstMeasureId() const { return m_firstMeasureId; } - void setFirstMeasureId(int firstId) { m_firstMeasureId = firstId; emit firstMeasureNrChanged(); } - int lastMeasureId() const { return m_lastMeasureId; } - void setLastMeasureId(int lastId) { m_lastMeasureId = lastId; } - - /** - * Checks positions of all notes to find lowest and highest. - * @p doEmit determines whether this method sends appropriate signals - */ - void checkNotesRange(bool doEmit = true); - - void appendMeasure(TmeasureObject* m); - - /** - * Inserts given @p m measure at @p index position - * which is in current staff measures numerology - */ - void insertMeasure(int index, TmeasureObject* m); - - /** - * Staff manages extra tie text when tie is broken among staves. - * It creates QML Text item and adds it to first note head in the staff. - * Whenever first note changes (measures are shifting), it check is that extra tie necessary - */ - void createExtraTie(TnoteItem* parent); - void deleteExtraTie(); - - void shiftToMeasure(int measureNr, QList<TnotePair*>& notesToShift); - - void shiftFromMeasure(int measureNr, int dur, QList<TnotePair*>& notesToShift); + void fit(); + void updateNotesPos(int startMeasure = 0); + + int firstMeasureId() const { return m_firstMeasureId; } + void setFirstMeasureId(int firstId) + { + m_firstMeasureId = firstId; + emit firstMeasureNrChanged(); + } + int lastMeasureId() const { return m_lastMeasureId; } + void setLastMeasureId(int lastId) { m_lastMeasureId = lastId; } + + /** + * Checks positions of all notes to find lowest and highest. + * @p doEmit determines whether this method sends appropriate signals + */ + void checkNotesRange(bool doEmit = true); + + void appendMeasure(TmeasureObject *m); + + /** + * Inserts given @p m measure at @p index position + * which is in current staff measures numerology + */ + void insertMeasure(int index, TmeasureObject *m); + + /** + * Staff manages extra tie text when tie is broken among staves. + * It creates QML Text item and adds it to first note head in the staff. + * Whenever first note changes (measures are shifting), it check is that extra tie necessary + */ + void createExtraTie(TnoteItem *parent); + void deleteExtraTie(); + + void shiftToMeasure(int measureNr, QList<TnotePair *> ¬esToShift); + + void shiftFromMeasure(int measureNr, int dur, QList<TnotePair *> ¬esToShift); private: - void findLowestNote(); /**< Checks all Y positions of staff notes to find lowest one */ - void findHighestNote(); /**< Checks all Y positions of staff notes to find highest one */ + void findLowestNote(); /**< Checks all Y positions of staff notes to find lowest one */ + void findHighestNote(); /**< Checks all Y positions of staff notes to find highest one */ private: - TscoreObject *m_scoreObj; - qreal m_upperLine; - qreal m_notesIndent; - qreal m_gapFactor; - qreal m_loNotePos, m_hiNotePos; - int m_number; - int m_firstMeasureId, m_lastMeasureId; - qreal m_allNotesWidth = 0.0; - qreal m_gapsSum = 0.0; - QQuickItem *m_extraTie = nullptr; /**< Tie at the staff beginning */ - qreal m_scordSpace = 0.0; + TscoreObject *m_scoreObj; + qreal m_upperLine; + qreal m_notesIndent; + qreal m_gapFactor; + qreal m_loNotePos, m_hiNotePos; + int m_number; + int m_firstMeasureId, m_lastMeasureId; + qreal m_allNotesWidth = 0.0; + qreal m_gapsSum = 0.0; + QQuickItem *m_extraTie = nullptr; /**< Tie at the staff beginning */ + qreal m_scordSpace = 0.0; }; #endif // TSTAFFITEM_H diff --git a/src/libs/core/score/tstafflines.cpp b/src/libs/core/score/tstafflines.cpp index c7d0146c2b3514d5197902d72b0ee59986312fe5..b05baba611f0fdb8995d2ced82ff7b4dd3e886a6 100644 --- a/src/libs/core/score/tstafflines.cpp +++ b/src/libs/core/score/tstafflines.cpp @@ -18,56 +18,54 @@ #include "tstafflines.h" -#include <QtGui/qpalette.h> -#include <QtGui/qpainter.h> #include <QtGui/qguiapplication.h> - +#include <QtGui/qpainter.h> +#include <QtGui/qpalette.h> #define LINE_WIDTH (0.2) - /** * There is fixed height set to 9.0 whether actual staff height is about 8.2 * To able to paint line with its complete width we need to set that staff height bigger */ -TstaffLines::TstaffLines(QQuickItem* parent) : - QQuickPaintedItem(parent) +TstaffLines::TstaffLines(QQuickItem *parent) + : QQuickPaintedItem(parent) { - setAcceptHoverEvents(true); - setRenderTarget(QQuickPaintedItem::FramebufferObject); - setAntialiasing(true); - setHeight(9.0); - connect(qApp, &QGuiApplication::paletteChanged, this, [=]{ update(); }); + setAcceptHoverEvents(true); + setRenderTarget(QQuickPaintedItem::FramebufferObject); + setAntialiasing(true); + setHeight(9.0); + connect(qApp, &QGuiApplication::paletteChanged, this, [=] { + update(); + }); } - -void TstaffLines::setStaffScale(qreal stScale) { - if (stScale != m_staffScale) { - m_staffScale = stScale; - setTextureSize(QSize(qRound(m_staffScale * width()), qRound(height() * m_staffScale))); - update(); - } +void TstaffLines::setStaffScale(qreal stScale) +{ + if (stScale != m_staffScale) { + m_staffScale = stScale; + setTextureSize(QSize(qRound(m_staffScale * width()), qRound(height() * m_staffScale))); + update(); + } } - -void TstaffLines::paint(QPainter* painter) { - painter->setPen(QPen(qApp->palette().color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Text), LINE_WIDTH)); - for (int l = 0; l < 5; ++l) { - qreal yy = l * 2.0 + LINE_WIDTH / 2.0; - painter->drawLine(QPointF(0.0, yy), QPointF(width(), yy)); - } +void TstaffLines::paint(QPainter *painter) +{ + painter->setPen(QPen(qApp->palette().color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Text), LINE_WIDTH)); + for (int l = 0; l < 5; ++l) { + qreal yy = l * 2.0 + LINE_WIDTH / 2.0; + painter->drawLine(QPointF(0.0, yy), QPointF(width(), yy)); + } } +// ################################################################################################# +// ################### PROTECTED ############################################ +// ################################################################################################# -//################################################################################################# -//################### PROTECTED ############################################ -//################################################################################################# - -void TstaffLines::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) { - if (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height()) { - setTextureSize(QSize(qRound(m_staffScale * newGeometry.width()), qRound(newGeometry.height() * m_staffScale))); - update(); - } +void TstaffLines::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height()) { + setTextureSize(QSize(qRound(m_staffScale * newGeometry.width()), qRound(newGeometry.height() * m_staffScale))); + update(); + } } - - diff --git a/src/libs/core/score/tstafflines.h b/src/libs/core/score/tstafflines.h index b5ac6c8979f7fe9ed349b1dbc1f82a6dbd926ed7..b39b79076a614715ee9312b5959f685c52c47c67 100644 --- a/src/libs/core/score/tstafflines.h +++ b/src/libs/core/score/tstafflines.h @@ -19,34 +19,31 @@ #ifndef TSTAFFLINES_H #define TSTAFFLINES_H - #include "nootkacoreglobal.h" #include <QtQuick/qquickpainteditem.h> - /** * Paints five staff lines */ class NOOTKACORE_EXPORT TstaffLines : public QQuickPaintedItem { + Q_OBJECT - Q_OBJECT - - Q_PROPERTY(qreal staffScale READ staffScale WRITE setStaffScale) + Q_PROPERTY(qreal staffScale READ staffScale WRITE setStaffScale) public: - explicit TstaffLines(QQuickItem* parent = nullptr); + explicit TstaffLines(QQuickItem *parent = nullptr); - qreal staffScale() { return m_staffScale; } - void setStaffScale(qreal stScale); + qreal staffScale() { return m_staffScale; } + void setStaffScale(qreal stScale); - void paint(QPainter* painter) override; + void paint(QPainter *painter) override; protected: - void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; private: - qreal m_staffScale = 1.0; + qreal m_staffScale = 1.0; }; #endif // TSTAFFLINES_H diff --git a/src/libs/core/taction.cpp b/src/libs/core/taction.cpp index 2266e65afe1bbe9458c8f65fdcbdd6ff3be0a085..2036ace946fe5159b9dd882a88eeaa5d5408fdb9 100644 --- a/src/libs/core/taction.cpp +++ b/src/libs/core/taction.cpp @@ -19,134 +19,133 @@ #include "taction.h" #include "tpath.h" -#include <QtQml/qqmlcomponent.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> +#include <QtQml/qqmlcomponent.h> #include <QtCore/qdebug.h> - -Taction::Taction(QObject* parent) : - QObject(parent) +Taction::Taction(QObject *parent) + : QObject(parent) { - m_bgColor = qApp->palette().base().color(); - m_bgColor.setAlpha(0); + m_bgColor = qApp->palette().base().color(); + m_bgColor.setAlpha(0); } - -Taction::Taction(const QString& txt, const QString& ico, QObject* parent, bool isEnabled) : - QObject(parent), - m_enabled(isEnabled), - m_iconTag(ico), - m_text(txt) +Taction::Taction(const QString &txt, const QString &ico, QObject *parent, bool isEnabled) + : QObject(parent) + , m_enabled(isEnabled) + , m_iconTag(ico) + , m_text(txt) { - m_bgColor = qApp->palette().base().color(); - m_bgColor.setAlpha(0); + m_bgColor = qApp->palette().base().color(); + m_bgColor.setAlpha(0); } - -Taction::~Taction() {} - - -QString Taction::icon() const { return m_iconTag.isEmpty() ? QString() : Tpath::pix(m_iconTag); } - - -void Taction::setIconTag(const QString& ic) { - if (ic != m_iconTag) { - m_iconTag = ic; - emit iconChanged(); - } +Taction::~Taction() +{ } - -void Taction::setText(const QString& t) { - if (t != m_text) { - m_text = t; - emit textChanged(); - } +QString Taction::icon() const +{ + return m_iconTag.isEmpty() ? QString() : Tpath::pix(m_iconTag); } - -void Taction::setTip(const QString& t, int pos) { - if (t != m_tip) { - m_tip = t; - m_tipPos = static_cast<quint8>(pos); - emit tipChanged(); - } +void Taction::setIconTag(const QString &ic) +{ + if (ic != m_iconTag) { + m_iconTag = ic; + emit iconChanged(); + } } - -void Taction::trigger() { - if (m_enabled) - emit triggered(); +void Taction::setText(const QString &t) +{ + if (t != m_text) { + m_text = t; + emit textChanged(); + } } - -void Taction::setCheckable(bool ch) { - if (ch != m_checkable) { - m_checkable = ch; - emit checkableChanged(); - } +void Taction::setTip(const QString &t, int pos) +{ + if (t != m_tip) { + m_tip = t; + m_tipPos = static_cast<quint8>(pos); + emit tipChanged(); + } } - -void Taction::setChecked(bool ch) { - if (ch != m_checked) { - m_checked = ch; - emit checkedChanged(); - } +void Taction::trigger() +{ + if (m_enabled) + emit triggered(); } - -void Taction::setShortcut(QObject* s) { - m_shortcut = s; - if (m_shortcut) { - connect(m_shortcut, SIGNAL(activated()), this, SLOT(triggerSlot())); - m_shortcut->setProperty("enabled", m_enabled); - } +void Taction::setCheckable(bool ch) +{ + if (ch != m_checkable) { + m_checkable = ch; + emit checkableChanged(); + } } - -void Taction::setEnabled(bool e) { - if (e != m_enabled) { - m_enabled = e; - if (m_shortcut) - m_shortcut->setProperty("enabled", m_enabled); - emit enabledChanged(); - } +void Taction::setChecked(bool ch) +{ + if (ch != m_checked) { + m_checked = ch; + emit checkedChanged(); + } } - -void Taction::setBgColor(const QColor& bgC) { - if (bgC != m_bgColor) { - m_bgColor = bgC; - emit bgColorChanged(); - } +void Taction::setShortcut(QObject *s) +{ + m_shortcut = s; + if (m_shortcut) { + connect(m_shortcut, SIGNAL(activated()), this, SLOT(triggerSlot())); + m_shortcut->setProperty("enabled", m_enabled); + } } - - -QString Taction::key() const { - return m_shortcut ? m_shortcut->property("nativeText").toString() : QString(); +void Taction::setEnabled(bool e) +{ + if (e != m_enabled) { + m_enabled = e; + if (m_shortcut) + m_shortcut->setProperty("enabled", m_enabled); + emit enabledChanged(); + } } +void Taction::setBgColor(const QColor &bgC) +{ + if (bgC != m_bgColor) { + m_bgColor = bgC; + emit bgColorChanged(); + } +} -void Taction::createQmlShortcut(QQmlComponent* qmlComp, const char* keySequence) { - if (m_shortcut) { - qDebug() << "[Taction] name:" << m_text << "has shortcut already! Ignored!"; - return; - } - if (keySequence) { // for empty key sequence, QML component data will be used - std::string d = "import QtQuick 2.9; Shortcut { sequence: "; - d.append(keySequence); - d.append(" }"); - qmlComp->setData(d.c_str(), QUrl()); - } - auto sc = qmlComp->create(qmlContext(parent())); - if (sc) { - sc->setParent(this); - setShortcut(sc); - } else - qDebug() << "[Taction] Can't create shortcut for" << keySequence; +QString Taction::key() const +{ + return m_shortcut ? m_shortcut->property("nativeText").toString() : QString(); } +void Taction::createQmlShortcut(QQmlComponent *qmlComp, const char *keySequence) +{ + if (m_shortcut) { + qDebug() << "[Taction] name:" << m_text << "has shortcut already! Ignored!"; + return; + } + if (keySequence) { // for empty key sequence, QML component data will be used + std::string d = "import QtQuick 2.9; Shortcut { sequence: "; + d.append(keySequence); + d.append(" }"); + qmlComp->setData(d.c_str(), QUrl()); + } + auto sc = qmlComp->create(qmlContext(parent())); + if (sc) { + sc->setParent(this); + setShortcut(sc); + } else + qDebug() << "[Taction] Can't create shortcut for" << keySequence; +} diff --git a/src/libs/core/taction.h b/src/libs/core/taction.h index 5d0784540285f9a25731bfc8d1aec5fa21e9c138..96ecb74fb3fbaa9422fc79feedf08bdd497fb035 100644 --- a/src/libs/core/taction.h +++ b/src/libs/core/taction.h @@ -19,15 +19,12 @@ #ifndef TACTION_H #define TACTION_H - -#include <nootkacoreglobal.h> #include <QtCore/qobject.h> #include <QtGui/qcolor.h> - +#include <nootkacoreglobal.h> class QQmlComponent; - /** * Describes action (icon, text, checked). * It is used to share actions between Nootka tool bar under desktop @@ -37,97 +34,96 @@ class QQmlComponent; */ class NOOTKACORE_EXPORT Taction : public QObject { - - Q_OBJECT - - Q_PROPERTY(QString icon READ icon WRITE setIconTag NOTIFY iconChanged) - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) - Q_PROPERTY(QString tip READ tip WRITE setTip NOTIFY tipChanged) - Q_PROPERTY(int tipPos READ tipPos NOTIFY tipChanged) - Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY checkedChanged) - Q_PROPERTY(bool checkable READ checkable WRITE setCheckable NOTIFY checkableChanged) - Q_PROPERTY(QObject* shortcut READ shortcut WRITE setShortcut) - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor NOTIFY bgColorChanged) + Q_OBJECT + + Q_PROPERTY(QString icon READ icon WRITE setIconTag NOTIFY iconChanged) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QString tip READ tip WRITE setTip NOTIFY tipChanged) + Q_PROPERTY(int tipPos READ tipPos NOTIFY tipChanged) + Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY checkedChanged) + Q_PROPERTY(bool checkable READ checkable WRITE setCheckable NOTIFY checkableChanged) + Q_PROPERTY(QObject *shortcut READ shortcut WRITE setShortcut) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor NOTIFY bgColorChanged) public: - explicit Taction(QObject* parent = nullptr); - explicit Taction(const QString& txt, const QString& ico, QObject* parent = nullptr, bool isEnabled = true); - ~Taction() override; + explicit Taction(QObject *parent = nullptr); + explicit Taction(const QString &txt, const QString &ico, QObject *parent = nullptr, bool isEnabled = true); + ~Taction() override; - QString icon() const; - void setIconTag(const QString& ic); + QString icon() const; + void setIconTag(const QString &ic); - QString text() const { return m_text; } - void setText(const QString& t); + QString text() const { return m_text; } + void setText(const QString &t); - QString tip() const { return m_tip; } - void setTip(const QString& t, int pos = 1); + QString tip() const { return m_tip; } + void setTip(const QString &t, int pos = 1); - int tipPos() const { return m_tipPos; } + int tipPos() const { return m_tipPos; } - bool checkable() const { return m_checkable; } - void setCheckable(bool ch); + bool checkable() const { return m_checkable; } + void setCheckable(bool ch); - bool checked() const { return m_checked; } - void setChecked(bool ch); + bool checked() const { return m_checked; } + void setChecked(bool ch); - QObject* shortcut() { return m_shortcut; } - void setShortcut(QObject* s); + QObject *shortcut() { return m_shortcut; } + void setShortcut(QObject *s); - bool enabled() const { return m_enabled; } - void setEnabled(bool e); + bool enabled() const { return m_enabled; } + void setEnabled(bool e); - /** - * Color used to highlight an item associated with this action - */ - QColor bgColor() const { return m_bgColor; } - void setBgColor(const QColor& bgC); + /** + * Color used to highlight an item associated with this action + */ + QColor bgColor() const { return m_bgColor; } + void setBgColor(const QColor &bgC); - /** - * Emits @p shakeButton() signal - * - to make corresponding tool button shaking to attract user attention - * - or to display flying button under Android - */ - void shake() { emit shakeButton(); } + /** + * Emits @p shakeButton() signal + * - to make corresponding tool button shaking to attract user attention + * - or to display flying button under Android + */ + void shake() { emit shakeButton(); } - Q_INVOKABLE void trigger(); - Q_INVOKABLE QString key() const; + Q_INVOKABLE void trigger(); + Q_INVOKABLE QString key() const; - /** - * Creates QML Shortcut object (if there is not already) - * with given @p keySequence. It could be either simple key text: \"M\" (wrapped with quotas) - * or standard key: StandardKey.Open (no quotas) - * or any of above followed by other parameters: \"E\"; enabled: !score.singleNote. - * In such a case this @p Taction parent has to belong to proper context where those params are available. - * But if this parameter is null, @p qmlComp data is used directly. - */ - void createQmlShortcut(QQmlComponent* qmlComp, const char* keySequence); + /** + * Creates QML Shortcut object (if there is not already) + * with given @p keySequence. It could be either simple key text: \"M\" (wrapped with quotas) + * or standard key: StandardKey.Open (no quotas) + * or any of above followed by other parameters: \"E\"; enabled: !score.singleNote. + * In such a case this @p Taction parent has to belong to proper context where those params are available. + * But if this parameter is null, @p qmlComp data is used directly. + */ + void createQmlShortcut(QQmlComponent *qmlComp, const char *keySequence); public slots: - void triggerSlot() { trigger(); } + void triggerSlot() { trigger(); } signals: - void iconChanged(); - void textChanged(); - void tipChanged(); - void triggered(); - void checkedChanged(); - void checkableChanged(); - void enabledChanged(); - void bgColorChanged(); - void shakeButton(); + void iconChanged(); + void textChanged(); + void tipChanged(); + void triggered(); + void checkedChanged(); + void checkableChanged(); + void enabledChanged(); + void bgColorChanged(); + void shakeButton(); private: - bool m_checkable = false; - bool m_checked = false; - bool m_enabled = true; - QString m_iconTag; - QString m_text; - QString m_tip; - QObject *m_shortcut = nullptr; - quint8 m_tipPos = 1; /**< It corresponds with @p QQUickItem::TransformOrigin, 1 means Top center */ - QColor m_bgColor; + bool m_checkable = false; + bool m_checked = false; + bool m_enabled = true; + QString m_iconTag; + QString m_text; + QString m_tip; + QObject *m_shortcut = nullptr; + quint8 m_tipPos = 1; /**< It corresponds with @p QQUickItem::TransformOrigin, 1 means Top center */ + QColor m_bgColor; }; #endif // TACTION_H diff --git a/src/libs/core/taudioparams.h b/src/libs/core/taudioparams.h index ae9268be9b4e07938a9f17220d1ebf081d13fc7a..ad319919818ca30187bed605479827d4234dde61 100644 --- a/src/libs/core/taudioparams.h +++ b/src/libs/core/taudioparams.h @@ -16,52 +16,47 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TAUDIOPARAMS_H #define TAUDIOPARAMS_H -#include <nootkacoreglobal.h> #include <QtCore/qstring.h> - +#include <nootkacoreglobal.h> /** * Class describes audio input and output parameters. */ class NOOTKACORE_EXPORT TaudioParams { - public: - - bool JACKorASIO; /**< forces to use JACK under Linux or ASIO under Windows (if they are available) */ - - bool INenabled; /**< is audio input enabled */ - QString INdevName; /**< input device name */ - qreal a440diff; /**< difference between standard a1 440Hz and user preferred base pitch */ - int midAfreq = 440; /**< Orchestral pitch - frequency of middle A */ - int transposition; /**< shift (interval) between score/note name and sound/instrument */ - qreal minimalVol; /**< only detected notes louder than this value are sending to Nootka */ - qreal minDuration; /**< minimal duration of a sound above which it is detected */ - int detectMethod; /**< pitch detection method (0 - MPM, 1 - auto-correlation, 2 - MPM modified cepstrum) */ - quint8 intonation; /**< accuracy of intonation in detected note - corresponds with @p Eaccuracy */ - bool stoppedByUser; /**< Stores user action when he stopped sniffing himself @default FALSE */ - bool audibleMetro; /**< Audible metronome ticking */ - bool countBefore; /**< Before playing or sniffing play a measure of ticking */ - int quantization; /**< Can be 6 or 12 (rhythmic duration of 16th or 8th), 4 (16th triplet is not yet supported) */ - - bool equalLoudness; /**< if TRUE - noise filters are performed - FALSE by default */ -// duplex mode - bool forwardInput; /**< if true, all captured audio data is pushed to output */ - -// audio output settings - bool OUTenabled; /**< audio output enabled */ - QString OUTdevName; /**< output device name */ - bool midiEnabled; /**< default false */ - QString midiPortName; /**< default empty to find system default */ - unsigned char midiInstrNr; /**< default 0 - grand piano */ - int audioInstrNr; /**< corresponds with Einstrument enum numbers, default 1 classical guitar */ - - QString dumpPath; /**< Path when captured PCM audio data will be dumped, if empty (default) - no dump performed */ + bool JACKorASIO; /**< forces to use JACK under Linux or ASIO under Windows (if they are available) */ + + bool INenabled; /**< is audio input enabled */ + QString INdevName; /**< input device name */ + qreal a440diff; /**< difference between standard a1 440Hz and user preferred base pitch */ + int midAfreq = 440; /**< Orchestral pitch - frequency of middle A */ + int transposition; /**< shift (interval) between score/note name and sound/instrument */ + qreal minimalVol; /**< only detected notes louder than this value are sending to Nootka */ + qreal minDuration; /**< minimal duration of a sound above which it is detected */ + int detectMethod; /**< pitch detection method (0 - MPM, 1 - auto-correlation, 2 - MPM modified cepstrum) */ + quint8 intonation; /**< accuracy of intonation in detected note - corresponds with @p Eaccuracy */ + bool stoppedByUser; /**< Stores user action when he stopped sniffing himself @default FALSE */ + bool audibleMetro; /**< Audible metronome ticking */ + bool countBefore; /**< Before playing or sniffing play a measure of ticking */ + int quantization; /**< Can be 6 or 12 (rhythmic duration of 16th or 8th), 4 (16th triplet is not yet supported) */ + + bool equalLoudness; /**< if TRUE - noise filters are performed - FALSE by default */ + // duplex mode + bool forwardInput; /**< if true, all captured audio data is pushed to output */ + + // audio output settings + bool OUTenabled; /**< audio output enabled */ + QString OUTdevName; /**< output device name */ + bool midiEnabled; /**< default false */ + QString midiPortName; /**< default empty to find system default */ + unsigned char midiInstrNr; /**< default 0 - grand piano */ + int audioInstrNr; /**< corresponds with Einstrument enum numbers, default 1 classical guitar */ + + QString dumpPath; /**< Path when captured PCM audio data will be dumped, if empty (default) - no dump performed */ }; - #endif // TAUDIOPARAMS_H diff --git a/src/libs/core/tcolor.cpp b/src/libs/core/tcolor.cpp index 77201e29bb3464d14a2488ce3febcbf9c81fb817..5d6923cce4481c6c412cf5e368f9e1d9292bceb9 100644 --- a/src/libs/core/tcolor.cpp +++ b/src/libs/core/tcolor.cpp @@ -19,11 +19,10 @@ #include "tcolor.h" #include <QtGui/qpalette.h> - QColor Tcolor::shadow = Qt::gray; - -void Tcolor::setShadow(const QPalette& pal) { +void Tcolor::setShadow(const QPalette &pal) +{ #if defined(Q_OS_MAC) QColor shadowC(pal.text().color()); shadowC.setAlpha(50); @@ -33,35 +32,32 @@ void Tcolor::setShadow(const QPalette& pal) { #endif } - -QColor Tcolor::merge(const QColor& C1, const QColor& C2) { - qreal al = iV(C1.alpha()) + iV(C2.alpha() * (1 - iV(C1.alpha()))); - return QColor(((iV(C1.red()) * iV(C1.alpha()) + iV(C2.red()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, - ((iV(C1.green()) * iV(C1.alpha()) + iV(C2.green()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, - ((iV(C1.blue()) * iV(C1.alpha()) + iV(C2.blue()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, - qMin(255, (int)(255 * al))); +QColor Tcolor::merge(const QColor &C1, const QColor &C2) +{ + qreal al = iV(C1.alpha()) + iV(C2.alpha() * (1 - iV(C1.alpha()))); + return QColor(((iV(C1.red()) * iV(C1.alpha()) + iV(C2.red()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, + ((iV(C1.green()) * iV(C1.alpha()) + iV(C2.green()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, + ((iV(C1.blue()) * iV(C1.alpha()) + iV(C2.blue()) * iV(C2.alpha()) * (1 - iV(C1.alpha()))) / al) * 255, + qMin(255, (int)(255 * al))); } - -QColor Tcolor::invert(const QColor& color) { - QColor C = color; - if (C.isValid()) - C.setRgb(qRgb(255 - C.red(), 255 - C.green(), 255 - C.blue())); - return C; +QColor Tcolor::invert(const QColor &color) +{ + QColor C = color; + if (C.isValid()) + C.setRgb(qRgb(255 - C.red(), 255 - C.green(), 255 - C.blue())); + return C; } - -QString Tcolor::rgbaText(const QColor& color, const QString& styleTag) { - return QString(styleTag + "rgba(%1, %2, %3, %4);").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha()); +QString Tcolor::rgbaText(const QColor &color, const QString &styleTag) +{ + return QString(styleTag + "rgba(%1, %2, %3, %4);").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha()); } - -QString Tcolor::bgTag(const QColor& color) { - if (color != -1 && color.alpha() > 0) - return rgbaText(color, "background-color:"); - else - return QString("background-color: transparent; "); +QString Tcolor::bgTag(const QColor &color) +{ + if (color != -1 && color.alpha() > 0) + return rgbaText(color, "background-color:"); + else + return QString("background-color: transparent; "); } - - - diff --git a/src/libs/core/tcolor.h b/src/libs/core/tcolor.h index 71e1a4984a99b65970ac3c359a1add756ac13693..ca7e72ed42a33ca30ce4dd31c350e4b6b76aaf47 100644 --- a/src/libs/core/tcolor.h +++ b/src/libs/core/tcolor.h @@ -19,14 +19,11 @@ #ifndef TCOLOR_H #define TCOLOR_H - -#include <nootkacoreglobal.h> #include <QtGui/qcolor.h> - +#include <nootkacoreglobal.h> class QPalette; - /** * A set of static methods with some tweaks of color: * - color inversion by invert() @@ -36,47 +33,42 @@ class QPalette; */ class NOOTKACORE_EXPORT Tcolor { - public: - - /** - * Merge given colors. - */ - static QColor merge(const QColor& C1, const QColor& C2); - - /** - * Returns inverted copy of given color - */ - static QColor invert(const QColor& color); - - /** - * Returns css style tag with rgba values of color. - * @p styleTag rgba(red, green, blue, alpha); - */ - static QString rgbaText(const QColor& color, const QString& styleTag = QString()); - - /** - * Returns - * background-color: rgba(red, green, blue, alpha) - */ - static QString bgTag(const QColor& color); - - /** - * Converts value (0 - 255) to (0.0 - 1.0) - */ - static qreal iV(int ch) { return ch / 255.0; } - - static QColor shadow; - - /** - * Sets default shadow color of tips - */ - static void setShadow(const QPalette& pal); - - static QColor alpha(const QColor& c, int a) { return QColor(c.red(), c.green(), c.blue(), a); } - + /** + * Merge given colors. + */ + static QColor merge(const QColor &C1, const QColor &C2); + + /** + * Returns inverted copy of given color + */ + static QColor invert(const QColor &color); + + /** + * Returns css style tag with rgba values of color. + * @p styleTag rgba(red, green, blue, alpha); + */ + static QString rgbaText(const QColor &color, const QString &styleTag = QString()); + + /** + * Returns + * background-color: rgba(red, green, blue, alpha) + */ + static QString bgTag(const QColor &color); + + /** + * Converts value (0 - 255) to (0.0 - 1.0) + */ + static qreal iV(int ch) { return ch / 255.0; } + + static QColor shadow; + + /** + * Sets default shadow color of tips + */ + static void setShadow(const QPalette &pal); + + static QColor alpha(const QColor &c, int a) { return QColor(c.red(), c.green(), c.blue(), a); } }; - - #endif // TCOLOR_H diff --git a/src/libs/core/texamparams.h b/src/libs/core/texamparams.h index 5247b6f13e36c98e67d2573f4ae38443360356bb..87b93cb5c27a7e7b438b90f93412bb158508efb0 100644 --- a/src/libs/core/texamparams.h +++ b/src/libs/core/texamparams.h @@ -16,57 +16,53 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TEXAMPARAMS_H #define TEXAMPARAMS_H -#include <nootkacoreglobal.h> -#include <QString> #include <QColor> - +#include <QString> +#include <nootkacoreglobal.h> #define RECENT_EXAMS_LIMIT (15) // max number of recent exams to remember - /** class describes en exam parameters changeable by user. */ +/** class describes en exam parameters changeable by user. */ class NOOTKACORE_EXPORT TexamParams { -// They are very global and Tglobals has them directly -// QColor questionColor; -// QColor answerColor; + // They are very global and Tglobals has them directly + // QColor questionColor; + // QColor answerColor; public: + /** Actions after user mistake when next question is asked automatically. */ + enum EafterMistake { + e_continue = 0, /** next question is asked immediately */ + e_wait = 1, /** next question is asked after delay defined in @p previewDuration */ + e_stop = 2 /** questioning is stopped, to get next one user has to click */ + }; - /** Actions after user mistake when next question is asked automatically. */ - enum EafterMistake { - e_continue = 0, /** next question is asked immediately */ - e_wait = 1, /** next question is asked after delay defined in @p previewDuration */ - e_stop = 2 /** questioning is stopped, to get next one user has to click */ - }; + bool autoNextQuest; /**< Next question in en exam is given automatically after correct answer @def false */ + bool repeatIncorrect; /**< If EautoNextQuest is true incorrect questions are asked again once @def true */ + EafterMistake afterMistake; /**< Actions after user mistake when next question is set to ask automatically. @def e_continue */ + bool expertsAnswerEnable; /**< Enable checking without confirm @def false */ + QString studentName; /**< @def empty */ + QString examsDir; /**< Path to dir with recent opened exam file. @def system_home_path */ + QString levelsDir; /**< Path to dir with recent opened level file. @def system_home_path */ + bool closeWithoutConfirm; /**< Do not ask anything when Nootka is closing. @def false */ + bool showNameOfAnswered; /**< Displays note name of answered (if possible). @def false */ - bool autoNextQuest; /**< Next question in en exam is given automatically after correct answer @def false */ - bool repeatIncorrect; /**< If EautoNextQuest is true incorrect questions are asked again once @def true */ - EafterMistake afterMistake; /**< Actions after user mistake when next question is set to ask automatically. @def e_continue */ - bool expertsAnswerEnable; /**< Enable checking without confirm @def false */ - QString studentName; /**< @def empty */ - QString examsDir; /**< Path to dir with recent opened exam file. @def system_home_path */ - QString levelsDir; /**< Path to dir with recent opened level file. @def system_home_path */ - bool closeWithoutConfirm; /**< Do not ask anything when Nootka is closing. @def false */ - bool showNameOfAnswered; /**< Displays note name of answered (if possible). @def false */ + // PRACTICE/EXERCISING + bool showCorrected; /**< When answer was wrong, corrected one will be shown */ + bool suggestExam; /**< Check is exercising going well and suggest to start an exam on that level. @def true */ + bool showWrongPlayed; /**< Displays what pitch Nootka detected when played answer was wrong. @def false */ + bool waitForCorrect; /**< When exercise melody is played it locks current note index until correct note is played. @def true */ -// PRACTICE/EXERCISING - bool showCorrected; /**< When answer was wrong, corrected one will be shown */ - bool suggestExam; /**< Check is exercising going well and suggest to start an exam on that level. @def true */ - bool showWrongPlayed; /**< Displays what pitch Nootka detected when played answer was wrong. @def false */ - bool waitForCorrect; /**< When exercise melody is played it locks current note index until correct note is played. @def true */ + int mistakePreview; /**< How long mistakes are exposed to ask auto next question. @def 3000ms */ + int questionDelay; /**< Wait time before every next question. */ + int correctPreview; /**< Preview of answer correction. */ - int mistakePreview; /**< How long mistakes are exposed to ask auto next question. @def 3000ms */ - int questionDelay; /**< Wait time before every next question. */ - int correctPreview; /**< Preview of answer correction. */ - -// Displaying dialogues check box state - bool askAboutExpert; /**< shows confirm dialog when expertsAnswerEnable is going to be changed @def true */ - bool showHelpOnStart; /**< shows dialog with help on start en exam or exercise @def true */ + // Displaying dialogues check box state + bool askAboutExpert; /**< shows confirm dialog when expertsAnswerEnable is going to be changed @def true */ + bool showHelpOnStart; /**< shows dialog with help on start en exam or exercise @def true */ }; - #endif // TEXAMPARAMS_H diff --git a/src/libs/core/tfingerpos.cpp b/src/libs/core/tfingerpos.cpp index 749d160c118b20149fbbaa0fde950704e572be55..0619c7cc934f384743f643717218d441798acb0e 100644 --- a/src/libs/core/tfingerpos.cpp +++ b/src/libs/core/tfingerpos.cpp @@ -16,63 +16,51 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tfingerpos.h" - /* static */ -const QString TfingerPos::fretsList[25] = { QStringLiteral("0"), - QStringLiteral("I"), QStringLiteral("II"), QStringLiteral("III"), QStringLiteral("IV"), QStringLiteral("V"), - QStringLiteral("VI"), QStringLiteral("VII"), QStringLiteral("VIII"), QStringLiteral("IX"), QStringLiteral("X"), - QStringLiteral("XI"), QStringLiteral("XII"), QStringLiteral("XIII"), QStringLiteral("XIV"), QStringLiteral("XV"), - QStringLiteral("XVI"), QStringLiteral("XVII"), QStringLiteral("XVIII"), QStringLiteral("XIX"), QStringLiteral("XX"), - QStringLiteral("XI"), QStringLiteral("XII"), QStringLiteral("XIII"), QStringLiteral("XIV") -}; - - -QString TfingerPos::romanFret(quint8 fret) { - if (fret >= 0 && fret < 25) - return fretsList[fret]; - else - return QString(); +const QString TfingerPos::fretsList[25] = {QStringLiteral("0"), QStringLiteral("I"), QStringLiteral("II"), QStringLiteral("III"), QStringLiteral("IV"), + QStringLiteral("V"), QStringLiteral("VI"), QStringLiteral("VII"), QStringLiteral("VIII"), QStringLiteral("IX"), + QStringLiteral("X"), QStringLiteral("XI"), QStringLiteral("XII"), QStringLiteral("XIII"), QStringLiteral("XIV"), + QStringLiteral("XV"), QStringLiteral("XVI"), QStringLiteral("XVII"), QStringLiteral("XVIII"), QStringLiteral("XIX"), + QStringLiteral("XX"), QStringLiteral("XI"), QStringLiteral("XII"), QStringLiteral("XIII"), QStringLiteral("XIV")}; + +QString TfingerPos::romanFret(quint8 fret) +{ + if (fret >= 0 && fret < 25) + return fretsList[fret]; + else + return QString(); } - -QString TfingerPos::toHtml() const { - return QString("<span style=\"font-size: xx-large; font-family: nootka\">%1</span><span style=\"font-size: xx-large;\">%2</span>").arg(str()).arg(fret()); +QString TfingerPos::toHtml() const +{ + return QString("<span style=\"font-size: xx-large; font-family: nootka\">%1</span><span style=\"font-size: xx-large;\">%2</span>").arg(str()).arg(fret()); } - -void TfingerPos::toXml(QXmlStreamWriter& xml, const QString& tag) const { - if (!tag.isEmpty()) - xml.writeStartElement(tag); - xml.writeTextElement(QLatin1String("string"), QString("%1").arg(str())); - xml.writeTextElement(QLatin1String("fret"), QString("%1").arg(fret())); - if (!tag.isEmpty()) - xml.writeEndElement(); // tag +void TfingerPos::toXml(QXmlStreamWriter &xml, const QString &tag) const +{ + if (!tag.isEmpty()) + xml.writeStartElement(tag); + xml.writeTextElement(QLatin1String("string"), QString("%1").arg(str())); + xml.writeTextElement(QLatin1String("fret"), QString("%1").arg(fret())); + if (!tag.isEmpty()) + xml.writeEndElement(); // tag } - -void TfingerPos::fromXml(QXmlStreamReader& xml) { - int s = 0, f = 50; - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("string")) - s = xml.readElementText().toInt(); - else if (xml.name() == QLatin1String("fret")) - f = xml.readElementText().toInt(); +void TfingerPos::fromXml(QXmlStreamReader &xml) +{ + int s = 0, f = 50; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("string")) + s = xml.readElementText().toInt(); + else if (xml.name() == QLatin1String("fret")) + f = xml.readElementText().toInt(); + else + xml.skipCurrentElement(); + } + if (s == 0 || f == 50) + m_pos = 255; // invalid else - xml.skipCurrentElement(); - } - if (s == 0 || f == 50) - m_pos = 255; // invalid - else - setPos(s, f); + setPos(s, f); } - - - - - - - - diff --git a/src/libs/core/tfingerpos.h b/src/libs/core/tfingerpos.h index 57496ae0801f69e99431cd3d57a6d69fa55d8bbb..e58eb9589412d995f67c8325fc782aeb934fe18f 100644 --- a/src/libs/core/tfingerpos.h +++ b/src/libs/core/tfingerpos.h @@ -16,103 +16,96 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #ifndef TFINGERPOS_H #define TFINGERPOS_H - -#include <nootkacoreglobal.h> #include <QtCore/qdatastream.h> #include <QtCore/qxmlstream.h> - +#include <nootkacoreglobal.h> /** * A class describing a finger position on a guitar fingerboard. * It supports up to six strings and up to 39 frets. * Default constructor @p TfingerPos() creates invalid position. * It can be detected with @p isValid() method -*/ + */ class NOOTKACORE_EXPORT TfingerPos { public: - TfingerPos() { m_pos = 255; } - TfingerPos(unsigned char realStr, unsigned char fret) { - setPos(realStr, fret); - } - - /** - * Constructor making @p TfingerPos instance from given integer value - */ - TfingerPos(quint8 positionData) { setData(positionData); } - - /** - * Returns integer value represents fret string number as @p TfingerPos stores that - */ - quint8 data() const { return m_pos; } - void setData(quint8 d) { m_pos = d; } - - /** - * Returns string number (real [1-6]) - */ - quint8 str() const { return (m_pos / 40) + 1; } - - /** - * Returns fret number [0-40] - */ - quint8 fret() const { return m_pos % 40; } - - /** - * Returns @p True when position is valid - */ - bool isValid() const { return m_pos < 241; } - - void setPos(unsigned char realStr, unsigned char fret) { - m_pos = (realStr - 1) * 40 + fret; - } - - /** [STATIC] - * List of QString with roman representation of numbers 0-24. - */ - static const QString fretsList[25]; - - /** [STATIC] - * Returns string with roman number of given value. Range [0-24] is supported - */ - static QString romanFret(quint8 fret); - - /** - * Returns string with roman number. Range [0-24] is supported - */ - QString romanFret() { return romanFret(fret()); } - - /** - * TfingerPos in HTML format as a string fe.: 3 XVII - */ - QString toHtml() const; - - bool operator==( TfingerPos f2) const { return m_pos == f2.m_pos; } - bool operator!=( TfingerPos f2) const { return m_pos != f2.m_pos; } - - friend QDataStream &operator<< (QDataStream &out, const TfingerPos &fPos) { - out << fPos.m_pos; - return out; - } - - friend QDataStream &operator>> (QDataStream &in, TfingerPos &fPos) { - in >> fPos.m_pos; - return in; - } - - /** - * Writes XML structure with <string> and <fret> wrapped into <tag> (if not empty) - by default it is <technical> - */ - void toXml (QXmlStreamWriter& xml, const QString& tag = QLatin1String("technical")) const; - void fromXml(QXmlStreamReader& xml); - - protected: - quint8 m_pos; + TfingerPos() { m_pos = 255; } + TfingerPos(unsigned char realStr, unsigned char fret) { setPos(realStr, fret); } + + /** + * Constructor making @p TfingerPos instance from given integer value + */ + TfingerPos(quint8 positionData) { setData(positionData); } + + /** + * Returns integer value represents fret string number as @p TfingerPos stores that + */ + quint8 data() const { return m_pos; } + void setData(quint8 d) { m_pos = d; } + + /** + * Returns string number (real [1-6]) + */ + quint8 str() const { return (m_pos / 40) + 1; } + + /** + * Returns fret number [0-40] + */ + quint8 fret() const { return m_pos % 40; } + + /** + * Returns @p True when position is valid + */ + bool isValid() const { return m_pos < 241; } + + void setPos(unsigned char realStr, unsigned char fret) { m_pos = (realStr - 1) * 40 + fret; } + + /** [STATIC] + * List of QString with roman representation of numbers 0-24. + */ + static const QString fretsList[25]; + + /** [STATIC] + * Returns string with roman number of given value. Range [0-24] is supported + */ + static QString romanFret(quint8 fret); + + /** + * Returns string with roman number. Range [0-24] is supported + */ + QString romanFret() { return romanFret(fret()); } + + /** + * TfingerPos in HTML format as a string fe.: 3 XVII + */ + QString toHtml() const; + + bool operator==(TfingerPos f2) const { return m_pos == f2.m_pos; } + bool operator!=(TfingerPos f2) const { return m_pos != f2.m_pos; } + + friend QDataStream &operator<<(QDataStream &out, const TfingerPos &fPos) + { + out << fPos.m_pos; + return out; + } + + friend QDataStream &operator>>(QDataStream &in, TfingerPos &fPos) + { + in >> fPos.m_pos; + return in; + } + + /** + * Writes XML structure with <string> and <fret> wrapped into <tag> (if not empty) - by default it is <technical> + */ + void toXml(QXmlStreamWriter &xml, const QString &tag = QLatin1String("technical")) const; + void fromXml(QXmlStreamReader &xml); + +protected: + quint8 m_pos; }; - - #endif // TFINGERPOS_H diff --git a/src/libs/core/tglobals.cpp b/src/libs/core/tglobals.cpp old mode 100755 new mode 100644 index 31d1ba1884a2dbc17825d850d00b80a8e290c87c..92a73d7c141be8d56ec7aa13ba6429464e161d52 --- a/src/libs/core/tglobals.cpp +++ b/src/libs/core/tglobals.cpp @@ -18,912 +18,1140 @@ #include "tglobals.h" #include "nootkaconfig.h" +#include "tcolor.h" +#include "tinitcorelib.h" +#include "tlayoutparams.h" +#include "tpath.h" #include <music/tkeysignature.h> +#include <music/tnamestylefilter.h> #include <music/ttuneobject.h> #include <taudioparams.h> #include <texamparams.h> #include <tscoreparams.h> -#include <music/tnamestylefilter.h> -#include "tpath.h" -#include "tlayoutparams.h" -#include "tinitcorelib.h" -#include "tcolor.h" -#if defined (Q_OS_ANDROID) - #include <Android/tandroid.h> +#if defined(Q_OS_ANDROID) +#include <Android/tandroid.h> #endif +#include <QtCore/qdebug.h> #include <QtCore/qdir.h> #include <QtCore/qsettings.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> #include <QtGui/qscreen.h> -#include <QtCore/qdebug.h> - /*static*/ -QString& Tglobals::path = Tpath::main; -Tglobals* Tglobals::m_instance = nullptr; +QString &Tglobals::path = Tpath::main; +Tglobals *Tglobals::m_instance = nullptr; -QString Tglobals::getInstPath(const QString& appInstPath) { +QString Tglobals::getInstPath(const QString &appInstPath) +{ QString p; QDir d = QDir(appInstPath); -#if defined (Q_OS_WIN) - p = d.path() + QLatin1String("/"); // Windows - #elif defined (Q_OS_ANDROID) +#if defined(Q_OS_WIN) + p = d.path() + QLatin1String("/"); // Windows +#elif defined(Q_OS_ANDROID) p = ":/"; - #elif defined (Q_OS_LINUX) - d.cdUp(); - p = d.path() + QLatin1String("/share/nootka/"); // Linux - #elif defined (Q_OS_MAC) - d.cdUp(); - p = d.path() + QLatin1String("/Resources/"); // MacOs +#elif defined(Q_OS_LINUX) + d.cdUp(); + p = d.path() + QLatin1String("/share/nootka/"); // Linux +#elif defined(Q_OS_MAC) + d.cdUp(); + p = d.path() + QLatin1String("/Resources/"); // MacOs #endif - return p; + return p; } -QString Tglobals::systemUserName() { - #if defined (Q_OS_ANDROID) +QString Tglobals::systemUserName() +{ +#if defined(Q_OS_ANDROID) return Tandroid::accountName(); - #elif defined(Q_OS_WIN32) +#elif defined(Q_OS_WIN32) return qgetenv("USERNAME"); - #else +#else return qgetenv("USER"); - #endif +#endif } -qreal Tglobals::pitchOfFreq(int freq) { - return -36.3763165622959152488 + 39.8631371386483481 * log10(static_cast<qreal>(freq)); +qreal Tglobals::pitchOfFreq(int freq) +{ + return -36.3763165622959152488 + 39.8631371386483481 * log10(static_cast<qreal>(freq)); } /*end static*/ - -Tglobals::Tglobals(QObject* parent) : - QObject(parent), - m_tune(nullptr) +Tglobals::Tglobals(QObject *parent) + : QObject(parent) + , scoreParams(new TscoreParams) + , examParams(new TexamParams) + , audioParams(new TaudioParams) + , layoutParams(new TlayoutParams) + , m_tune(nullptr) { - version = NOOTKA_VERSION; + version = NOOTKA_VERSION; - qRegisterMetaTypeStreamOperators<Ttune>("Ttune"); - qRegisterMetaType<Tnote>("Tnote"); + qRegisterMetaTypeStreamOperators<Ttune>("Ttune"); + qRegisterMetaType<Tnote>("Tnote"); - QCoreApplication::setOrganizationName(QStringLiteral("Nootka")); - QCoreApplication::setOrganizationDomain(QStringLiteral("nootka.sf.net")); - QCoreApplication::setApplicationName(QStringLiteral("Nootka")); + QCoreApplication::setOrganizationName(QStringLiteral("Nootka")); + QCoreApplication::setOrganizationDomain(QStringLiteral("nootka.sf.net")); + QCoreApplication::setApplicationName(QStringLiteral("Nootka")); - E = new TexamParams(); - A = new TaudioParams(); - S = new TscoreParams(); - L = new TlayoutParams(); - m_tuneObject = new TtuneObject(this); + m_tuneObject = new TtuneObject(this); #if defined(Q_OS_WIN32) || defined(Q_OS_MAC) - config = new QSettings(QSettings::IniFormat, QSettings::UserScope, QStringLiteral("Nootka"), qApp->applicationName()); + config = new QSettings(QSettings::IniFormat, QSettings::UserScope, QStringLiteral("Nootka"), qApp->applicationName()); #else - config = new QSettings(); + config = new QSettings(); #endif - loadSettings(config); + loadSettings(config); - if (!m_instance) { - m_instance = this; - } else { - qDebug() << "Tglobals instance has already existed. Application is terminating!"; - qApp->exit(109); - } + if (!m_instance) { + m_instance = this; + } else { + qDebug() << "Tglobals instance has already existed. Application is terminating!"; + qApp->exit(109); + } } - -Tglobals::~Tglobals() { - storeSettings(config); - delete E; - delete A; - delete S; - delete L; - delete m_tune; - delete config; - m_instance = nullptr; +Tglobals::~Tglobals() +{ + storeSettings(config); + delete examParams; + delete audioParams; + delete scoreParams; + delete layoutParams; + delete m_tune; + delete config; + m_instance = nullptr; } +// ########################################################################################## +// ####################### PUBLIC ########################################### +// ########################################################################################## -//########################################################################################## -//####################### PUBLIC ########################################### -//########################################################################################## - -bool Tglobals::wasFirstRun() const { - return config->value(QStringLiteral("common/isFirstRun"), true).toBool(); +bool Tglobals::wasFirstRun() const +{ + return config->value(QStringLiteral("common/isFirstRun"), true).toBool(); } - -void Tglobals::warnAboutNewerVersion(const QString& fileName) { - emit newerVersion(fileName); // TnootkaQML will handle that +void Tglobals::warnAboutNewerVersion(const QString &fileName) +{ + emit newerVersion(fileName); // TnootkaQML will handle that } - -void Tglobals::setUseAnimations(bool use) { - if (m_useAnimations != use) { - m_useAnimations = use; - emit useAnimationsChanged(); - } +void Tglobals::setUseAnimations(bool use) +{ + if (m_useAnimations != use) { + m_useAnimations = use; + emit useAnimationsChanged(); + } } -void Tglobals::setShowHints(bool showH) { - if (showH != m_showHints) { - m_showHints = showH; - emit showHintsChanged(); - } +void Tglobals::setShowHints(bool showH) +{ + if (showH != m_showHints) { + m_showHints = showH; + emit showHintsChanged(); + } } -void Tglobals::setGuiScale(qreal sc) { - if (sc != m_guiScale) { - m_guiScale = sc; - emit guiScaleChanged(); - } +void Tglobals::setGuiScale(qreal sc) +{ + if (sc != m_guiScale) { + m_guiScale = sc; + emit guiScaleChanged(); + } } - -bool Tglobals::showEnharmNotes() const { return S->showEnharmNotes; } -void Tglobals::setShowEnharmNotes(bool showEnharm) { - if (showEnharm != S->showEnharmNotes) { - S->showEnharmNotes = showEnharm; - emit showEnharmNotesChanged(); - } +bool Tglobals::showEnharmNotes() const +{ + return scoreParams->showEnharmNotes; +} +void Tglobals::setShowEnharmNotes(bool showEnharm) +{ + if (showEnharm != scoreParams->showEnharmNotes) { + scoreParams->showEnharmNotes = showEnharm; + emit showEnharmNotesChanged(); + } } -QColor Tglobals::getEnharmNoteColor() const { return S->enharmNotesColor; } -void Tglobals::setEnharmNoteColor(const QColor& c) { S->enharmNotesColor = c; } - -QColor Tglobals::getNoteCursorColor() const { return S->pointerColor; } -void Tglobals::setNoteCursorColor(const QColor& c) { S->pointerColor = c; emit noteCursorColorChanged(); } +QColor Tglobals::getEnharmNoteColor() const +{ + return scoreParams->enharmNotesColor; +} +void Tglobals::setEnharmNoteColor(const QColor &c) +{ + scoreParams->enharmNotesColor = c; +} -bool Tglobals::isSingleNote() const { return S->isSingleNoteMode; } -void Tglobals::setSingleNote(bool sn) { - if (sn != S->isSingleNoteMode) { - S->isSingleNoteMode = sn; - emit singleNoteModeChanged(); - } +QColor Tglobals::getNoteCursorColor() const +{ + return scoreParams->pointerColor; +} +void Tglobals::setNoteCursorColor(const QColor &c) +{ + scoreParams->pointerColor = c; + emit noteCursorColorChanged(); } -bool Tglobals::enableDoubleAccids() const { return S->doubleAccidentalsEnabled; } -void Tglobals::setEnableDoubleAccids(bool dblAcc) { - if (dblAcc != S->doubleAccidentalsEnabled) { - S->doubleAccidentalsEnabled = dblAcc; - emit enableDoubleAccidsChanged(); - } +bool Tglobals::isSingleNote() const +{ + return scoreParams->isSingleNoteMode; +} +void Tglobals::setSingleNote(bool sn) +{ + if (sn != scoreParams->isSingleNoteMode) { + scoreParams->isSingleNoteMode = sn; + emit singleNoteModeChanged(); + } } -bool Tglobals::keySignatureEnabled() const { return S->keySignatureEnabled; } -void Tglobals::setKeySignatureEnabled(bool enKey) { - if (enKey != S->keySignatureEnabled) { - S->keySignatureEnabled = enKey; - emit enableKeySignatureChanged(); - } +bool Tglobals::enableDoubleAccids() const +{ + return scoreParams->doubleAccidentalsEnabled; +} +void Tglobals::setEnableDoubleAccids(bool dblAcc) +{ + if (dblAcc != scoreParams->doubleAccidentalsEnabled) { + scoreParams->doubleAccidentalsEnabled = dblAcc; + emit enableDoubleAccidsChanged(); + } } -int Tglobals::clefType() const { return static_cast<int>(S->clef); } -void Tglobals::setClefType(int clType) { - if (static_cast<Tclef::EclefType>(clType) != S->clef) { - S->clef = static_cast<Tclef::EclefType>(clType); - emit clefTypeChanged(); - } +bool Tglobals::keySignatureEnabled() const +{ + return scoreParams->keySignatureEnabled; +} +void Tglobals::setKeySignatureEnabled(bool enKey) +{ + if (enKey != scoreParams->keySignatureEnabled) { + scoreParams->keySignatureEnabled = enKey; + emit enableKeySignatureChanged(); + } } -QString Tglobals::majorKeyNameSufix() const { return S->majKeyNameSufix; } -void Tglobals::setMajorKeyNameSufix(const QString& mkns) { S->majKeyNameSufix = mkns; } +int Tglobals::clefType() const +{ + return static_cast<int>(scoreParams->clef); +} +void Tglobals::setClefType(int clType) +{ + if (static_cast<Tclef::EclefType>(clType) != scoreParams->clef) { + scoreParams->clef = static_cast<Tclef::EclefType>(clType); + emit clefTypeChanged(); + } +} -QString Tglobals::minorKeyNameSufix() const { return S->minKeyNameSufix; } -void Tglobals::setMinorKeyNameSufix(const QString& mkns) { S->minKeyNameSufix = mkns; } +QString Tglobals::majorKeyNameSufix() const +{ + return scoreParams->majKeyNameSufix; +} +void Tglobals::setMajorKeyNameSufix(const QString &mkns) +{ + scoreParams->majKeyNameSufix = mkns; +} -int Tglobals::keyNameStyle() const { return static_cast<int>(S->nameStyleInKeySign); } -void Tglobals::setKeyNameStyle(int keyStyle) { S->nameStyleInKeySign = static_cast<Tnote::EnameStyle>(keyStyle); } +QString Tglobals::minorKeyNameSufix() const +{ + return scoreParams->minKeyNameSufix; +} +void Tglobals::setMinorKeyNameSufix(const QString &mkns) +{ + scoreParams->minKeyNameSufix = mkns; +} -bool Tglobals::showKeyName() const { return S->showKeySignName; } -void Tglobals::setShowKeyName(bool showKey) { S->showKeySignName = showKey; emit showKeyNameChanged(); } +int Tglobals::keyNameStyle() const +{ + return static_cast<int>(scoreParams->nameStyleInKeySign); +} +void Tglobals::setKeyNameStyle(int keyStyle) +{ + scoreParams->nameStyleInKeySign = static_cast<Tnote::EnameStyle>(keyStyle); +} +bool Tglobals::showKeyName() const +{ + return scoreParams->showKeySignName; +} +void Tglobals::setShowKeyName(bool showKey) +{ + scoreParams->showKeySignName = showKey; + emit showKeyNameChanged(); +} -void Tglobals::updateKeySignatureNames() { - TkeySignature::setNameStyle(S->nameStyleInKeySign, S->majKeyNameSufix, S->minKeyNameSufix); - emit keyNameChanged(); +void Tglobals::updateKeySignatureNames() +{ + TkeySignature::setNameStyle(scoreParams->nameStyleInKeySign, scoreParams->majKeyNameSufix, scoreParams->minKeyNameSufix); + emit keyNameChanged(); } -bool Tglobals::rhythmsEnabled() const { return S->rhythmsEnabled; } -void Tglobals::setRhythmsEnabled(bool enR) { - if (S->rhythmsEnabled != enR) { - S->rhythmsEnabled = enR; - emit rhythmsEnabledChanged(); - } +bool Tglobals::rhythmsEnabled() const +{ + return scoreParams->rhythmsEnabled; +} +void Tglobals::setRhythmsEnabled(bool enR) +{ + if (scoreParams->rhythmsEnabled != enR) { + scoreParams->rhythmsEnabled = enR; + emit rhythmsEnabledChanged(); + } } /* ------------------ Note name switches ------------------ */ -bool Tglobals::seventhIsB() const { return S->seventhIs_B; } +bool Tglobals::seventhIsB() const +{ + return scoreParams->seventhIs_B; +} -void Tglobals::setSeventhIsB(bool isB) { - if (isB != S->seventhIs_B) { - S->seventhIs_B = isB; - emit seventhIsBChanged(); - } +void Tglobals::setSeventhIsB(bool isB) +{ + if (isB != scoreParams->seventhIs_B) { + scoreParams->seventhIs_B = isB; + emit seventhIsBChanged(); + } } -int Tglobals::noteNameStyle() const { return static_cast<int>(S->nameStyleInNoteName); } +int Tglobals::noteNameStyle() const +{ + return static_cast<int>(scoreParams->nameStyleInNoteName); +} -void Tglobals::setNoteNameStyle(int nameStyle) { - Tnote::EnameStyle newNameStyle = static_cast<Tnote::EnameStyle>(nameStyle); - if (newNameStyle != S->nameStyleInNoteName) { - S->nameStyleInNoteName = static_cast<Tnote::EnameStyle>(nameStyle); - Tnote::defaultStyle = S->nameStyleInNoteName; - emit noteNameStyleChanged(); - } +void Tglobals::setNoteNameStyle(int nameStyle) +{ + Tnote::EnameStyle newNameStyle = static_cast<Tnote::EnameStyle>(nameStyle); + if (newNameStyle != scoreParams->nameStyleInNoteName) { + scoreParams->nameStyleInNoteName = static_cast<Tnote::EnameStyle>(nameStyle); + Tnote::defaultStyle = scoreParams->nameStyleInNoteName; + emit noteNameStyleChanged(); + } } -bool Tglobals::scientificOctaves() const { return S->scientificOctaves; } +bool Tglobals::scientificOctaves() const +{ + return scoreParams->scientificOctaves; +} -void Tglobals::setScientificOctaves(bool sciO) { - if (sciO != S->scientificOctaves) { - S->scientificOctaves = sciO; - Tnote::scientificOctaves = sciO; - emit noteNameStyleChanged(); - } +void Tglobals::setScientificOctaves(bool sciO) +{ + if (sciO != scoreParams->scientificOctaves) { + scoreParams->scientificOctaves = sciO; + Tnote::scientificOctaves = sciO; + emit noteNameStyleChanged(); + } } -bool Tglobals::namesOnScore() const { return S->namesOnScore; } +bool Tglobals::namesOnScore() const +{ + return scoreParams->namesOnScore; +} -void Tglobals::setNamesOnScore(bool showNames) { - if (showNames != S->namesOnScore) { - S->namesOnScore = showNames; - emit namesOnScoreChanged(); - } +void Tglobals::setNamesOnScore(bool showNames) +{ + if (showNames != scoreParams->namesOnScore) { + scoreParams->namesOnScore = showNames; + emit namesOnScoreChanged(); + } } -QColor Tglobals::nameColor() const { return S->nameColor; } +QColor Tglobals::nameColor() const +{ + return scoreParams->nameColor; +} -void Tglobals::setNameColor(const QColor& nameC) { - if (nameC != S->nameColor) { - S->nameColor = nameC; - emit nameColorChanged(); - } +void Tglobals::setNameColor(const QColor &nameC) +{ + if (nameC != scoreParams->nameColor) { + scoreParams->nameColor = nameC; + emit nameColorChanged(); + } } /* ------------------ Instrument switches ------------------ */ -void Tglobals::setInstrument(Tinstrument::Etype t) { - if (t != m_instrument.type()) { - m_instrument.setType(t); - emit instrumentChanged(); - } +void Tglobals::setInstrument(Tinstrument::Etype t) +{ + if (t != m_instrument.type()) { + m_instrument.setType(t); + emit instrumentChanged(); + } } - -int Tglobals::transposition() const { return A->transposition; } -void Tglobals::setTransposition(int t) { - if (t != A->transposition) { - A->transposition = t; - emit transpositionChanged(); - } +int Tglobals::transposition() const +{ + return audioParams->transposition; } - -QString Tglobals::markedFrets() const { - QString fretText; - for (int i = 0; i < GmarkedFrets.size(); ++i) { - fretText.append(GmarkedFrets.at(i).toString()); - if (i < GmarkedFrets.size() - 1) - fretText.append(QStringLiteral(",")); - } - return fretText; +void Tglobals::setTransposition(int t) +{ + if (t != audioParams->transposition) { + audioParams->transposition = t; + emit transpositionChanged(); + } } - -void Tglobals::setMarkedFrets(const QString& frets) { - GmarkedFrets.clear(); - QString ex = QStringLiteral("!"); - QStringList fr = frets.split(QStringLiteral(",")); - for (int i = 0; i < fr.size(); ++i) { - QString exMark; - if (fr[i].contains(ex)) { - exMark = ex; - fr[i].replace(ex, QString()); +QString Tglobals::markedFrets() const +{ + QString fretText; + for (int i = 0; i < GmarkedFrets.size(); ++i) { + fretText.append(GmarkedFrets.at(i).toString()); + if (i < GmarkedFrets.size() - 1) + fretText.append(QStringLiteral(",")); } - bool ok; - int frNr = fr[i].toInt(&ok); - if (ok && frNr > 0) - GmarkedFrets << fr[i] + exMark; - } + return fretText; } +void Tglobals::setMarkedFrets(const QString &frets) +{ + GmarkedFrets.clear(); + QString ex = QStringLiteral("!"); + QStringList fr = frets.split(QStringLiteral(",")); + for (int i = 0; i < fr.size(); ++i) { + QString exMark; + if (fr[i].contains(ex)) { + exMark = ex; + fr[i].replace(ex, QString()); + } + bool ok; + int frNr = fr[i].toInt(&ok); + if (ok && frNr > 0) + GmarkedFrets << fr[i] + exMark; + } +} /* ------------------ Sound switches ------------------ */ -bool Tglobals::audioInEnabled() const { return A->INenabled; } -void Tglobals::setAudioInEnabled(bool inEnabled) { A->INenabled = inEnabled; } - -QString Tglobals::inDevName() const { return A->INdevName; } -void Tglobals::setInDevName(const QString& inName) { A->INdevName = inName; } +bool Tglobals::audioInEnabled() const +{ + return audioParams->INenabled; +} +void Tglobals::setAudioInEnabled(bool inEnabled) +{ + audioParams->INenabled = inEnabled; +} -int Tglobals::audioInstrument() const { return A->audioInstrNr; } -void Tglobals::setAudioInstrument(int ai) { A->audioInstrNr = ai; } +QString Tglobals::inDevName() const +{ + return audioParams->INdevName; +} +void Tglobals::setInDevName(const QString &inName) +{ + audioParams->INdevName = inName; +} -qreal Tglobals::minDuration() const { return A->minDuration; } +int Tglobals::audioInstrument() const +{ + return audioParams->audioInstrNr; +} +void Tglobals::setAudioInstrument(int ai) +{ + audioParams->audioInstrNr = ai; +} -void Tglobals::setMinDuration(qreal md) { A->minDuration = md; } +qreal Tglobals::minDuration() const +{ + return audioParams->minDuration; +} -qreal Tglobals::minVolume() const { return A->minimalVol; } -void Tglobals::setMinVolume(qreal mv) { - if (mv != A->minimalVol) { - A->minimalVol = mv; - emit minVolumeChanged(); - } +void Tglobals::setMinDuration(qreal md) +{ + audioParams->minDuration = md; } -int Tglobals::detectionMethod() const { return A->detectMethod; } -void Tglobals::setDetectionMethod(int m) { A->detectMethod = m; } +qreal Tglobals::minVolume() const +{ + return audioParams->minimalVol; +} +void Tglobals::setMinVolume(qreal mv) +{ + if (mv != audioParams->minimalVol) { + audioParams->minimalVol = mv; + emit minVolumeChanged(); + } +} -bool Tglobals::useFilter() const { return A->equalLoudness; } -void Tglobals::setUseFilter(bool use) { A->equalLoudness = use; } +int Tglobals::detectionMethod() const +{ + return audioParams->detectMethod; +} +void Tglobals::setDetectionMethod(int m) +{ + audioParams->detectMethod = m; +} -bool Tglobals::audioOutEnabled() const { return A->OUTenabled; } -void Tglobals::setAudioOutEnabled(bool outEnabled) { A->OUTenabled = outEnabled; } +bool Tglobals::useFilter() const +{ + return audioParams->equalLoudness; +} +void Tglobals::setUseFilter(bool use) +{ + audioParams->equalLoudness = use; +} -QString Tglobals::outDevName() const { return A->OUTdevName; } -void Tglobals::setOutDevName(const QString& odn) { A->OUTdevName = odn; } +bool Tglobals::audioOutEnabled() const +{ + return audioParams->OUTenabled; +} +void Tglobals::setAudioOutEnabled(bool outEnabled) +{ + audioParams->OUTenabled = outEnabled; +} -bool Tglobals::forwardInput() const { return A->forwardInput; } -void Tglobals::setForwardInput(bool fi) { A->forwardInput = fi; } +QString Tglobals::outDevName() const +{ + return audioParams->OUTdevName; +} +void Tglobals::setOutDevName(const QString &odn) +{ + audioParams->OUTdevName = odn; +} +bool Tglobals::forwardInput() const +{ + return audioParams->forwardInput; +} +void Tglobals::setForwardInput(bool fi) +{ + audioParams->forwardInput = fi; +} -int Tglobals::midAfreq() const { return A->midAfreq; } -void Tglobals::setMidAfreq(int midA) { - if (midA != A->midAfreq) { - if (midA < 391 || midA > 493) { - qDebug() << "[Tglobals] middle A frequency out of supported range. Revert to 440Hz" ; - midA = 440; +int Tglobals::midAfreq() const +{ + return audioParams->midAfreq; +} +void Tglobals::setMidAfreq(int midA) +{ + if (midA != audioParams->midAfreq) { + if (midA < 391 || midA > 493) { + qDebug() << "[Tglobals] middle A frequency out of supported range. Revert to 440Hz"; + midA = 440; + } + audioParams->midAfreq = midA; // in range of two semitones up and down around middle A (440Hz) + audioParams->a440diff = midA == 440 ? 0.0 : pitchOfFreq(audioParams->midAfreq) - pitchOfFreq(440); + emit midAfreqChanged(); } - A->midAfreq = midA; // in range of two semitones up and down around middle A (440Hz) - A->a440diff = midA == 440 ? 0.0 : pitchOfFreq(A->midAfreq) - pitchOfFreq(440); - emit midAfreqChanged(); - } } -bool Tglobals::JACKorASIO() const { return A->JACKorASIO; } -void Tglobals::setJACKorASIO(bool JorA) { A->JACKorASIO = JorA; } +bool Tglobals::JACKorASIO() const +{ + return audioParams->JACKorASIO; +} +void Tglobals::setJACKorASIO(bool JorA) +{ + audioParams->JACKorASIO = JorA; +} -void Tglobals::setShowNotesDiff(bool notesDiff) { - if (m_showNotesDiff != notesDiff) { - m_showNotesDiff = notesDiff; - emit showNotesDiffChanged(); - } +void Tglobals::setShowNotesDiff(bool notesDiff) +{ + if (m_showNotesDiff != notesDiff) { + m_showNotesDiff = notesDiff; + emit showNotesDiffChanged(); + } } /* ------------------ Exam switches ------------------ */ -void Tglobals::setIsExam(bool is) { - if (is != m_isExam) { - m_isExam = is; - emit isExamChanged(); - } +void Tglobals::setIsExam(bool is) +{ + if (is != m_isExam) { + m_isExam = is; + emit isExamChanged(); + } } -QString Tglobals::student() const { return E->studentName; } -void Tglobals::setStudent(const QString& st) { - if (st != E->studentName) { - E->studentName = st; - emit studentChanged(); - } +QString Tglobals::student() const +{ + return examParams->studentName; } - -void Tglobals::setCorrectColor(const QColor& c) { - if (c != EanswerColor) { - EanswerColor = c; - emit correctColorChanged(); - } +void Tglobals::setStudent(const QString &st) +{ + if (st != examParams->studentName) { + examParams->studentName = st; + emit studentChanged(); + } } -void Tglobals::setNotBadColor(const QColor& c) { - if (c != EnotBadColor) { - EnotBadColor = c; - emit notBadColorChanged(); - } +void Tglobals::setCorrectColor(const QColor &c) +{ + if (c != EanswerColor) { + EanswerColor = c; + emit correctColorChanged(); + } } -void Tglobals::setWrongColor(const QColor& c) { - if (c != EquestionColor) { - EquestionColor = c; - emit wrongColorChanged(); - } +void Tglobals::setNotBadColor(const QColor &c) +{ + if (c != EnotBadColor) { + EnotBadColor = c; + emit notBadColorChanged(); + } } -bool Tglobals::autoNextQuestion() const { return E->autoNextQuest; } -void Tglobals::setAutoNextQuestion(bool autoNext) { E->autoNextQuest = autoNext; } - -bool Tglobals::expertAnswers() const { return E->expertsAnswerEnable; } -void Tglobals::setExpertAnswers(bool expertA) { E->expertsAnswerEnable = expertA; } - -bool Tglobals::correctAnswers() const { return E->showCorrected; } -void Tglobals::setCorrectAnswers(bool corrAnsw) { E->showCorrected = corrAnsw; } - -bool Tglobals::repeatIncorect() const { return E->repeatIncorrect; } -void Tglobals::setRepeatIncorect(bool repInCorr) { E->repeatIncorrect = repInCorr; } - -bool Tglobals::closeWithoutConfirm() const { return E->closeWithoutConfirm; } -void Tglobals::setCloseWithoutConfirm(bool closeNoConf) { E->closeWithoutConfirm = closeNoConf; } - -bool Tglobals::suggestExam() const { return E->suggestExam; } -void Tglobals::setSuggestExam(bool suggest) { E->suggestExam = suggest; } - -bool Tglobals::extraNames() const { return E->showNameOfAnswered; } -void Tglobals::setExtraNames(bool extraN) { E->showNameOfAnswered = extraN; } +void Tglobals::setWrongColor(const QColor &c) +{ + if (c != EquestionColor) { + EquestionColor = c; + emit wrongColorChanged(); + } +} -bool Tglobals::showWrongPlayed() const { return E->showWrongPlayed; } -void Tglobals::setShowWrongPlayed(bool wrongPlayed) { E->showWrongPlayed = wrongPlayed; } +bool Tglobals::autoNextQuestion() const +{ + return examParams->autoNextQuest; +} +void Tglobals::setAutoNextQuestion(bool autoNext) +{ + examParams->autoNextQuest = autoNext; +} -bool Tglobals::waitForCorrect() const { return E->waitForCorrect; } -void Tglobals::setWaitForCorrect(bool waitFor) { E->waitForCorrect = waitFor; } +bool Tglobals::expertAnswers() const +{ + return examParams->expertsAnswerEnable; +} +void Tglobals::setExpertAnswers(bool expertA) +{ + examParams->expertsAnswerEnable = expertA; +} -QString Tglobals::examsDir() const { return E->examsDir; } +bool Tglobals::correctAnswers() const +{ + return examParams->showCorrected; +} +void Tglobals::setCorrectAnswers(bool corrAnsw) +{ + examParams->showCorrected = corrAnsw; +} -bool Tglobals::gotIt(const QString& key, bool retVal) const { - return config->value(QLatin1String("gotIt/") + key, retVal).toBool(); +bool Tglobals::repeatIncorect() const +{ + return examParams->repeatIncorrect; +} +void Tglobals::setRepeatIncorect(bool repInCorr) +{ + examParams->repeatIncorrect = repInCorr; } -void Tglobals::setGotIt(const QString& key, bool val) { - config->setValue(QLatin1String("gotIt/") + key, val); +bool Tglobals::closeWithoutConfirm() const +{ + return examParams->closeWithoutConfirm; +} +void Tglobals::setCloseWithoutConfirm(bool closeNoConf) +{ + examParams->closeWithoutConfirm = closeNoConf; } +bool Tglobals::suggestExam() const +{ + return examParams->suggestExam; +} +void Tglobals::setSuggestExam(bool suggest) +{ + examParams->suggestExam = suggest; +} +bool Tglobals::extraNames() const +{ + return examParams->showNameOfAnswered; +} +void Tglobals::setExtraNames(bool extraN) +{ + examParams->showNameOfAnswered = extraN; +} -void Tglobals::setGuitarParams(int fretNr, const Ttune& tun) { - if (static_cast<uint>(fretNr) != GfretsNumber) - GfretsNumber = fretNr; +bool Tglobals::showWrongPlayed() const +{ + return examParams->showWrongPlayed; +} +void Tglobals::setShowWrongPlayed(bool wrongPlayed) +{ + examParams->showWrongPlayed = wrongPlayed; +} - if (tun.type() == Ttune::Custom || tun.type() != tuning()->typeInt()) - setTune(tun); +bool Tglobals::waitForCorrect() const +{ + return examParams->waitForCorrect; +} +void Tglobals::setWaitForCorrect(bool waitFor) +{ + examParams->waitForCorrect = waitFor; +} - emit guitarParamsChanged(); +QString Tglobals::examsDir() const +{ + return examParams->examsDir; } +bool Tglobals::gotIt(const QString &key, bool retVal) const +{ + return config->value(QLatin1String("gotIt/") + key, retVal).toBool(); +} -int Tglobals::stringNumber() { return m_tune->stringNr(); } +void Tglobals::setGotIt(const QString &key, bool val) +{ + config->setValue(QLatin1String("gotIt/") + key, val); +} +void Tglobals::setGuitarParams(int fretNr, const Ttune &tun) +{ + if (static_cast<uint>(fretNr) != GfretsNumber) + GfretsNumber = fretNr; -void Tglobals::setFingerColor(const QColor& fc) { GfingerColor = fc; emit fingerColorChanged(); } -void Tglobals::setSelectedColor(const QColor& sc) { GselectedColor = sc; emit selectedColorChanged(); } + if (tun.type() == Ttune::Custom || tun.type() != tuning()->typeInt()) + setTune(tun); + emit guitarParamsChanged(); +} -void Tglobals::loadSettings(QSettings* cfg) { - // In fact, values without group are stored under 'General' key, but using it explicitly makes group '%General' - different. - // It is messy, so get rid of directly calling that group - if (cfg->contains(QLatin1String("General/geometry"))) { // old config key - m_geometry = cfg->value(QStringLiteral("General/geometry"), QRect()).toRect(); - cfg->remove(QLatin1String("General/geometry")); // and remove it to grab new one next launch - } else - m_geometry = cfg->value(QStringLiteral("geometry"), QRect()).toRect(); - if (m_geometry.width() < 720 || m_geometry.height() < 480) { - m_geometry.setWidth(qMax(qRound(qApp->primaryScreen()->size().width() * 0.75), 720)); - m_geometry.setHeight(qMax(qRound(qApp->primaryScreen()->size().height() * 0.75), 480)); - m_geometry.setX((qApp->primaryScreen()->size().width() - m_geometry.width()) / 2); - m_geometry.setY((qApp->primaryScreen()->size().height() - m_geometry.height()) / 2); - } - m_guiScale = qBound(0.5, cfg->value(QStringLiteral("scale"), 1.0).toReal(), 1.5); +int Tglobals::stringNumber() +{ + return m_tune->stringNr(); +} - cfg->beginGroup(QLatin1String("common")); - isFirstRun = cfg->value(QStringLiteral("isFirstRun"), true).toBool(); - m_useAnimations = cfg->value(QStringLiteral("useAnimations"), true).toBool(); - m_showHints = cfg->value(QStringLiteral("showHints"), true).toBool(); - lang = cfg->value(QStringLiteral("language"), QString()).toString(); - cfg->endGroup(); +void Tglobals::setFingerColor(const QColor &fc) +{ + GfingerColor = fc; + emit fingerColorChanged(); +} +void Tglobals::setSelectedColor(const QColor &sc) +{ + GselectedColor = sc; + emit selectedColorChanged(); +} -//score widget settings - cfg->beginGroup(QLatin1String("score")); - S->keySignatureEnabled = cfg->value(QStringLiteral("keySignature"), false).toBool(); - S->showKeySignName = cfg->value(QStringLiteral("keyName"), true).toBool(); //true; - S->nameStyleInKeySign = Tnote::EnameStyle(cfg->value(QStringLiteral("nameStyleInKey"), - static_cast<int>(Tnote::e_english_Bb)).toInt()); - S->majKeyNameSufix = cfg->value(QStringLiteral("majorKeysSufix"), QString()).toString(); - S->minKeyNameSufix = cfg->value(QStringLiteral("minorKeysSufix"), QString()).toString(); - if (cfg->contains("pointerColor")) - S->pointerColor = cfg->value(QStringLiteral("pointerColor")).value<QColor>(); - else - S->pointerColor = Tcolor::invert(qApp->palette().highlight().color()); - S->clef = Tclef::EclefType(cfg->value(QStringLiteral("clef"), static_cast<int>(Tclef::Treble_G_8down)).toInt()); - // Rhythms has to be enabled when no clef (percussion) - S->rhythmsEnabled = cfg->value(QStringLiteral("rhythmsEnabled"), true).toBool() || S->clef == Tclef::NoClef; - S->isSingleNoteMode = cfg->value(QStringLiteral("singleNoteMode"), false).toBool(); - S->tempo = cfg->value(QStringLiteral("tempo"), 120).toInt(); - S->scoreScale = cfg->value(QStringLiteral("scoreScale"), 1.0).toReal(); -#if defined (Q_OS_ANDROID) - S->lastXmlDir = cfg->value(QStringLiteral("lastXmlDir"), QString()).toString(); - if (!S->lastXmlDir.isEmpty() && (S->lastXmlDir == QDir::homePath() || !QFileInfo::exists(S->lastXmlDir) || !QFileInfo(S->lastXmlDir).isWritable())) { - S->lastXmlDir.clear(); - /** WORKAROUND: This is workaround for 2.0.0 bug where lastXmlDir was set to QDir::homePath() - * which is internal application location - no way to navigate outside, - * so musicXML files on device storage(s) couldn't be reached. */ - qDebug() << "[Tglobals] Fixed music XML directory path."; - } +void Tglobals::loadSettings(QSettings *cfg) +{ + // In fact, values without group are stored under 'General' key, but using it explicitly makes group '%General' - different. + // It is messy, so get rid of directly calling that group + if (cfg->contains(QLatin1String("General/geometry"))) { // old config key + m_geometry = cfg->value(QStringLiteral("General/geometry"), QRect()).toRect(); + cfg->remove(QLatin1String("General/geometry")); // and remove it to grab new one next launch + } else + m_geometry = cfg->value(QStringLiteral("geometry"), QRect()).toRect(); + if (m_geometry.width() < 720 || m_geometry.height() < 480) { + m_geometry.setWidth(qMax(qRound(qApp->primaryScreen()->size().width() * 0.75), 720)); + m_geometry.setHeight(qMax(qRound(qApp->primaryScreen()->size().height() * 0.75), 480)); + m_geometry.setX((qApp->primaryScreen()->size().width() - m_geometry.width()) / 2); + m_geometry.setY((qApp->primaryScreen()->size().height() - m_geometry.height()) / 2); + } + m_guiScale = qBound(0.5, cfg->value(QStringLiteral("scale"), 1.0).toReal(), 1.5); + + cfg->beginGroup(QLatin1String("common")); + isFirstRun = cfg->value(QStringLiteral("isFirstRun"), true).toBool(); + m_useAnimations = cfg->value(QStringLiteral("useAnimations"), true).toBool(); + m_showHints = cfg->value(QStringLiteral("showHints"), true).toBool(); + lang = cfg->value(QStringLiteral("language"), QString()).toString(); + cfg->endGroup(); + + // score widget settings + cfg->beginGroup(QLatin1String("score")); + scoreParams->keySignatureEnabled = cfg->value(QStringLiteral("keySignature"), false).toBool(); + scoreParams->showKeySignName = cfg->value(QStringLiteral("keyName"), true).toBool(); // true; + scoreParams->nameStyleInKeySign = Tnote::EnameStyle(cfg->value(QStringLiteral("nameStyleInKey"), static_cast<int>(Tnote::e_english_Bb)).toInt()); + scoreParams->majKeyNameSufix = cfg->value(QStringLiteral("majorKeysSufix"), QString()).toString(); + scoreParams->minKeyNameSufix = cfg->value(QStringLiteral("minorKeysSufix"), QString()).toString(); + if (cfg->contains("pointerColor")) + scoreParams->pointerColor = cfg->value(QStringLiteral("pointerColor")).value<QColor>(); + else + scoreParams->pointerColor = Tcolor::invert(qApp->palette().highlight().color()); + scoreParams->clef = Tclef::EclefType(cfg->value(QStringLiteral("clef"), static_cast<int>(Tclef::Treble_G_8down)).toInt()); + // Rhythms has to be enabled when no clef (percussion) + scoreParams->rhythmsEnabled = cfg->value(QStringLiteral("rhythmsEnabled"), true).toBool() || scoreParams->clef == Tclef::NoClef; + scoreParams->isSingleNoteMode = cfg->value(QStringLiteral("singleNoteMode"), false).toBool(); + scoreParams->tempo = cfg->value(QStringLiteral("tempo"), 120).toInt(); + scoreParams->scoreScale = cfg->value(QStringLiteral("scoreScale"), 1.0).toReal(); +#if defined(Q_OS_ANDROID) + S->lastXmlDir = cfg->value(QStringLiteral("lastXmlDir"), QString()).toString(); + if (!S->lastXmlDir.isEmpty() && (S->lastXmlDir == QDir::homePath() || !QFileInfo::exists(S->lastXmlDir) || !QFileInfo(S->lastXmlDir).isWritable())) { + S->lastXmlDir.clear(); + /** WORKAROUND: This is workaround for 2.0.0 bug where lastXmlDir was set to QDir::homePath() + * which is internal application location - no way to navigate outside, + * so musicXML files on device storage(s) couldn't be reached. */ + qDebug() << "[Tglobals] Fixed music XML directory path."; + } #else - S->lastXmlDir = cfg->value(QStringLiteral("lastXmlDir"), QDir::homePath()).toString(); + scoreParams->lastXmlDir = cfg->value(QStringLiteral("lastXmlDir"), QDir::homePath()).toString(); #endif - S->scientificOctaves = cfg->value(QStringLiteral("scientificOctaves"), false).toBool(); - Tnote::scientificOctaves = S->scientificOctaves; - cfg->endGroup(); - -//common for score widget and note name - cfg->beginGroup(QLatin1String("common")); - S->doubleAccidentalsEnabled = cfg->value(QStringLiteral("doubleAccidentals"), false).toBool(); - S->showEnharmNotes = cfg->value(QStringLiteral("showEnaharmonicNotes"), false).toBool(); -// if (!S->isSingleNoteMode) // enharmonically equal notes can be enabled only in single note mode -// S->showEnharmNotes = false; - S->enharmNotesColor = cfg->value(QStringLiteral("enharmonicNotesColor"), QColor(0, 162, 162)).value<QColor>(); // turquoise - S->seventhIs_B = cfg->value(QStringLiteral("is7thNote_B"), true).toBool(); //true; - cfg->endGroup(); - -//note name settings - cfg->beginGroup(QLatin1String("noteName")); - S->nameStyleInNoteName = Tnote::EnameStyle(cfg->value(QStringLiteral("nameStyle"), - static_cast<int>(Tnote::e_english_Bb)).toInt()); - S->solfegeStyle = Tnote::EnameStyle(cfg->value(QStringLiteral("solfegeStyle"), - static_cast<int>(getSolfegeStyle())).toInt()); - S->octaveInNoteNameFormat = cfg->value(QStringLiteral("octaveInName"), true).toBool(); - S->namesOnScore = cfg->value(QStringLiteral("namesOnScore"), true).toBool(); - S->nameColor = cfg->value(QStringLiteral("namesColor"), QColor(0, 225, 225)).value<QColor>(); - cfg->endGroup(); -// Fix name style depending on 7th note is was set wrongly in configuration (naughty user) - if (S->seventhIs_B) { - if (S->nameStyleInNoteName == Tnote::e_norsk_Hb) - S->nameStyleInNoteName = Tnote::e_english_Bb; - else if (S->nameStyleInNoteName == Tnote::e_deutsch_His) - S->nameStyleInNoteName = Tnote::e_nederl_Bis; - } else { - if (S->nameStyleInNoteName == Tnote::e_english_Bb) - S->nameStyleInNoteName = Tnote::e_norsk_Hb; - else if (S->nameStyleInNoteName == Tnote::e_nederl_Bis) - S->nameStyleInNoteName = Tnote::e_deutsch_His; - } -// Initialize name filter - TnameStyleFilter::setStyleFilter(&S->seventhIs_B, &S->solfegeStyle); - Tnote::defaultStyle = S->nameStyleInNoteName; -// verify key name style - does it match with 7th note name - auto tmpKeyStyle = S->nameStyleInKeySign; - S->nameStyleInKeySign = TnameStyleFilter::get(S->nameStyleInKeySign); - if (tmpKeyStyle != S->nameStyleInKeySign) - qDebug() << "[Tglobals] Name style in key signatures" << tmpKeyStyle - << "didn't match with 7th note name. Fixed to" << S->nameStyleInKeySign; - -// guitar settings - Ttune::prepareDefinedTunes(); - cfg->beginGroup(QLatin1String("guitar")); - int instr = cfg->value(QStringLiteral("instrument"), static_cast<int>(Tinstrument::ClassicalGuitar)).toInt(); - if (instr < 0 || instr >= INSTR_COUNT) { + scoreParams->scientificOctaves = cfg->value(QStringLiteral("scientificOctaves"), false).toBool(); + Tnote::scientificOctaves = scoreParams->scientificOctaves; + cfg->endGroup(); + + // common for score widget and note name + cfg->beginGroup(QLatin1String("common")); + scoreParams->doubleAccidentalsEnabled = cfg->value(QStringLiteral("doubleAccidentals"), false).toBool(); + scoreParams->showEnharmNotes = cfg->value(QStringLiteral("showEnaharmonicNotes"), false).toBool(); + // if (!S->isSingleNoteMode) // enharmonically equal notes can be enabled only in single note mode + // S->showEnharmNotes = false; + scoreParams->enharmNotesColor = cfg->value(QStringLiteral("enharmonicNotesColor"), QColor(0, 162, 162)).value<QColor>(); // turquoise + scoreParams->seventhIs_B = cfg->value(QStringLiteral("is7thNote_B"), true).toBool(); // true; + cfg->endGroup(); + + // note name settings + cfg->beginGroup(QLatin1String("noteName")); + scoreParams->nameStyleInNoteName = Tnote::EnameStyle(cfg->value(QStringLiteral("nameStyle"), static_cast<int>(Tnote::e_english_Bb)).toInt()); + scoreParams->solfegeStyle = Tnote::EnameStyle(cfg->value(QStringLiteral("solfegeStyle"), static_cast<int>(getSolfegeStyle())).toInt()); + scoreParams->octaveInNoteNameFormat = cfg->value(QStringLiteral("octaveInName"), true).toBool(); + scoreParams->namesOnScore = cfg->value(QStringLiteral("namesOnScore"), true).toBool(); + scoreParams->nameColor = cfg->value(QStringLiteral("namesColor"), QColor(0, 225, 225)).value<QColor>(); + cfg->endGroup(); + // Fix name style depending on 7th note is was set wrongly in configuration (naughty user) + if (scoreParams->seventhIs_B) { + if (scoreParams->nameStyleInNoteName == Tnote::e_norsk_Hb) + scoreParams->nameStyleInNoteName = Tnote::e_english_Bb; + else if (scoreParams->nameStyleInNoteName == Tnote::e_deutsch_His) + scoreParams->nameStyleInNoteName = Tnote::e_nederl_Bis; + } else { + if (scoreParams->nameStyleInNoteName == Tnote::e_english_Bb) + scoreParams->nameStyleInNoteName = Tnote::e_norsk_Hb; + else if (scoreParams->nameStyleInNoteName == Tnote::e_nederl_Bis) + scoreParams->nameStyleInNoteName = Tnote::e_deutsch_His; + } + // Initialize name filter + TnameStyleFilter::setStyleFilter(&scoreParams->seventhIs_B, &scoreParams->solfegeStyle); + Tnote::defaultStyle = scoreParams->nameStyleInNoteName; + // verify key name style - does it match with 7th note name + auto tmpKeyStyle = scoreParams->nameStyleInKeySign; + scoreParams->nameStyleInKeySign = TnameStyleFilter::get(scoreParams->nameStyleInKeySign); + if (tmpKeyStyle != scoreParams->nameStyleInKeySign) + qDebug() << "[Tglobals] Name style in key signatures" << tmpKeyStyle << "didn't match with 7th note name. Fixed to" << scoreParams->nameStyleInKeySign; + + // guitar settings + Ttune::prepareDefinedTunes(); + cfg->beginGroup(QLatin1String("guitar")); + int instr = cfg->value(QStringLiteral("instrument"), static_cast<int>(Tinstrument::ClassicalGuitar)).toInt(); + if (instr < 0 || instr >= INSTR_COUNT) { qDebug() << "[Tglobals] Unsupported instrument in configuration file!" << instr << "\nReseted to none."; instr = 0; - } - m_instrument.setType(static_cast<Tinstrument::Etype>(instr)); - GfretsNumber = cfg->value(QStringLiteral("fretNumber"), 19).toInt(); - GisRightHanded = cfg->value(QStringLiteral("rightHanded"), true).toBool(); //true; - GshowOtherPos = cfg->value(QStringLiteral("showOtherPos"), false).toBool(); - if (cfg->contains("fingerColor")) - GfingerColor = cfg->value(QStringLiteral("fingerColor")).value<QColor>(); - else - GfingerColor = QColor(255, 0, 127, 150); // nice pink with translucency - if (cfg->contains("selectedColor")) - GselectedColor = cfg->value(QStringLiteral("selectedColor")).value<QColor>(); - else - GselectedColor = QColor(51, 153, 255); // nice blue as default - auto tun = cfg->value(QStringLiteral("tune")); - if (tun.isValid()) { - Ttune tmpTune = tun.value<Ttune>(); - setTune(tmpTune); - } else - setTune(Ttune::stdTune); - GpreferFlats = cfg->value(QStringLiteral("flatsPrefered"), false).toBool();; - QList<QVariant> fretsList; - fretsList << 5 << 7 << (m_instrument.ukulele() ? 10 : 9) << "12!"; - if (!m_instrument.ukulele()) + } + m_instrument.setType(static_cast<Tinstrument::Etype>(instr)); + GfretsNumber = cfg->value(QStringLiteral("fretNumber"), 19).toInt(); + GisRightHanded = cfg->value(QStringLiteral("rightHanded"), true).toBool(); // true; + GshowOtherPos = cfg->value(QStringLiteral("showOtherPos"), false).toBool(); + if (cfg->contains("fingerColor")) + GfingerColor = cfg->value(QStringLiteral("fingerColor")).value<QColor>(); + else + GfingerColor = QColor(255, 0, 127, 150); // nice pink with translucency + if (cfg->contains("selectedColor")) + GselectedColor = cfg->value(QStringLiteral("selectedColor")).value<QColor>(); + else + GselectedColor = QColor(51, 153, 255); // nice blue as default + auto tun = cfg->value(QStringLiteral("tune")); + if (tun.isValid()) { + Ttune tmpTune = tun.value<Ttune>(); + setTune(tmpTune); + } else + setTune(Ttune::stdTune); + GpreferFlats = cfg->value(QStringLiteral("flatsPrefered"), false).toBool(); + ; + QList<QVariant> fretsList; + fretsList << 5 << 7 << (m_instrument.ukulele() ? 10 : 9) << "12!"; + if (!m_instrument.ukulele()) fretsList << 15 << 17; - GmarkedFrets = cfg->value(QStringLiteral("dotsOnFrets"), fretsList).toList(); - cfg->endGroup(); + GmarkedFrets = cfg->value(QStringLiteral("dotsOnFrets"), fretsList).toList(); + cfg->endGroup(); -// Exam settings - cfg->beginGroup(QLatin1String("exam")); - if (cfg->contains(QLatin1String("questionColor"))) + // Exam settings + cfg->beginGroup(QLatin1String("exam")); + if (cfg->contains(QLatin1String("questionColor"))) EquestionColor = cfg->value(QStringLiteral("questionColor")).value<QColor>(); - else + else EquestionColor = QColor(255, 0, 0); // red - if (cfg->contains(QLatin1String("answerColor"))) + if (cfg->contains(QLatin1String("answerColor"))) EanswerColor = cfg->value(QStringLiteral("answerColor")).value<QColor>(); - else + else EanswerColor = QColor(0, 255, 0); // green - if (cfg->contains(QLatin1String("notBadColor"))) + if (cfg->contains(QLatin1String("notBadColor"))) EnotBadColor = cfg->value(QStringLiteral("notBadColor")).value<QColor>(); - else + else EnotBadColor = QColor(255, 128, 0); // #FF8000 - EquestionColor.setAlpha(255); - EanswerColor.setAlpha(255); - EnotBadColor.setAlpha(255); - E->autoNextQuest = cfg->value(QStringLiteral("autoNextQuest"), false).toBool(); - E->repeatIncorrect = cfg->value(QStringLiteral("repeatIncorrect"), true).toBool(); - E->expertsAnswerEnable = cfg->value(QStringLiteral("expertsAnswerEnable"), false).toBool(); - E->studentName = cfg->value(QStringLiteral("studentName"), QString()).toString(); - if (E->studentName.isEmpty()) - E->studentName = systemUserName(); -#if defined (Q_OS_ANDROID) - bool hasWriteAccess = Tandroid::hasWriteAccess(); - if (hasWriteAccess) { + EquestionColor.setAlpha(255); + EanswerColor.setAlpha(255); + EnotBadColor.setAlpha(255); + examParams->autoNextQuest = cfg->value(QStringLiteral("autoNextQuest"), false).toBool(); + examParams->repeatIncorrect = cfg->value(QStringLiteral("repeatIncorrect"), true).toBool(); + examParams->expertsAnswerEnable = cfg->value(QStringLiteral("expertsAnswerEnable"), false).toBool(); + examParams->studentName = cfg->value(QStringLiteral("studentName"), QString()).toString(); + if (examParams->studentName.isEmpty()) + examParams->studentName = systemUserName(); +#if defined(Q_OS_ANDROID) + bool hasWriteAccess = Tandroid::hasWriteAccess(); + if (hasWriteAccess) { E->examsDir = cfg->value(QStringLiteral("examsDir"), Tandroid::getExternalPath()).toString(); if (!QFileInfo::exists(E->examsDir) || !QFileInfo(E->examsDir).isWritable()) // reset if doesn't exist - E->examsDir = Tandroid::getExternalPath(); + E->examsDir = Tandroid::getExternalPath(); E->levelsDir = cfg->value(QStringLiteral("levelsDir"), Tandroid::getExternalPath()).toString(); if (!QFileInfo::exists(E->levelsDir) || !QFileInfo(E->levelsDir).isWritable()) - E->levelsDir = Tandroid::getExternalPath(); - } + E->levelsDir = Tandroid::getExternalPath(); + } #else - E->examsDir = cfg->value(QStringLiteral("examsDir"), QDir::homePath()).toString(); - if (!QFileInfo::exists(E->examsDir)) // reset if doesn't exist - E->examsDir = QDir::homePath(); - E->levelsDir = cfg->value(QStringLiteral("levelsDir"), QDir::homePath()).toString(); - if (!QFileInfo::exists(E->levelsDir)) // reset if doesn't exist - E->levelsDir = QDir::homePath(); + examParams->examsDir = cfg->value(QStringLiteral("examsDir"), QDir::homePath()).toString(); + if (!QFileInfo::exists(examParams->examsDir)) // reset if doesn't exist + examParams->examsDir = QDir::homePath(); + examParams->levelsDir = cfg->value(QStringLiteral("levelsDir"), QDir::homePath()).toString(); + if (!QFileInfo::exists(examParams->levelsDir)) // reset if doesn't exist + examParams->levelsDir = QDir::homePath(); #endif - E->closeWithoutConfirm = cfg->value(QStringLiteral("closeWithoutConfirm"), false).toBool(); - E->showCorrected = cfg->value(QStringLiteral("showCorrected"), true).toBool(); - E->mistakePreview = cfg->value(QStringLiteral("mistakePreview"), 3000).toInt(); - E->correctPreview = cfg->value(QStringLiteral("correctPreview"), 3000).toInt(); - E->questionDelay = cfg->value(QStringLiteral("questionDelay"), 150).toInt(); - E->suggestExam = cfg->value(QStringLiteral("suggestExam"), true).toBool(); - E->afterMistake = (TexamParams::EafterMistake)cfg->value(QStringLiteral("afterMistake"), - static_cast<int>(TexamParams::e_continue)).toInt(); - E->showNameOfAnswered = cfg->value(QStringLiteral("showNameOfAnswered"), true).toBool(); - E->showWrongPlayed = cfg->value(QStringLiteral("showWrongPlayed"), false).toBool(); - E->waitForCorrect = cfg->value(QStringLiteral("waitForCorrect"), true).toBool(); - E->showHelpOnStart = cfg->value(QStringLiteral("showHelpOnStart"), true).toBool(); - E->askAboutExpert = cfg->value(QStringLiteral("askAboutExpert"), true).toBool(); - cfg->endGroup(); - -// Sound settings - cfg->beginGroup(QLatin1String("sound")); - A->JACKorASIO = cfg->value(QStringLiteral("JACKorASIO"), false).toBool(); - A->OUTenabled = cfg->value(QStringLiteral("outSoundEnabled"), true).toBool(); - A->OUTdevName = cfg->value(QStringLiteral("outDeviceName"), QString()).toString(); - A->midiEnabled = cfg->value(QStringLiteral("midiEnabled"), false).toBool(); - A->midiPortName = cfg->value(QStringLiteral("midiPortName"), QString()).toString(); - A->midiInstrNr = (unsigned char)cfg->value(QStringLiteral("midiInstrumentNr"), 0).toInt(); - A->audioInstrNr = qBound(1, cfg->value(QStringLiteral("audioInstrumentNr"), 1).toInt(), INSTR_COUNT); - A->INenabled = cfg->value(QStringLiteral("inSoundEnabled"), true).toBool(); - A->INdevName = cfg->value(QStringLiteral("inDeviceName"), QString()).toString(); - A->detectMethod = qBound(0, cfg->value(QStringLiteral("detectionMethod"), 2).toInt(), 2); // MPM modified cepstrum -#if defined (Q_OS_ANDROID) // Input sound is loud on mobile + examParams->closeWithoutConfirm = cfg->value(QStringLiteral("closeWithoutConfirm"), false).toBool(); + examParams->showCorrected = cfg->value(QStringLiteral("showCorrected"), true).toBool(); + examParams->mistakePreview = cfg->value(QStringLiteral("mistakePreview"), 3000).toInt(); + examParams->correctPreview = cfg->value(QStringLiteral("correctPreview"), 3000).toInt(); + examParams->questionDelay = cfg->value(QStringLiteral("questionDelay"), 150).toInt(); + examParams->suggestExam = cfg->value(QStringLiteral("suggestExam"), true).toBool(); + examParams->afterMistake = (TexamParams::EafterMistake)cfg->value(QStringLiteral("afterMistake"), static_cast<int>(TexamParams::e_continue)).toInt(); + examParams->showNameOfAnswered = cfg->value(QStringLiteral("showNameOfAnswered"), true).toBool(); + examParams->showWrongPlayed = cfg->value(QStringLiteral("showWrongPlayed"), false).toBool(); + examParams->waitForCorrect = cfg->value(QStringLiteral("waitForCorrect"), true).toBool(); + examParams->showHelpOnStart = cfg->value(QStringLiteral("showHelpOnStart"), true).toBool(); + examParams->askAboutExpert = cfg->value(QStringLiteral("askAboutExpert"), true).toBool(); + cfg->endGroup(); + + // Sound settings + cfg->beginGroup(QLatin1String("sound")); + audioParams->JACKorASIO = cfg->value(QStringLiteral("JACKorASIO"), false).toBool(); + audioParams->OUTenabled = cfg->value(QStringLiteral("outSoundEnabled"), true).toBool(); + audioParams->OUTdevName = cfg->value(QStringLiteral("outDeviceName"), QString()).toString(); + audioParams->midiEnabled = cfg->value(QStringLiteral("midiEnabled"), false).toBool(); + audioParams->midiPortName = cfg->value(QStringLiteral("midiPortName"), QString()).toString(); + audioParams->midiInstrNr = (unsigned char)cfg->value(QStringLiteral("midiInstrumentNr"), 0).toInt(); + audioParams->audioInstrNr = qBound(1, cfg->value(QStringLiteral("audioInstrumentNr"), 1).toInt(), INSTR_COUNT); + audioParams->INenabled = cfg->value(QStringLiteral("inSoundEnabled"), true).toBool(); + audioParams->INdevName = cfg->value(QStringLiteral("inDeviceName"), QString()).toString(); + audioParams->detectMethod = qBound(0, cfg->value(QStringLiteral("detectionMethod"), 2).toInt(), 2); // MPM modified cepstrum +#if defined(Q_OS_ANDROID) // Input sound is loud on mobile A->minimalVol = cfg->value(QStringLiteral("minimalVolume"), 0.6).toFloat(); #else - A->minimalVol = cfg->value(QStringLiteral("minimalVolume"), 0.4).toReal(); - A->dumpPath = cfg->value(QLatin1String("dumpPath"), QString()).toString(); + audioParams->minimalVol = cfg->value(QStringLiteral("minimalVolume"), 0.4).toReal(); + audioParams->dumpPath = cfg->value(QLatin1String("dumpPath"), QString()).toString(); m_showNotesDiff = cfg->value(QStringLiteral("showNotesDiff"), false).toBool(); #endif - A->minDuration = cfg->value(QStringLiteral("minimalDuration"), 0.15).toReal(); // 150 ms + audioParams->minDuration = cfg->value(QStringLiteral("minimalDuration"), 0.15).toReal(); // 150 ms setMidAfreq(cfg->value(QStringLiteral("midAfreq"), 440).toInt()); - A->intonation = static_cast<quint8>(qBound(0, cfg->value(QStringLiteral("intonation"), 3).toInt(), 5)); - A->forwardInput = cfg->value(QStringLiteral("forwardInput"), false).toBool(); - A->equalLoudness = cfg->value(QStringLiteral("equalLoudness"), true).toBool(); - A->transposition = cfg->value(QStringLiteral("transposition"), 0).toInt(); - A->stoppedByUser = cfg->value(QStringLiteral("stoppedByUser"), false).toBool(); - A->audibleMetro = cfg->value(QStringLiteral("audibleMetro"), false).toBool(); - A->countBefore = cfg->value(QStringLiteral("countBefore"), false).toBool(); - A->quantization = cfg->value(QStringLiteral("quantization"), 6).toInt(); - cfg->endGroup(); - -#if defined (Q_OS_ANDROID) - m_keepScreenOn = cfg->value(QStringLiteral("keepScreenOn"), true).toBool(); - m_disableRotation = cfg->value(QStringLiteral("disableRotation"), true).toBool(); - m_fullScreen = cfg->value(QStringLiteral("fullScreen"), true).toBool(); - m_touchStopsSniff = cfg->value(QStringLiteral("touchStopsSniff"), true).toBool(); + audioParams->intonation = static_cast<quint8>(qBound(0, cfg->value(QStringLiteral("intonation"), 3).toInt(), 5)); + audioParams->forwardInput = cfg->value(QStringLiteral("forwardInput"), false).toBool(); + audioParams->equalLoudness = cfg->value(QStringLiteral("equalLoudness"), true).toBool(); + audioParams->transposition = cfg->value(QStringLiteral("transposition"), 0).toInt(); + audioParams->stoppedByUser = cfg->value(QStringLiteral("stoppedByUser"), false).toBool(); + audioParams->audibleMetro = cfg->value(QStringLiteral("audibleMetro"), false).toBool(); + audioParams->countBefore = cfg->value(QStringLiteral("countBefore"), false).toBool(); + audioParams->quantization = cfg->value(QStringLiteral("quantization"), 6).toInt(); + cfg->endGroup(); + +#if defined(Q_OS_ANDROID) + m_keepScreenOn = cfg->value(QStringLiteral("keepScreenOn"), true).toBool(); + m_disableRotation = cfg->value(QStringLiteral("disableRotation"), true).toBool(); + m_fullScreen = cfg->value(QStringLiteral("fullScreen"), true).toBool(); + m_touchStopsSniff = cfg->value(QStringLiteral("touchStopsSniff"), true).toBool(); #endif - bool transposeTuning = S->clef == Tclef::Bass_F_8down; - if (S->clef == Tclef::Bass_F_8down) { - // This is clear: old Nootka had dropped down bass clef, now there is just bass clef - // so clef is changed and transposition is added - qDebug() << "[Tglobals] Dropped bass clef detected. Converting to ordinary bass clef."; - S->clef = Tclef::Bass_F; - if (A->transposition == 0) { - qDebug() << "[Tglobals] Adding sound transposition one octave down due to bass clef conversion."; - A->transposition = -12; + bool transposeTuning = scoreParams->clef == Tclef::Bass_F_8down; + if (scoreParams->clef == Tclef::Bass_F_8down) { + // This is clear: old Nootka had dropped down bass clef, now there is just bass clef + // so clef is changed and transposition is added + qDebug() << "[Tglobals] Dropped bass clef detected. Converting to ordinary bass clef."; + scoreParams->clef = Tclef::Bass_F; + if (audioParams->transposition == 0) { + qDebug() << "[Tglobals] Adding sound transposition one octave down due to bass clef conversion."; + audioParams->transposition = -12; + } + } + // But in-between versions (1.5 to 1.7.0) bass clef might have low notes and so tuning. + // -12 is note B in contra octave (-2) and it is lowest note achieve on the Nootka score + // So try to fix that here, with hope there are no false positives + if (transposeTuning || (scoreParams->clef != Tclef::Tenor_C && loNote().chromatic() < -12)) { + qDebug() << "[Tglobals] Tuning transposed one octave up to fit score capability."; + m_tune->riseOctaveUp(); // As long as we are transposing all strings the same step, string order doesn't change. } - } - // But in-between versions (1.5 to 1.7.0) bass clef might have low notes and so tuning. - // -12 is note B in contra octave (-2) and it is lowest note achieve on the Nootka score - // So try to fix that here, with hope there are no false positives - if (transposeTuning || (S->clef != Tclef::Tenor_C && loNote().chromatic() < -12)) { - qDebug() << "[Tglobals] Tuning transposed one octave up to fit score capability."; - m_tune->riseOctaveUp(); // As long as we are transposing all strings the same step, string order doesn't change. - } - } - -void Tglobals::setTune(const Ttune& t) { - delete m_tune; - m_tune = new Ttune(t.name, t.str(1), t.str(2), t.str(3), t.str(4), t.str(5), t.str(6), t.type()); - m_tuneObject->setTune(m_tune); - // creating array with guitar strings ordered by their pitch height - char openStr[6]; - for (int i = 0; i < 6; i++) { - m_order[i] = i; - if (m_tune->str(i + 1).note() != 0) - openStr[i] = m_tune->str(i + 1).chromatic(); - else // empty note - not such string - openStr[i] = -120; // make it lowest - } - int i = 4; - while (i > -1) { - for (int j = i; j < 5 && openStr[m_order[j]] < openStr[m_order[j + 1]]; j++) { - char tmp = m_order[j]; - m_order[j] = m_order[j + 1]; - m_order[j + 1] = tmp; +void Tglobals::setTune(const Ttune &t) +{ + delete m_tune; + m_tune = new Ttune(t.name, t.str(1), t.str(2), t.str(3), t.str(4), t.str(5), t.str(6), t.type()); + m_tuneObject->setTune(m_tune); + // creating array with guitar strings ordered by their pitch height + char openStr[6]; + for (int i = 0; i < 6; i++) { + m_order[i] = i; + if (m_tune->str(i + 1).note() != 0) + openStr[i] = m_tune->str(i + 1).chromatic(); + else // empty note - not such string + openStr[i] = -120; // make it lowest } - i--; - } - emit tuningChanged(); + int i = 4; + while (i > -1) { + for (int j = i; j < 5 && openStr[m_order[j]] < openStr[m_order[j + 1]]; j++) { + char tmp = m_order[j]; + m_order[j] = m_order[j + 1]; + m_order[j + 1] = tmp; + } + i--; + } + emit tuningChanged(); } - -Tnote Tglobals::hiString() { - return m_tune->str(m_order[0] + 1); +Tnote Tglobals::hiString() +{ + return m_tune->str(m_order[0] + 1); } - -Tnote Tglobals::loString() { +Tnote Tglobals::loString() +{ return m_tune->str(m_order[m_tune->stringNr() - 1] + 1); } +Tnote::EnameStyle Tglobals::getSolfegeStyle() +{ + Tnote::EnameStyle solStyle = Tnote::e_italiano_Si; + QString ll = lang; + if (ll.isEmpty()) { + QLocale loc; // default locale (QLocale::setDefault()) grabs local LANG variable in contrary to QLocale::system() which not + ll = loc.name(); + } + if (ll.contains(QLatin1String("ru"))) + solStyle = Tnote::e_russian_Ci; + return solStyle; +} -Tnote::EnameStyle Tglobals::getSolfegeStyle() { - Tnote::EnameStyle solStyle = Tnote::e_italiano_Si; - QString ll = lang; - if (ll.isEmpty()) { - QLocale loc; // default locale (QLocale::setDefault()) grabs local LANG variable in contrary to QLocale::system() which not - ll = loc.name(); - } - if (ll.contains(QLatin1String("ru"))) - solStyle = Tnote::e_russian_Ci; - return solStyle; -} - - -QString Tglobals::lastXmlDir() const { return S->lastXmlDir; } -void Tglobals::setLastXmlDir(const QString& lastXml) { S->lastXmlDir = lastXml; } - - - -void Tglobals::storeSettings(QSettings* cfg) { - cfg->setValue(QStringLiteral("geometry"), m_geometry); - cfg->setValue(QStringLiteral("scale"), m_guiScale); - - cfg->beginGroup(QLatin1String("common")); - cfg->setValue(QStringLiteral("isFirstRun"), isFirstRun); - cfg->setValue(QStringLiteral("useAnimations"), m_useAnimations); - cfg->setValue(QStringLiteral("showHints"), m_showHints); - cfg->setValue(QStringLiteral("doubleAccidentals"), S->doubleAccidentalsEnabled); - cfg->setValue(QStringLiteral("showEnaharmonicNotes"), S->showEnharmNotes); - cfg->setValue(QStringLiteral("enharmonicNotesColor"), S->enharmNotesColor); - cfg->setValue(QStringLiteral("is7thNote_B"), S->seventhIs_B); - cfg->setValue(QStringLiteral("language"), lang); - cfg->endGroup(); - - cfg->beginGroup(QLatin1String("score")); - cfg->setValue(QStringLiteral("keySignature"), S->keySignatureEnabled); - cfg->setValue(QStringLiteral("keyName"), S->showKeySignName); - cfg->setValue(QStringLiteral("nameStyleInKey"), static_cast<int>(S->nameStyleInKeySign)); - QString majS, minS; - if (S->majKeyNameSufix != TkeySignature::majorSufixTxt()) - majS = S->majKeyNameSufix; - else - majS.clear(); // default suffixes are reset to be translatable in next run - cfg->setValue(QStringLiteral("majorKeysSufix"), majS); - if (S->minKeyNameSufix != TkeySignature::minorSufixTxt()) - minS = S->minKeyNameSufix; - else - minS.clear(); - cfg->setValue(QStringLiteral("minorKeysSufix"), minS); - cfg->setValue(QStringLiteral("pointerColor"), S->pointerColor); - cfg->setValue(QStringLiteral("clef"), static_cast<int>(S->clef)); - cfg->setValue(QStringLiteral("rhythmsEnabled"), S->rhythmsEnabled); - cfg->setValue(QStringLiteral("singleNoteMode"), S->isSingleNoteMode); - cfg->setValue(QStringLiteral("tempo"), S->tempo); - cfg->setValue(QStringLiteral("scoreScale"), S->scoreScale); - cfg->setValue(QStringLiteral("lastXmlDir"), S->lastXmlDir); - cfg->setValue(QStringLiteral("scientificOctaves"), S->scientificOctaves); - cfg->endGroup(); - - cfg->beginGroup(QLatin1String("noteName")); - cfg->setValue(QStringLiteral("nameStyle"), static_cast<int>(S->nameStyleInNoteName)); - cfg->setValue(QStringLiteral("octaveInName"), S->octaveInNoteNameFormat); - cfg->setValue(QStringLiteral("solfegeStyle"), S->solfegeStyle); - cfg->setValue(QStringLiteral("namesOnScore"), S->namesOnScore ); - cfg->setValue(QStringLiteral("namesColor"), S->nameColor); - cfg->endGroup(); - - cfg->beginGroup(QLatin1String("guitar")); - cfg->setValue(QStringLiteral("instrument"), static_cast<int>(m_instrument.type())); - cfg->setValue(QStringLiteral("fretNumber"), static_cast<int>(GfretsNumber)); - cfg->setValue(QStringLiteral("rightHanded"), GisRightHanded); - cfg->setValue(QStringLiteral("showOtherPos"), GshowOtherPos); - cfg->setValue(QStringLiteral("fingerColor"), GfingerColor); - cfg->setValue(QStringLiteral("selectedColor"), GselectedColor); - cfg->setValue(QStringLiteral("tune"), QVariant::fromValue(*Gtune())); - cfg->setValue(QStringLiteral("flatsPrefered"), GpreferFlats); - cfg->setValue(QStringLiteral("dotsOnFrets"), GmarkedFrets); - cfg->endGroup(); - - cfg->beginGroup(QLatin1String("exam")); - cfg->setValue(QStringLiteral("questionColor"), EquestionColor); - cfg->setValue(QStringLiteral("answerColor"), EanswerColor); - cfg->setValue(QStringLiteral("notBadColor"), EnotBadColor); - cfg->setValue(QStringLiteral("autoNextQuest"), E->autoNextQuest); - cfg->setValue(QStringLiteral("repeatIncorrect"), E->repeatIncorrect); - cfg->setValue(QStringLiteral("showCorrected"), E->showCorrected); - cfg->setValue(QStringLiteral("expertsAnswerEnable"), E->expertsAnswerEnable); - cfg->setValue(QStringLiteral("studentName"), E->studentName); - cfg->setValue(QStringLiteral("examsDir"), E->examsDir); - cfg->setValue(QStringLiteral("levelsDir"), E->levelsDir); - cfg->setValue(QStringLiteral("closeWithoutConfirm"), E->closeWithoutConfirm); - cfg->setValue(QStringLiteral("mistakePreview"), E->mistakePreview); - cfg->setValue(QStringLiteral("correctPreview"), E->correctPreview); - cfg->setValue(QStringLiteral("questionDelay"), E->questionDelay); - cfg->setValue(QStringLiteral("suggestExam"), E->suggestExam); - cfg->setValue(QStringLiteral("afterMistake"), static_cast<int>(E->afterMistake)); - cfg->setValue(QStringLiteral("showNameOfAnswered"), E->showNameOfAnswered); - cfg->setValue(QStringLiteral("showWrongPlayed"), E->showWrongPlayed); - cfg->setValue(QStringLiteral("waitForCorrect"), E->waitForCorrect); - cfg->setValue(QStringLiteral("askAboutExpert"), E->askAboutExpert); - cfg->setValue(QStringLiteral("showHelpOnStart"), E->showHelpOnStart); - cfg->endGroup(); - - cfg->beginGroup(QLatin1String("sound")); - cfg->setValue(QStringLiteral("JACKorASIO"), A->JACKorASIO); - cfg->setValue(QStringLiteral("outSoundEnabled"), A->OUTenabled); - cfg->setValue(QStringLiteral("outDeviceName"), A->OUTdevName); - cfg->setValue(QStringLiteral("midiEnabled"), A->midiEnabled); - cfg->setValue(QStringLiteral("midiPortName"), A->midiPortName); - cfg->setValue(QStringLiteral("midiInstrumentNr"), static_cast<int>(A->midiInstrNr)); - cfg->setValue(QStringLiteral("audioInstrumentNr"), static_cast<int>(A->audioInstrNr)); - cfg->setValue(QStringLiteral("inSoundEnabled"), A->INenabled); - cfg->setValue(QStringLiteral("inDeviceName"), A->INdevName); - cfg->setValue(QStringLiteral("detectionMethod"), A->detectMethod); - cfg->setValue(QStringLiteral("minimalVolume"), A->minimalVol); - cfg->setValue(QStringLiteral("minimalDuration"), A->minDuration); - cfg->setValue(QStringLiteral("midAfreq"), A->midAfreq); - cfg->setValue(QStringLiteral("intonation"), A->intonation); - cfg->setValue(QStringLiteral("forwardInput"), A->forwardInput); - cfg->setValue(QStringLiteral("equalLoudness"), A->equalLoudness); - cfg->setValue(QStringLiteral("transposition"), A->transposition); - cfg->setValue(QStringLiteral("stoppedByUser"), A->stoppedByUser); - cfg->setValue(QStringLiteral("audibleMetro"), A->audibleMetro); - cfg->setValue(QStringLiteral("countBefore"), A->countBefore); - cfg->setValue(QStringLiteral("quantization"), A->quantization); -#if !defined (Q_OS_ANDROID) - cfg->setValue(QStringLiteral("showNotesDiff"), m_showNotesDiff); - cfg->setValue(QLatin1String("dumpPath"), A->dumpPath); -#endif - cfg->endGroup(); +QString Tglobals::lastXmlDir() const +{ + return scoreParams->lastXmlDir; +} +void Tglobals::setLastXmlDir(const QString &lastXml) +{ + scoreParams->lastXmlDir = lastXml; +} -#if defined (Q_OS_ANDROID) - cfg->setValue(QStringLiteral("keepScreenOn"), m_keepScreenOn); - cfg->setValue(QStringLiteral("disableRotation"), m_disableRotation); - cfg->setValue(QStringLiteral("fullScreen"), m_fullScreen); - cfg->setValue(QStringLiteral("touchStopsSniff"), m_touchStopsSniff); +void Tglobals::storeSettings(QSettings *cfg) +{ + cfg->setValue(QStringLiteral("geometry"), m_geometry); + cfg->setValue(QStringLiteral("scale"), m_guiScale); + + cfg->beginGroup(QLatin1String("common")); + cfg->setValue(QStringLiteral("isFirstRun"), isFirstRun); + cfg->setValue(QStringLiteral("useAnimations"), m_useAnimations); + cfg->setValue(QStringLiteral("showHints"), m_showHints); + cfg->setValue(QStringLiteral("doubleAccidentals"), scoreParams->doubleAccidentalsEnabled); + cfg->setValue(QStringLiteral("showEnaharmonicNotes"), scoreParams->showEnharmNotes); + cfg->setValue(QStringLiteral("enharmonicNotesColor"), scoreParams->enharmNotesColor); + cfg->setValue(QStringLiteral("is7thNote_B"), scoreParams->seventhIs_B); + cfg->setValue(QStringLiteral("language"), lang); + cfg->endGroup(); + + cfg->beginGroup(QLatin1String("score")); + cfg->setValue(QStringLiteral("keySignature"), scoreParams->keySignatureEnabled); + cfg->setValue(QStringLiteral("keyName"), scoreParams->showKeySignName); + cfg->setValue(QStringLiteral("nameStyleInKey"), static_cast<int>(scoreParams->nameStyleInKeySign)); + QString majS, minS; + if (scoreParams->majKeyNameSufix != TkeySignature::majorSufixTxt()) + majS = scoreParams->majKeyNameSufix; + else + majS.clear(); // default suffixes are reset to be translatable in next run + cfg->setValue(QStringLiteral("majorKeysSufix"), majS); + if (scoreParams->minKeyNameSufix != TkeySignature::minorSufixTxt()) + minS = scoreParams->minKeyNameSufix; + else + minS.clear(); + cfg->setValue(QStringLiteral("minorKeysSufix"), minS); + cfg->setValue(QStringLiteral("pointerColor"), scoreParams->pointerColor); + cfg->setValue(QStringLiteral("clef"), static_cast<int>(scoreParams->clef)); + cfg->setValue(QStringLiteral("rhythmsEnabled"), scoreParams->rhythmsEnabled); + cfg->setValue(QStringLiteral("singleNoteMode"), scoreParams->isSingleNoteMode); + cfg->setValue(QStringLiteral("tempo"), scoreParams->tempo); + cfg->setValue(QStringLiteral("scoreScale"), scoreParams->scoreScale); + cfg->setValue(QStringLiteral("lastXmlDir"), scoreParams->lastXmlDir); + cfg->setValue(QStringLiteral("scientificOctaves"), scoreParams->scientificOctaves); + cfg->endGroup(); + + cfg->beginGroup(QLatin1String("noteName")); + cfg->setValue(QStringLiteral("nameStyle"), static_cast<int>(scoreParams->nameStyleInNoteName)); + cfg->setValue(QStringLiteral("octaveInName"), scoreParams->octaveInNoteNameFormat); + cfg->setValue(QStringLiteral("solfegeStyle"), scoreParams->solfegeStyle); + cfg->setValue(QStringLiteral("namesOnScore"), scoreParams->namesOnScore); + cfg->setValue(QStringLiteral("namesColor"), scoreParams->nameColor); + cfg->endGroup(); + + cfg->beginGroup(QLatin1String("guitar")); + cfg->setValue(QStringLiteral("instrument"), static_cast<int>(m_instrument.type())); + cfg->setValue(QStringLiteral("fretNumber"), static_cast<int>(GfretsNumber)); + cfg->setValue(QStringLiteral("rightHanded"), GisRightHanded); + cfg->setValue(QStringLiteral("showOtherPos"), GshowOtherPos); + cfg->setValue(QStringLiteral("fingerColor"), GfingerColor); + cfg->setValue(QStringLiteral("selectedColor"), GselectedColor); + cfg->setValue(QStringLiteral("tune"), QVariant::fromValue(*Gtune())); + cfg->setValue(QStringLiteral("flatsPrefered"), GpreferFlats); + cfg->setValue(QStringLiteral("dotsOnFrets"), GmarkedFrets); + cfg->endGroup(); + + cfg->beginGroup(QLatin1String("exam")); + cfg->setValue(QStringLiteral("questionColor"), EquestionColor); + cfg->setValue(QStringLiteral("answerColor"), EanswerColor); + cfg->setValue(QStringLiteral("notBadColor"), EnotBadColor); + cfg->setValue(QStringLiteral("autoNextQuest"), examParams->autoNextQuest); + cfg->setValue(QStringLiteral("repeatIncorrect"), examParams->repeatIncorrect); + cfg->setValue(QStringLiteral("showCorrected"), examParams->showCorrected); + cfg->setValue(QStringLiteral("expertsAnswerEnable"), examParams->expertsAnswerEnable); + cfg->setValue(QStringLiteral("studentName"), examParams->studentName); + cfg->setValue(QStringLiteral("examsDir"), examParams->examsDir); + cfg->setValue(QStringLiteral("levelsDir"), examParams->levelsDir); + cfg->setValue(QStringLiteral("closeWithoutConfirm"), examParams->closeWithoutConfirm); + cfg->setValue(QStringLiteral("mistakePreview"), examParams->mistakePreview); + cfg->setValue(QStringLiteral("correctPreview"), examParams->correctPreview); + cfg->setValue(QStringLiteral("questionDelay"), examParams->questionDelay); + cfg->setValue(QStringLiteral("suggestExam"), examParams->suggestExam); + cfg->setValue(QStringLiteral("afterMistake"), static_cast<int>(examParams->afterMistake)); + cfg->setValue(QStringLiteral("showNameOfAnswered"), examParams->showNameOfAnswered); + cfg->setValue(QStringLiteral("showWrongPlayed"), examParams->showWrongPlayed); + cfg->setValue(QStringLiteral("waitForCorrect"), examParams->waitForCorrect); + cfg->setValue(QStringLiteral("askAboutExpert"), examParams->askAboutExpert); + cfg->setValue(QStringLiteral("showHelpOnStart"), examParams->showHelpOnStart); + cfg->endGroup(); + + cfg->beginGroup(QLatin1String("sound")); + cfg->setValue(QStringLiteral("JACKorASIO"), audioParams->JACKorASIO); + cfg->setValue(QStringLiteral("outSoundEnabled"), audioParams->OUTenabled); + cfg->setValue(QStringLiteral("outDeviceName"), audioParams->OUTdevName); + cfg->setValue(QStringLiteral("midiEnabled"), audioParams->midiEnabled); + cfg->setValue(QStringLiteral("midiPortName"), audioParams->midiPortName); + cfg->setValue(QStringLiteral("midiInstrumentNr"), static_cast<int>(audioParams->midiInstrNr)); + cfg->setValue(QStringLiteral("audioInstrumentNr"), static_cast<int>(audioParams->audioInstrNr)); + cfg->setValue(QStringLiteral("inSoundEnabled"), audioParams->INenabled); + cfg->setValue(QStringLiteral("inDeviceName"), audioParams->INdevName); + cfg->setValue(QStringLiteral("detectionMethod"), audioParams->detectMethod); + cfg->setValue(QStringLiteral("minimalVolume"), audioParams->minimalVol); + cfg->setValue(QStringLiteral("minimalDuration"), audioParams->minDuration); + cfg->setValue(QStringLiteral("midAfreq"), audioParams->midAfreq); + cfg->setValue(QStringLiteral("intonation"), audioParams->intonation); + cfg->setValue(QStringLiteral("forwardInput"), audioParams->forwardInput); + cfg->setValue(QStringLiteral("equalLoudness"), audioParams->equalLoudness); + cfg->setValue(QStringLiteral("transposition"), audioParams->transposition); + cfg->setValue(QStringLiteral("stoppedByUser"), audioParams->stoppedByUser); + cfg->setValue(QStringLiteral("audibleMetro"), audioParams->audibleMetro); + cfg->setValue(QStringLiteral("countBefore"), audioParams->countBefore); + cfg->setValue(QStringLiteral("quantization"), audioParams->quantization); +#if !defined(Q_OS_ANDROID) + cfg->setValue(QStringLiteral("showNotesDiff"), m_showNotesDiff); + cfg->setValue(QLatin1String("dumpPath"), audioParams->dumpPath); #endif + cfg->endGroup(); +#if defined(Q_OS_ANDROID) + cfg->setValue(QStringLiteral("keepScreenOn"), m_keepScreenOn); + cfg->setValue(QStringLiteral("disableRotation"), m_disableRotation); + cfg->setValue(QStringLiteral("fullScreen"), m_fullScreen); + cfg->setValue(QStringLiteral("touchStopsSniff"), m_touchStopsSniff); +#endif } +#if defined(Q_OS_ANDROID) -#if defined (Q_OS_ANDROID) - -void Tglobals::setDisableRotation(bool disRot) { - if (disRot != m_disableRotation) { - Tandroid::disableRotation(disRot); - m_disableRotation = disRot; - } +void Tglobals::setDisableRotation(bool disRot) +{ + if (disRot != m_disableRotation) { + Tandroid::disableRotation(disRot); + m_disableRotation = disRot; + } } - -void Tglobals::keepScreenOn(bool on) { - if (on != m_keepScreenOn) { - Tandroid::keepScreenOn(on); - m_keepScreenOn = on; - } +void Tglobals::keepScreenOn(bool on) +{ + if (on != m_keepScreenOn) { + Tandroid::keepScreenOn(on); + m_keepScreenOn = on; + } } #endif - diff --git a/src/libs/core/tglobals.h b/src/libs/core/tglobals.h index 9cb49c79195035e083664c4f6590ab7e47163c92..245b6d4dfbd8bef72e1b6534072f368e3cf343da 100644 --- a/src/libs/core/tglobals.h +++ b/src/libs/core/tglobals.h @@ -19,15 +19,13 @@ #ifndef TGLOBALS_H #define TGLOBALS_H - -#include <nootkacoreglobal.h> #include <QtCore/qobject.h> -#include <QtCore/qvariant.h> #include <QtCore/qrect.h> +#include <QtCore/qvariant.h> #include <QtGui/qcolor.h> -#include <music/tnote.h> #include <music/tinstrument.h> - +#include <music/tnote.h> +#include <nootkacoreglobal.h> class TlayoutParams; class TscoreParams; @@ -37,484 +35,477 @@ class TexamParams; class TaudioParams; class TtuneObject; - -#define GLOB Tglobals::instance() - +#define GLOB Tglobals::instance() class NOOTKACORE_EXPORT Tglobals : public QObject { - Q_OBJECT - - Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry NOTIFY fakeSignal) - Q_PROPERTY(bool useAnimations READ useAnimations WRITE setUseAnimations NOTIFY useAnimationsChanged) - Q_PROPERTY(bool showHints READ showHints WRITE setShowHints NOTIFY showHintsChanged) - Q_PROPERTY(QString lang READ getLang WRITE setLang) - Q_PROPERTY(qreal scale READ guiScale WRITE setGuiScale NOTIFY guiScaleChanged) - /* Score switches */ - Q_PROPERTY(bool showEnharmNotes READ showEnharmNotes WRITE setShowEnharmNotes NOTIFY showEnharmNotesChanged) - Q_PROPERTY(QColor enharmNoteColor READ getEnharmNoteColor WRITE setEnharmNoteColor) - Q_PROPERTY(QColor noteCursorColor READ getNoteCursorColor WRITE setNoteCursorColor NOTIFY noteCursorColorChanged) - Q_PROPERTY(bool singleNoteMode READ isSingleNote WRITE setSingleNote NOTIFY singleNoteModeChanged) - Q_PROPERTY(bool enableDoubleAccids READ enableDoubleAccids WRITE setEnableDoubleAccids NOTIFY enableDoubleAccidsChanged) - Q_PROPERTY(bool keySignatureEnabled READ keySignatureEnabled WRITE setKeySignatureEnabled NOTIFY enableKeySignatureChanged) - Q_PROPERTY(bool showKeyName READ showKeyName WRITE setShowKeyName NOTIFY showKeyNameChanged) - Q_PROPERTY(QString majorKeyNameSufix READ majorKeyNameSufix WRITE setMajorKeyNameSufix) - Q_PROPERTY(QString minorKeyNameSufix READ minorKeyNameSufix WRITE setMinorKeyNameSufix) - Q_PROPERTY(int keyNameStyle READ keyNameStyle WRITE setKeyNameStyle) - Q_PROPERTY(bool rhythmsEnabled READ rhythmsEnabled WRITE setRhythmsEnabled NOTIFY rhythmsEnabledChanged) - - Q_PROPERTY(int clefType READ clefType WRITE setClefType NOTIFY clefTypeChanged) - - /* Note name switches */ - Q_PROPERTY(bool namesOnScore READ namesOnScore WRITE setNamesOnScore NOTIFY namesOnScoreChanged) - Q_PROPERTY(int noteNameStyle READ noteNameStyle WRITE setNoteNameStyle NOTIFY noteNameStyleChanged) - Q_PROPERTY(bool seventhIsB READ seventhIsB WRITE setSeventhIsB NOTIFY seventhIsBChanged) - Q_PROPERTY(QColor nameColor READ nameColor WRITE setNameColor NOTIFY nameColorChanged) - Q_PROPERTY(bool scientificOctaves READ scientificOctaves WRITE setScientificOctaves NOTIFY noteNameStyleChanged) - - /* Instrument switches */ - Q_PROPERTY(Tinstrument instrument READ instrument NOTIFY instrumentChanged) - Q_PROPERTY(TtuneObject* tuning READ tuning NOTIFY tuningChanged) - Q_PROPERTY(qreal transposition READ transposition WRITE setTransposition NOTIFY transpositionChanged) - - Q_PROPERTY(QColor fingerColor READ fingerColor WRITE setFingerColor NOTIFY fingerColorChanged) - Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged) - Q_PROPERTY(bool preferFlats READ preferFlats WRITE setPreferFlats NOTIFY fakeSignal) - Q_PROPERTY(int fretNumber READ fretNumber NOTIFY guitarParamsChanged) - Q_PROPERTY(bool showOtherPos READ showOtherPos WRITE setShowOtherPos) - Q_PROPERTY(QString markedFrets READ markedFrets WRITE setMarkedFrets); - - /* Sound switches */ - Q_PROPERTY(bool audioInEnabled READ audioInEnabled WRITE setAudioInEnabled) - Q_PROPERTY(int audioInstrument READ audioInstrument WRITE setAudioInstrument) - Q_PROPERTY(QString inDevName READ inDevName WRITE setInDevName) - Q_PROPERTY(qreal minDuration READ minDuration WRITE setMinDuration) - Q_PROPERTY(qreal minVolume READ minVolume WRITE setMinVolume NOTIFY minVolumeChanged) - Q_PROPERTY(int detectionMethod READ detectionMethod WRITE setDetectionMethod) - Q_PROPERTY(bool useFilter READ useFilter WRITE setUseFilter) - Q_PROPERTY(bool audioOutEnabled READ audioOutEnabled WRITE setAudioOutEnabled) - Q_PROPERTY(QString outDevName READ outDevName WRITE setOutDevName) - Q_PROPERTY(bool forwardInput READ forwardInput WRITE setForwardInput) - Q_PROPERTY(int midAfreq READ midAfreq WRITE setMidAfreq NOTIFY midAfreqChanged) - Q_PROPERTY(bool JACKorASIO READ JACKorASIO WRITE setJACKorASIO) - Q_PROPERTY(bool showNotesDiff READ showNotesDiff WRITE setShowNotesDiff NOTIFY showNotesDiffChanged) - - /* Exam */ - Q_PROPERTY(bool isExam READ isExam WRITE setIsExam NOTIFY isExamChanged) - Q_PROPERTY(QString student READ student WRITE setStudent NOTIFY studentChanged) - Q_PROPERTY(QColor correctColor READ correctColor WRITE setCorrectColor NOTIFY correctColorChanged) - Q_PROPERTY(QColor notBadColor READ notBadColor WRITE setNotBadColor NOTIFY notBadColorChanged) - Q_PROPERTY(QColor wrongColor READ wrongColor WRITE setWrongColor NOTIFY wrongColorChanged) - Q_PROPERTY(bool autoNextQuestion READ autoNextQuestion WRITE setAutoNextQuestion) - Q_PROPERTY(bool expertAnswers READ expertAnswers WRITE setExpertAnswers) - Q_PROPERTY(bool correctAnswers READ correctAnswers WRITE setCorrectAnswers) - Q_PROPERTY(bool repeatIncorect READ repeatIncorect WRITE setRepeatIncorect) - Q_PROPERTY(bool closeWithoutConfirm READ closeWithoutConfirm WRITE setCloseWithoutConfirm) - Q_PROPERTY(bool suggestExam READ suggestExam WRITE setSuggestExam) - Q_PROPERTY(bool extraNames READ extraNames WRITE setExtraNames) - Q_PROPERTY(bool showWrongPlayed READ showWrongPlayed WRITE setShowWrongPlayed) - Q_PROPERTY(bool waitForCorrect READ waitForCorrect WRITE setWaitForCorrect) - Q_PROPERTY(QString examsDir READ examsDir NOTIFY fakeSignal) + Q_OBJECT + + Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry NOTIFY fakeSignal) + Q_PROPERTY(bool useAnimations READ useAnimations WRITE setUseAnimations NOTIFY useAnimationsChanged) + Q_PROPERTY(bool showHints READ showHints WRITE setShowHints NOTIFY showHintsChanged) + Q_PROPERTY(QString lang READ getLang WRITE setLang) + Q_PROPERTY(qreal scale READ guiScale WRITE setGuiScale NOTIFY guiScaleChanged) + /* Score switches */ + Q_PROPERTY(bool showEnharmNotes READ showEnharmNotes WRITE setShowEnharmNotes NOTIFY showEnharmNotesChanged) + Q_PROPERTY(QColor enharmNoteColor READ getEnharmNoteColor WRITE setEnharmNoteColor) + Q_PROPERTY(QColor noteCursorColor READ getNoteCursorColor WRITE setNoteCursorColor NOTIFY noteCursorColorChanged) + Q_PROPERTY(bool singleNoteMode READ isSingleNote WRITE setSingleNote NOTIFY singleNoteModeChanged) + Q_PROPERTY(bool enableDoubleAccids READ enableDoubleAccids WRITE setEnableDoubleAccids NOTIFY enableDoubleAccidsChanged) + Q_PROPERTY(bool keySignatureEnabled READ keySignatureEnabled WRITE setKeySignatureEnabled NOTIFY enableKeySignatureChanged) + Q_PROPERTY(bool showKeyName READ showKeyName WRITE setShowKeyName NOTIFY showKeyNameChanged) + Q_PROPERTY(QString majorKeyNameSufix READ majorKeyNameSufix WRITE setMajorKeyNameSufix) + Q_PROPERTY(QString minorKeyNameSufix READ minorKeyNameSufix WRITE setMinorKeyNameSufix) + Q_PROPERTY(int keyNameStyle READ keyNameStyle WRITE setKeyNameStyle) + Q_PROPERTY(bool rhythmsEnabled READ rhythmsEnabled WRITE setRhythmsEnabled NOTIFY rhythmsEnabledChanged) + + Q_PROPERTY(int clefType READ clefType WRITE setClefType NOTIFY clefTypeChanged) + + /* Note name switches */ + Q_PROPERTY(bool namesOnScore READ namesOnScore WRITE setNamesOnScore NOTIFY namesOnScoreChanged) + Q_PROPERTY(int noteNameStyle READ noteNameStyle WRITE setNoteNameStyle NOTIFY noteNameStyleChanged) + Q_PROPERTY(bool seventhIsB READ seventhIsB WRITE setSeventhIsB NOTIFY seventhIsBChanged) + Q_PROPERTY(QColor nameColor READ nameColor WRITE setNameColor NOTIFY nameColorChanged) + Q_PROPERTY(bool scientificOctaves READ scientificOctaves WRITE setScientificOctaves NOTIFY noteNameStyleChanged) + + /* Instrument switches */ + Q_PROPERTY(Tinstrument instrument READ instrument NOTIFY instrumentChanged) + Q_PROPERTY(TtuneObject *tuning READ tuning NOTIFY tuningChanged) + Q_PROPERTY(qreal transposition READ transposition WRITE setTransposition NOTIFY transpositionChanged) + + Q_PROPERTY(QColor fingerColor READ fingerColor WRITE setFingerColor NOTIFY fingerColorChanged) + Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged) + Q_PROPERTY(bool preferFlats READ preferFlats WRITE setPreferFlats NOTIFY fakeSignal) + Q_PROPERTY(int fretNumber READ fretNumber NOTIFY guitarParamsChanged) + Q_PROPERTY(bool showOtherPos READ showOtherPos WRITE setShowOtherPos) + Q_PROPERTY(QString markedFrets READ markedFrets WRITE setMarkedFrets); + + /* Sound switches */ + Q_PROPERTY(bool audioInEnabled READ audioInEnabled WRITE setAudioInEnabled) + Q_PROPERTY(int audioInstrument READ audioInstrument WRITE setAudioInstrument) + Q_PROPERTY(QString inDevName READ inDevName WRITE setInDevName) + Q_PROPERTY(qreal minDuration READ minDuration WRITE setMinDuration) + Q_PROPERTY(qreal minVolume READ minVolume WRITE setMinVolume NOTIFY minVolumeChanged) + Q_PROPERTY(int detectionMethod READ detectionMethod WRITE setDetectionMethod) + Q_PROPERTY(bool useFilter READ useFilter WRITE setUseFilter) + Q_PROPERTY(bool audioOutEnabled READ audioOutEnabled WRITE setAudioOutEnabled) + Q_PROPERTY(QString outDevName READ outDevName WRITE setOutDevName) + Q_PROPERTY(bool forwardInput READ forwardInput WRITE setForwardInput) + Q_PROPERTY(int midAfreq READ midAfreq WRITE setMidAfreq NOTIFY midAfreqChanged) + Q_PROPERTY(bool JACKorASIO READ JACKorASIO WRITE setJACKorASIO) + Q_PROPERTY(bool showNotesDiff READ showNotesDiff WRITE setShowNotesDiff NOTIFY showNotesDiffChanged) + + /* Exam */ + Q_PROPERTY(bool isExam READ isExam WRITE setIsExam NOTIFY isExamChanged) + Q_PROPERTY(QString student READ student WRITE setStudent NOTIFY studentChanged) + Q_PROPERTY(QColor correctColor READ correctColor WRITE setCorrectColor NOTIFY correctColorChanged) + Q_PROPERTY(QColor notBadColor READ notBadColor WRITE setNotBadColor NOTIFY notBadColorChanged) + Q_PROPERTY(QColor wrongColor READ wrongColor WRITE setWrongColor NOTIFY wrongColorChanged) + Q_PROPERTY(bool autoNextQuestion READ autoNextQuestion WRITE setAutoNextQuestion) + Q_PROPERTY(bool expertAnswers READ expertAnswers WRITE setExpertAnswers) + Q_PROPERTY(bool correctAnswers READ correctAnswers WRITE setCorrectAnswers) + Q_PROPERTY(bool repeatIncorect READ repeatIncorect WRITE setRepeatIncorect) + Q_PROPERTY(bool closeWithoutConfirm READ closeWithoutConfirm WRITE setCloseWithoutConfirm) + Q_PROPERTY(bool suggestExam READ suggestExam WRITE setSuggestExam) + Q_PROPERTY(bool extraNames READ extraNames WRITE setExtraNames) + Q_PROPERTY(bool showWrongPlayed READ showWrongPlayed WRITE setShowWrongPlayed) + Q_PROPERTY(bool waitForCorrect READ waitForCorrect WRITE setWaitForCorrect) + Q_PROPERTY(QString examsDir READ examsDir NOTIFY fakeSignal) public: + /** If @p true, setting are loaded from temporary config file */ + Tglobals(QObject *parent = nullptr); + ~Tglobals() override; - /** If @p true, setting are loaded from temporary config file */ - Tglobals(QObject* parent = nullptr); - ~Tglobals() override; - - /** - * Instance (single for whole Nootka) of Tglobals class. - * Also available through @p GLOB macro - */ - static Tglobals* instance() { return m_instance; } + /** + * Instance (single for whole Nootka) of Tglobals class. + * Also available through @p GLOB macro + */ + static Tglobals *instance() { return m_instance; } - static qreal pitchOfFreq(int freq); + static qreal pitchOfFreq(int freq); - /** Geometry of main Nootka window */ - QRect geometry() const { return m_geometry; } - void setGeometry(const QRect& g) { m_geometry = g; } + /** Geometry of main Nootka window */ + QRect geometry() const { return m_geometry; } + void setGeometry(const QRect &g) { m_geometry = g; } - /** To show GUI animations. */ - bool useAnimations() const { return m_useAnimations; } - void setUseAnimations(bool use); + /** To show GUI animations. */ + bool useAnimations() const { return m_useAnimations; } + void setUseAnimations(bool use); - bool showHints() const { return m_showHints; } - void setShowHints(bool showH); + bool showHints() const { return m_showHints; } + void setShowHints(bool showH); - QString getLang() const { return lang; } - void setLang(const QString& l) { lang = l; } + QString getLang() const { return lang; } + void setLang(const QString &l) { lang = l; } - qreal guiScale() const { return m_guiScale; } - void setGuiScale(qreal sc); + qreal guiScale() const { return m_guiScale; } + void setGuiScale(qreal sc); - /* ------------------ Score switches ------------------ */ - bool showEnharmNotes() const; - void setShowEnharmNotes(bool showEnharm); + /* ------------------ Score switches ------------------ */ + bool showEnharmNotes() const; + void setShowEnharmNotes(bool showEnharm); - QColor getEnharmNoteColor() const; - void setEnharmNoteColor(const QColor& c); + QColor getEnharmNoteColor() const; + void setEnharmNoteColor(const QColor &c); - QColor getNoteCursorColor() const; - void setNoteCursorColor(const QColor& c); + QColor getNoteCursorColor() const; + void setNoteCursorColor(const QColor &c); - bool isSingleNote() const; - void setSingleNote(bool sn); + bool isSingleNote() const; + void setSingleNote(bool sn); - bool namesOnScore() const; - void setNamesOnScore(bool showNames); + bool namesOnScore() const; + void setNamesOnScore(bool showNames); - QColor nameColor() const; - void setNameColor(const QColor& nameC); + QColor nameColor() const; + void setNameColor(const QColor &nameC); - bool enableDoubleAccids() const; - void setEnableDoubleAccids(bool dblAcc); + bool enableDoubleAccids() const; + void setEnableDoubleAccids(bool dblAcc); - bool keySignatureEnabled() const; - void setKeySignatureEnabled(bool enKey); + bool keySignatureEnabled() const; + void setKeySignatureEnabled(bool enKey); - int clefType() const; - void setClefType(int clType); + int clefType() const; + void setClefType(int clType); - bool showKeyName() const; - void setShowKeyName(bool showKey); + bool showKeyName() const; + void setShowKeyName(bool showKey); - QString majorKeyNameSufix() const; - void setMajorKeyNameSufix(const QString& mkns); + QString majorKeyNameSufix() const; + void setMajorKeyNameSufix(const QString &mkns); - QString minorKeyNameSufix() const; - void setMinorKeyNameSufix(const QString& mkns); + QString minorKeyNameSufix() const; + void setMinorKeyNameSufix(const QString &mkns); - int keyNameStyle() const; - void setKeyNameStyle(int keyStyle); + int keyNameStyle() const; + void setKeyNameStyle(int keyStyle); - bool rhythmsEnabled() const; - void setRhythmsEnabled(bool enR); + bool rhythmsEnabled() const; + void setRhythmsEnabled(bool enR); - /* ------------------ Note name switches ------------------ */ - bool seventhIsB() const; - void setSeventhIsB(bool isB); + /* ------------------ Note name switches ------------------ */ + bool seventhIsB() const; + void setSeventhIsB(bool isB); - int noteNameStyle() const; - void setNoteNameStyle(int nameStyle); + int noteNameStyle() const; + void setNoteNameStyle(int nameStyle); - /** - * As long as it corresponds with note name style, any change invokes @p noteNameStyleChanged() signal - */ - bool scientificOctaves() const; - void setScientificOctaves(bool sciO); + /** + * As long as it corresponds with note name style, any change invokes @p noteNameStyleChanged() signal + */ + bool scientificOctaves() const; + void setScientificOctaves(bool sciO); - /* ------------------ Instrument switches ------------------ */ - QColor fingerColor() const { return GfingerColor; } - void setFingerColor(const QColor& fc); + /* ------------------ Instrument switches ------------------ */ + QColor fingerColor() const { return GfingerColor; } + void setFingerColor(const QColor &fc); - QColor selectedColor() const { return GselectedColor; } - void setSelectedColor(const QColor& sc); + QColor selectedColor() const { return GselectedColor; } + void setSelectedColor(const QColor &sc); - bool preferFlats() const { return GpreferFlats; } - void setPreferFlats(bool prefFlat) { GpreferFlats = prefFlat; } + bool preferFlats() const { return GpreferFlats; } + void setPreferFlats(bool prefFlat) { GpreferFlats = prefFlat; } - bool showOtherPos() const { return GshowOtherPos; } - void setShowOtherPos(bool show) { GshowOtherPos = show; } + bool showOtherPos() const { return GshowOtherPos; } + void setShowOtherPos(bool show) { GshowOtherPos = show; } - int transposition() const; - void setTransposition(int t); + int transposition() const; + void setTransposition(int t); - QString markedFrets() const; - void setMarkedFrets(const QString& frets); + QString markedFrets() const; + void setMarkedFrets(const QString &frets); - /* ------------------ Sound switches ------------------ */ - bool audioInEnabled() const; - void setAudioInEnabled(bool inEnabled); + /* ------------------ Sound switches ------------------ */ + bool audioInEnabled() const; + void setAudioInEnabled(bool inEnabled); - int audioInstrument() const; - void setAudioInstrument(int ai); + int audioInstrument() const; + void setAudioInstrument(int ai); - QString inDevName() const; - void setInDevName(const QString& inName); + QString inDevName() const; + void setInDevName(const QString &inName); - qreal minDuration() const; - void setMinDuration(qreal md); + qreal minDuration() const; + void setMinDuration(qreal md); - qreal minVolume() const; - void setMinVolume(qreal mv); + qreal minVolume() const; + void setMinVolume(qreal mv); - int detectionMethod() const; - void setDetectionMethod(int m); + int detectionMethod() const; + void setDetectionMethod(int m); - bool useFilter() const; - void setUseFilter(bool use); + bool useFilter() const; + void setUseFilter(bool use); - bool audioOutEnabled() const; - void setAudioOutEnabled(bool outEnabled); + bool audioOutEnabled() const; + void setAudioOutEnabled(bool outEnabled); - QString outDevName() const; - void setOutDevName(const QString& odn); + QString outDevName() const; + void setOutDevName(const QString &odn); - bool forwardInput() const; - void setForwardInput(bool fi); + bool forwardInput() const; + void setForwardInput(bool fi); - int midAfreq() const; - void setMidAfreq(int midA); + int midAfreq() const; + void setMidAfreq(int midA); - bool JACKorASIO() const; - void setJACKorASIO(bool JorA); + bool JACKorASIO() const; + void setJACKorASIO(bool JorA); - bool showNotesDiff() const { return m_showNotesDiff; } - void setShowNotesDiff(bool notesDiff); + bool showNotesDiff() const { return m_showNotesDiff; } + void setShowNotesDiff(bool notesDiff); - /* ------------------ Exam switches ------------------ */ - bool isExam() const { return m_isExam; } - void setIsExam(bool is); + /* ------------------ Exam switches ------------------ */ + bool isExam() const { return m_isExam; } + void setIsExam(bool is); - QString student() const; - void setStudent(const QString& st); + QString student() const; + void setStudent(const QString &st); - QColor correctColor() const { return EanswerColor; } - void setCorrectColor(const QColor& c); + QColor correctColor() const { return EanswerColor; } + void setCorrectColor(const QColor &c); - QColor notBadColor() const { return EnotBadColor; } - void setNotBadColor(const QColor& c); + QColor notBadColor() const { return EnotBadColor; } + void setNotBadColor(const QColor &c); - QColor wrongColor() const { return EquestionColor; } - void setWrongColor(const QColor& c); + QColor wrongColor() const { return EquestionColor; } + void setWrongColor(const QColor &c); - bool autoNextQuestion() const; - void setAutoNextQuestion(bool autoNext); + bool autoNextQuestion() const; + void setAutoNextQuestion(bool autoNext); - bool expertAnswers() const; - void setExpertAnswers(bool expertA); + bool expertAnswers() const; + void setExpertAnswers(bool expertA); - bool correctAnswers() const; - void setCorrectAnswers(bool corrAnsw); + bool correctAnswers() const; + void setCorrectAnswers(bool corrAnsw); - bool repeatIncorect() const; - void setRepeatIncorect(bool repInCorr); + bool repeatIncorect() const; + void setRepeatIncorect(bool repInCorr); - bool closeWithoutConfirm() const; - void setCloseWithoutConfirm(bool closeNoConf); + bool closeWithoutConfirm() const; + void setCloseWithoutConfirm(bool closeNoConf); - bool suggestExam() const; - void setSuggestExam(bool suggest); + bool suggestExam() const; + void setSuggestExam(bool suggest); - bool extraNames() const; - void setExtraNames(bool extraN); + bool extraNames() const; + void setExtraNames(bool extraN); - bool showWrongPlayed() const; - void setShowWrongPlayed(bool wrongPlayed); + bool showWrongPlayed() const; + void setShowWrongPlayed(bool wrongPlayed); - bool waitForCorrect() const; - void setWaitForCorrect(bool waitFor); + bool waitForCorrect() const; + void setWaitForCorrect(bool waitFor); - QString examsDir() const; + QString examsDir() const; - /** - * Reads configure option @p key from 'gotIt' group - * and returns it if exists or @p retVal instead. - */ - Q_INVOKABLE bool gotIt(const QString& key, bool retVal) const; + /** + * Reads configure option @p key from 'gotIt' group + * and returns it if exists or @p retVal instead. + */ + Q_INVOKABLE bool gotIt(const QString &key, bool retVal) const; - /** - * Sets configure option @p key in 'gotIt' group - */ - Q_INVOKABLE void setGotIt(const QString& key, bool val); + /** + * Sets configure option @p key in 'gotIt' group + */ + Q_INVOKABLE void setGotIt(const QString &key, bool val); -#if defined (Q_OS_ANDROID) - Q_INVOKABLE void keepScreenOn(bool on); - Q_INVOKABLE bool isKeepScreenOn() { return m_keepScreenOn; } - Q_INVOKABLE bool disableRotation() const{ return m_disableRotation; } - Q_INVOKABLE bool fullScreen() const { return m_fullScreen; } - Q_INVOKABLE void setDisableRotation(bool disRot); - Q_INVOKABLE void setFullScreen(bool fs) { m_fullScreen = fs; } - Q_INVOKABLE bool touchStopsSniff() const { return m_touchStopsSniff;} - Q_INVOKABLE void setTouchStopsSniff(bool touchSniff) { m_touchStopsSniff = touchSniff; } +#if defined(Q_OS_ANDROID) + Q_INVOKABLE void keepScreenOn(bool on); + Q_INVOKABLE bool isKeepScreenOn() { return m_keepScreenOn; } + Q_INVOKABLE bool disableRotation() const { return m_disableRotation; } + Q_INVOKABLE bool fullScreen() const { return m_fullScreen; } + Q_INVOKABLE void setDisableRotation(bool disRot); + Q_INVOKABLE void setFullScreen(bool fs) { m_fullScreen = fs; } + Q_INVOKABLE bool touchStopsSniff() const { return m_touchStopsSniff; } + Q_INVOKABLE void setTouchStopsSniff(bool touchSniff) { m_touchStopsSniff = touchSniff; } #endif - /** - * Updates key signature names according to name style and major/minor suffixes. - * Emits @p keyNameChanged() to inform MainScore.qml - */ - Q_INVOKABLE void updateKeySignatureNames(); - - /** - * This method return application install path - path from where Nootka was started. - */ - static QString getInstPath(const QString& appInstPath); - static QString& path; /**< Reference to Tpath::main - Nootka resources path */ - - /** - * Saves settings stored in @p cfg - */ - void storeSettings(QSettings* cfg); - - /** - * Loads Nootka settings from file to @p cfg - */ - void loadSettings(QSettings* cfg); - - QString version; - bool isFirstRun; /**< to show or skip first run wizard*/ - QString lang; /**< Application language. If empty - selected from system info */ - QSettings *config; /**< Pointer to @p QSettings instance of Nootka */ - - /** - * @p TRUE if Nootka is launched first time - with wizard at very beginning. - * In contrary to @p isFirstRun which returns state of variable, - * @p wasFirstRun() returns @p QSettings state, so it changes only after app quit. - */ - Q_INVOKABLE bool wasFirstRun() const; - - /** - * Emits delayed signal @p newerVersion() when any Nootka file - * created with newer version was detected. - */ - void warnAboutNewerVersion(const QString& fileName); - - /** - * Let's have a convention: - * globals settings for @class TnoteName will started from 'N' letter - * for @class TscoreWidget and @class TscoreWidgetSimple - * and for guitar (@class TfingerBoard) from 'G' letter. - * For sound and exam there are pointers to appropirate classes with theirs parameters. - */ - - //============ score settings ============================================================= - TscoreParams *S; /**< Score parameters */ - + /** + * Updates key signature names according to name style and major/minor suffixes. + * Emits @p keyNameChanged() to inform MainScore.qml + */ + Q_INVOKABLE void updateKeySignatureNames(); + + /** + * This method return application install path - path from where Nootka was started. + */ + static QString getInstPath(const QString &appInstPath); + static QString &path; /**< Reference to Tpath::main - Nootka resources path */ + + /** + * Saves settings stored in @p cfg + */ + void storeSettings(QSettings *cfg); + + /** + * Loads Nootka settings from file to @p cfg + */ + void loadSettings(QSettings *cfg); + + QString version; + bool isFirstRun; /**< to show or skip first run wizard*/ + QString lang; /**< Application language. If empty - selected from system info */ + QSettings *config; /**< Pointer to @p QSettings instance of Nootka */ + + /** + * @p TRUE if Nootka is launched first time - with wizard at very beginning. + * In contrary to @p isFirstRun which returns state of variable, + * @p wasFirstRun() returns @p QSettings state, so it changes only after app quit. + */ + Q_INVOKABLE bool wasFirstRun() const; + + /** + * Emits delayed signal @p newerVersion() when any Nootka file + * created with newer version was detected. + */ + void warnAboutNewerVersion(const QString &fileName); + + /** + * Let's have a convention: + * globals settings for @class TnoteName will started from 'N' letter + * for @class TscoreWidget and @class TscoreWidgetSimple + * and for guitar (@class TfingerBoard) from 'G' letter. + * For sound and exam there are pointers to appropirate classes with theirs parameters. + */ + + //============ score settings ============================================================= + TscoreParams *const scoreParams; /**< Score parameters */ /** Guessing solfege name style from current locale setting. F.e.: ru is e_russian_Ci */ - Tnote::EnameStyle getSolfegeStyle(); - - Q_INVOKABLE QString lastXmlDir() const; - void setLastXmlDir(const QString& lastXml); - - //============ guitar settings ============================================================= - Tinstrument instrument() { return m_instrument; } - Q_INVOKABLE void setInstrument(Tinstrument::Etype t); - Q_INVOKABLE void setInstrument(int t) { setInstrument(t >= 0 && t < INSTR_COUNT ? static_cast<Tinstrument::Etype>(t) : Tinstrument::NoInstrument); } - int fretNumber() const { return GfretsNumber; } - unsigned GfretsNumber; /**< default 19 */ - bool GisRightHanded; /**< default true */ - bool GshowOtherPos; /**< Shows other possibilities of note (sound) on the fretboard (default true) */ - QColor GfingerColor; /**< rules the same like in S->enharmNotesColor */ - QColor GselectedColor; - /** - * Guitar params like guitar type or fret/string numbers, fret marks - affects guitar repainting. - * Keep their change in single call then. - */ - Q_INVOKABLE void setGuitarParams(int fretNr, const Ttune& tun /*TODO , bool rightHand */); - - /** - * Actual tune of the guitar also with information about strings number - * available by Ttune::stringsNr() - */ - Ttune *Gtune() { return m_tune; } - Q_INVOKABLE void setTune(const Ttune &t); - TtuneObject* tuning() { return m_tuneObject; } - Q_INVOKABLE int stringNumber(); - - /** - * It returns real string number (0 - 5) when @param strNr - * is sorted number from highest (0) to lowest (5) - */ - qint8 strOrder(qint8 strNr) { return m_order[strNr]; } - - /** - * Returns the highest (usually first - 0) string. - */ - Tnote hiString(); - - /** - * Returns the lowest (usually last) string - */ - Tnote loString(); - - /** - * The highest available note in current tune with current fret number - * or highest note in the non guitar instrument scale. - */ - Q_INVOKABLE Tnote hiNote() { return Tnote(hiString().chromatic() + GfretsNumber); } - - /** - * The same as @p loString() - * or lowest note in the non guitar instrument scale. - */ - Q_INVOKABLE Tnote loNote() { return loString(); } - - /** - * It says witch accidentals are preferred while user clicks guitar - * and note is calculated. Default are sharps (@p false) - */ - bool GpreferFlats; - QList<QVariant> GmarkedFrets; - - - TexamParams *E; /**< Exam parameters */ - QColor EquestionColor; /**< red */ - QColor EanswerColor; /**< green */ - QColor EnotBadColor; /**< #ff8000 (orange) */ - - TaudioParams *A; /**< Audio parameters */ - TlayoutParams *L; /**< Main window Layout params. */ - - /** - * Returns system user name (log-in name) - */ - static QString systemUserName(); + Tnote::EnameStyle getSolfegeStyle(); + + Q_INVOKABLE QString lastXmlDir() const; + void setLastXmlDir(const QString &lastXml); + + //============ guitar settings ============================================================= + Tinstrument instrument() { return m_instrument; } + Q_INVOKABLE void setInstrument(Tinstrument::Etype t); + Q_INVOKABLE void setInstrument(int t) { setInstrument(t >= 0 && t < INSTR_COUNT ? static_cast<Tinstrument::Etype>(t) : Tinstrument::NoInstrument); } + int fretNumber() const { return GfretsNumber; } + unsigned GfretsNumber; /**< default 19 */ + bool GisRightHanded; /**< default true */ + bool GshowOtherPos; /**< Shows other possibilities of note (sound) on the fretboard (default true) */ + QColor GfingerColor; /**< rules the same like in S->enharmNotesColor */ + QColor GselectedColor; + /** + * Guitar params like guitar type or fret/string numbers, fret marks - affects guitar repainting. + * Keep their change in single call then. + */ + Q_INVOKABLE void setGuitarParams(int fretNr, const Ttune &tun /*TODO , bool rightHand */); + + /** + * Actual tune of the guitar also with information about strings number + * available by Ttune::stringsNr() + */ + Ttune *Gtune() { return m_tune; } + Q_INVOKABLE void setTune(const Ttune &t); + TtuneObject *tuning() { return m_tuneObject; } + Q_INVOKABLE int stringNumber(); + + /** + * It returns real string number (0 - 5) when @param strNr + * is sorted number from highest (0) to lowest (5) + */ + qint8 strOrder(qint8 strNr) { return m_order[strNr]; } + + /** + * Returns the highest (usually first - 0) string. + */ + Tnote hiString(); + + /** + * Returns the lowest (usually last) string + */ + Tnote loString(); + + /** + * The highest available note in current tune with current fret number + * or highest note in the non guitar instrument scale. + */ + Q_INVOKABLE Tnote hiNote() { return Tnote(hiString().chromatic() + GfretsNumber); } + + /** + * The same as @p loString() + * or lowest note in the non guitar instrument scale. + */ + Q_INVOKABLE Tnote loNote() { return loString(); } + + /** + * It says witch accidentals are preferred while user clicks guitar + * and note is calculated. Default are sharps (@p false) + */ + bool GpreferFlats; + QList<QVariant> GmarkedFrets; + + TexamParams *const examParams; /**< Exam parameters */ + QColor EquestionColor; /**< red */ + QColor EanswerColor; /**< green */ + QColor EnotBadColor; /**< #ff8000 (orange) */ + + TaudioParams *const audioParams; /**< Audio parameters */ + TlayoutParams *const layoutParams; /**< Main window Layout parameter */ + + /** + * Returns system user name (log-in name) + */ + static QString systemUserName(); signals: - void useAnimationsChanged(); - void showHintsChanged(); - void guiScaleChanged(); - void noteCursorColorChanged(); - void singleNoteModeChanged(); - void showEnharmNotesChanged(); - void namesOnScoreChanged(); - void enableDoubleAccidsChanged(); - void enableKeySignatureChanged(); - void clefTypeChanged(); - void nameColorChanged(); - void noteNameStyleChanged(); - void seventhIsBChanged(); - void showKeyNameChanged(); - void keyNameChanged(); - void rhythmsEnabledChanged(); - void instrumentChanged(); - void tuningChanged(); - void fingerColorChanged(); - void selectedColorChanged(); - void guitarParamsChanged(); - void transpositionChanged(); - void minVolumeChanged(); - void isExamChanged(); - void studentChanged(); - void correctColorChanged(); - void notBadColorChanged(); - void wrongColorChanged(); /**< To silence warning about non-NOTIFYable properties */ - void midAfreqChanged(); - void showNotesDiffChanged(); - void fakeSignal(); - void wantOpenFile(const QString& fileName); /**< Emitted when Nootka starts with an argument which is exam or level file */ - void newerVersion(const QString& fileName); /**< When exam or level file was created with newer version than the current one */ + void useAnimationsChanged(); + void showHintsChanged(); + void guiScaleChanged(); + void noteCursorColorChanged(); + void singleNoteModeChanged(); + void showEnharmNotesChanged(); + void namesOnScoreChanged(); + void enableDoubleAccidsChanged(); + void enableKeySignatureChanged(); + void clefTypeChanged(); + void nameColorChanged(); + void noteNameStyleChanged(); + void seventhIsBChanged(); + void showKeyNameChanged(); + void keyNameChanged(); + void rhythmsEnabledChanged(); + void instrumentChanged(); + void tuningChanged(); + void fingerColorChanged(); + void selectedColorChanged(); + void guitarParamsChanged(); + void transpositionChanged(); + void minVolumeChanged(); + void isExamChanged(); + void studentChanged(); + void correctColorChanged(); + void notBadColorChanged(); + void wrongColorChanged(); /**< To silence warning about non-NOTIFYable properties */ + void midAfreqChanged(); + void showNotesDiffChanged(); + void fakeSignal(); + void wantOpenFile(const QString &fileName); /**< Emitted when Nootka starts with an argument which is exam or level file */ + void newerVersion(const QString &fileName); /**< When exam or level file was created with newer version than the current one */ private: - static Tglobals *m_instance; - Ttune *m_tune; /**< current guitar tune */ - TtuneObject *m_tuneObject; - qint8 m_order[6]; /**< Strings order is determined in @param setTune() method */ - QRect m_geometry; - bool m_useAnimations; - bool m_showHints; - Tinstrument m_instrument; - qreal m_guiScale; - bool m_isExam = false; - bool m_showNotesDiff; -#if defined (Q_OS_ANDROID) - bool m_keepScreenOn; - bool m_disableRotation; - bool m_fullScreen; - bool m_touchStopsSniff; + static Tglobals *m_instance; + Ttune *m_tune; /**< current guitar tune */ + TtuneObject *m_tuneObject; + qint8 m_order[6]; /**< Strings order is determined in @param setTune() method */ + QRect m_geometry; + bool m_useAnimations; + bool m_showHints; + Tinstrument m_instrument; + qreal m_guiScale; + bool m_isExam = false; + bool m_showNotesDiff; +#if defined(Q_OS_ANDROID) + bool m_keepScreenOn; + bool m_disableRotation; + bool m_fullScreen; + bool m_touchStopsSniff; #endif - }; #endif // TGLOBALS_H - diff --git a/src/libs/core/tinitcorelib.cpp b/src/libs/core/tinitcorelib.cpp index 0e757d0f3a291edc5aec48a9e7b8a01d993d19ca..1d89e352c74bea80ff40b4b6d454c12c6b25d6ab 100644 --- a/src/libs/core/tinitcorelib.cpp +++ b/src/libs/core/tinitcorelib.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2014-2021 by Tomasz Bojczuk * + * Copyright (C) 2014-2025 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -17,121 +17,119 @@ ***************************************************************************/ #include "tinitcorelib.h" -#include "tglobals.h" -#include "music/ttune.h" #include "music/tkeysignature.h" +#include "music/ttune.h" +#include "qtr.h" #include "tcolor.h" -#include "tscoreparams.h" +#include "tglobals.h" #include "tpath.h" -#include "qtr.h" -#include <QtGui/qguiapplication.h> -#include <QtGui/qpalette.h> -#include <QtCore/qtranslator.h> -#include <QtCore/qlibraryinfo.h> -#include <QtGui/qfontdatabase.h> +#include "tscoreparams.h" #include <QtCore/qdebug.h> #include <QtCore/qdir.h> -#if defined (Q_OS_ANDROID) - #include "Android/tandroid.h" +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qtranslator.h> +#include <QtGui/qfontdatabase.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qpalette.h> +#if defined(Q_OS_ANDROID) +#include "Android/tandroid.h" #endif +bool initCoreLibrary() +{ + if (GLOB == nullptr) { + qDebug() << "[tinitcorelib] Tglobals was not created. Construct it first!"; + return false; + } -bool initCoreLibrary() { - if (GLOB == nullptr) { - qDebug() << "[tinitcorelib] Tglobals was not created. Construct it first!"; - return false; - } - - // dummy - just to consume that by lupdate - QGuiApplication::translate("about translator", "translator", - "Do not translate this, just put in 'translator comment field' your data: translator name, " - "translator e-mail (optional) and translator site (optional)"); + // dummy - just to consume that by lupdate + QGuiApplication::translate("about translator", + "translator", + "Do not translate this, just put in 'translator comment field' your data: translator name, " + "translator e-mail (optional) and translator site (optional)"); - Trhythm::initialize(); - Tcolor::setShadow(qApp->palette()); + Trhythm::initialize(); + Tcolor::setShadow(qApp->palette()); #if defined(Q_OS_MAC) - QDir dir(qApp->applicationDirPath()); - dir.cdUp(); - qApp->addLibraryPath(dir.path() + QLatin1String("/Frameworks")); + QDir dir(qApp->applicationDirPath()); + dir.cdUp(); + qApp->addLibraryPath(dir.path() + QLatin1String("/Frameworks")); #else - qApp->addLibraryPath(qApp->applicationDirPath()); + qApp->addLibraryPath(qApp->applicationDirPath()); #endif #if defined(Q_OS_LINUX) - QDir dir(qApp->applicationDirPath()); - dir.cdUp(); - qApp->addLibraryPath(dir.path() + QLatin1String("/lib/nootka")); + QDir dir(qApp->applicationDirPath()); + dir.cdUp(); + qApp->addLibraryPath(dir.path() + QLatin1String("/lib/nootka")); #endif -#if defined (Q_OS_ANDROID) - qApp->addLibraryPath(qApp->applicationDirPath()); - Tandroid::keepScreenOn(GLOB->isKeepScreenOn()); - Tandroid::disableRotation(GLOB->disableRotation()); +#if defined(Q_OS_ANDROID) + qApp->addLibraryPath(qApp->applicationDirPath()); + Tandroid::keepScreenOn(GLOB->isKeepScreenOn()); + Tandroid::disableRotation(GLOB->disableRotation()); #endif - return true; + return true; } - -void prepareTranslations(QGuiApplication* a, QTranslator& qt, QTranslator& noo) { - if (!GLOB) - return; - -#if defined (Q_OS_ANDROID) - QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().language() : QLocale(GLOB->lang).language()); -#elif defined (Q_OS_MAC) - QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().uiLanguages().first() : GLOB->lang); -#elif defined (Q_OS_WIN) - QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().language() : QLocale(GLOB->lang).language()); +void prepareTranslations(QGuiApplication *a, QTranslator &qt, QTranslator &noo) +{ + if (!GLOB) + return; + +#if defined(Q_OS_ANDROID) + QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().language() : QLocale(GLOB->lang).language()); +#elif defined(Q_OS_MAC) + QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().uiLanguages().first() : GLOB->lang); +#elif defined(Q_OS_WIN) + QLocale loc(GLOB->lang.isEmpty() ? QLocale::system().language() : QLocale(GLOB->lang).language()); #else - QLocale loc(QLocale(GLOB->lang.isEmpty() ? qgetenv("LANG") : GLOB->lang).language(), - QLocale(GLOB->lang.isEmpty() ? qgetenv("LANG") : GLOB->lang).country()); + QLocale loc(QLocale(GLOB->lang.isEmpty() ? qgetenv("LANG") : GLOB->lang).language(), + QLocale(GLOB->lang.isEmpty() ? qgetenv("LANG") : GLOB->lang).country()); #endif - QLocale::setDefault(loc); + QLocale::setDefault(loc); - QString translationsPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); -#if !defined (Q_OS_LINUX) || defined (Q_OS_ANDROID) - translationsPath = Tpath::lang(); + QString translationsPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); +#if !defined(Q_OS_LINUX) || defined(Q_OS_ANDROID) + translationsPath = Tpath::lang(); #endif - if (qt.load(loc, QStringLiteral("qtbase_"), QString(), translationsPath)) - a->installTranslator(&qt); - - noo.load(loc, QStringLiteral("nootka_"), QString(), Tpath::lang()); - a->installTranslator(&noo); - - if (GLOB->isFirstRun) { - GLOB->setSeventhIsB(qTR("Notation", "b").toLower() == QLatin1String("b")); - if (GLOB->S->seventhIs_B) - GLOB->S->nameStyleInNoteName = Tnote::e_english_Bb; - else - GLOB->S->nameStyleInNoteName = Tnote::e_norsk_Hb; - if (qTR("Notation", "letters").toLower() == QLatin1String("solfege")) { - if (loc.language() == QLocale::Russian) - GLOB->S->nameStyleInNoteName = Tnote::e_russian_Ci; - else - GLOB->S->nameStyleInNoteName = Tnote::e_italiano_Si; + if (qt.load(loc, QStringLiteral("qtbase_"), QString(), translationsPath)) + a->installTranslator(&qt); + + noo.load(loc, QStringLiteral("nootka_"), QString(), Tpath::lang()); + a->installTranslator(&noo); + + if (GLOB->isFirstRun) { + GLOB->setSeventhIsB(qTR("Notation", "b").toLower() == QLatin1String("b")); + if (GLOB->scoreParams->seventhIs_B) + GLOB->scoreParams->nameStyleInNoteName = Tnote::e_english_Bb; + else + GLOB->scoreParams->nameStyleInNoteName = Tnote::e_norsk_Hb; + if (qTR("Notation", "letters").toLower() == QLatin1String("solfege")) { + if (loc.language() == QLocale::Russian) + GLOB->scoreParams->nameStyleInNoteName = Tnote::e_russian_Ci; + else + GLOB->scoreParams->nameStyleInNoteName = Tnote::e_italiano_Si; + } + Tnote::defaultStyle = GLOB->scoreParams->nameStyleInNoteName; } - Tnote::defaultStyle = GLOB->S->nameStyleInNoteName; - } - TkeySignature::setNameStyle(GLOB->S->nameStyleInKeySign, GLOB->S->majKeyNameSufix, GLOB->S->minKeyNameSufix); - Ttune::prepareDefinedTunes(); + TkeySignature::setNameStyle(GLOB->scoreParams->nameStyleInKeySign, GLOB->scoreParams->majKeyNameSufix, GLOB->scoreParams->minKeyNameSufix); + Ttune::prepareDefinedTunes(); } - -bool loadNootkaFont(QGuiApplication* a) { - Q_UNUSED(a) - QFontDatabase fd; - int fid = fd.addApplicationFont(Tpath::main + QLatin1String("fonts/nootka.ttf")); - int fid2 = fd.addApplicationFont(Tpath::main + QLatin1String("fonts/Scorek.otf")); - if (fid == -1 || fid2 == -1) { - qDebug() << "Cannot load Nootka fonts!\nDid you forget to invoke:\n" - << "make runinplace\nor\nninja runinplace\nafter the first compilation?\n"; - return false; - } - return true; +bool loadNootkaFont(QGuiApplication *a) +{ + Q_UNUSED(a) + QFontDatabase fd; + int fid = fd.addApplicationFont(Tpath::main + QLatin1String("fonts/nootka.ttf")); + int fid2 = fd.addApplicationFont(Tpath::main + QLatin1String("fonts/Scorek.otf")); + if (fid == -1 || fid2 == -1) { + qDebug() << "Cannot load Nootka fonts!\nDid you forget to invoke:\n" + << "make runinplace\nor\nninja runinplace\nafter the first compilation?\n"; + return false; + } + return true; } - - - diff --git a/src/libs/core/tinitcorelib.h b/src/libs/core/tinitcorelib.h old mode 100755 new mode 100644 index 005e13d915c3be08bc0398fb8350c85471d2e8d5..6c7109ed746d5e9dcd7408eb4a6aef25cab3ff7f --- a/src/libs/core/tinitcorelib.h +++ b/src/libs/core/tinitcorelib.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2014-2019 by Tomasz Bojczuk * + * Copyright (C) 2014-2025 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -21,28 +21,26 @@ #include "nootkacoreglobal.h" - class QTranslator; class QGuiApplication; - - /** - * Initializes static values in library: - * - adds tuning definitions - * - adds TpushButton colors - */ +/** + * Initializes static values in library: + * - adds tuning definitions + * - adds TpushButton colors + */ NOOTKACORE_EXPORT bool initCoreLibrary(); - /** - * Loads translations files for appropriate language (system or user preferred) - * Translator object has to be created before. - */ -NOOTKACORE_EXPORT void prepareTranslations(QGuiApplication* a, QTranslator& qt, QTranslator& noo); - - /** - * Checks nootka.ttf file and loads it. Returns true if successful. - * libNootkaCore has to be initialized first by initCoreLibrary() - */ -NOOTKACORE_EXPORT bool loadNootkaFont(QGuiApplication* a); +/** + * Loads translations files for appropriate language (system or user preferred) + * Translator object has to be created before. + */ +NOOTKACORE_EXPORT void prepareTranslations(QGuiApplication *a, QTranslator &qt, QTranslator &noo); + +/** + * Checks nootka.ttf file and loads it. Returns true if successful. + * libNootkaCore has to be initialized first by initCoreLibrary() + */ +NOOTKACORE_EXPORT bool loadNootkaFont(QGuiApplication *a); #endif // TINITCORELIB_H diff --git a/src/libs/core/tlayoutparams.h b/src/libs/core/tlayoutparams.h index cfcc4eccfdc0fbbd8645fb94d4ba6039b6c34b4a..62d6bcdd38f753ab511757a7969fc157a6b8b1f2 100644 --- a/src/libs/core/tlayoutparams.h +++ b/src/libs/core/tlayoutparams.h @@ -19,19 +19,16 @@ #include "nootkacoreglobal.h" #include <Qt> - /** * Layout parameters of main Nootka window. */ class NOOTKACORE_EXPORT TlayoutParams { - public: - bool toolBarAutoHide; /** When tool bar appears only after mouse action - @p FALSE by default */ - Qt::ToolButtonStyle iconTextOnToolBar; /** How text and icons are displayed on tool bar - text under icons by default. */ - - bool hintsBarEnabled; /** Is hints bar visible - @p TRUE by default */ - bool soundViewEnabled; /** Is sound view visible - @p TRUE by default */ - bool guitarEnabled; /** Is guitar visible - @p TRUE by default */ + bool toolBarAutoHide; /** When tool bar appears only after mouse action - @p FALSE by default */ + Qt::ToolButtonStyle iconTextOnToolBar; /** How text and icons are displayed on tool bar - text under icons by default. */ + bool hintsBarEnabled; /** Is hints bar visible - @p TRUE by default */ + bool soundViewEnabled; /** Is sound view visible - @p TRUE by default */ + bool guitarEnabled; /** Is guitar visible - @p TRUE by default */ }; diff --git a/src/libs/core/tmtr.cpp b/src/libs/core/tmtr.cpp index a33557f5d567179896feb23b112e44455162f1bf..b58fd2f9b78254664ad2fd1d7dca43851139853b 100644 --- a/src/libs/core/tmtr.cpp +++ b/src/libs/core/tmtr.cpp @@ -23,12 +23,12 @@ int Tmtr::m_shortScreenSide = 480; int Tmtr::m_longScreenSide = 600; QFont Tmtr::systemFont = QFont(); - -void Tmtr::init(QApplication *a) { - systemFont = a->font(); - if (a->screens().size()) { - m_shortScreenSide = qMin(a->screens().at(0)->geometry().height(), a->screens().at(0)->geometry().width()); - m_longScreenSide = qMax(a->screens().at(0)->geometry().height(), a->screens().at(0)->geometry().width()); - m_fingerPixels = a->screens().at(0)->geometry().width() / (a->screens().at(0)->physicalSize().width() / 7.0); - } +void Tmtr::init(QApplication *a) +{ + systemFont = a->font(); + if (a->screens().size()) { + m_shortScreenSide = qMin(a->screens().at(0)->geometry().height(), a->screens().at(0)->geometry().width()); + m_longScreenSide = qMax(a->screens().at(0)->geometry().height(), a->screens().at(0)->geometry().width()); + m_fingerPixels = a->screens().at(0)->geometry().width() / (a->screens().at(0)->physicalSize().width() / 7.0); + } } diff --git a/src/libs/core/tmtr.h b/src/libs/core/tmtr.h index 15c764cf2c860d4f373b12ea4f8d3b18e4a785ef..eea248cea40c36d10f048130e7b26e69ca2d0ab9 100644 --- a/src/libs/core/tmtr.h +++ b/src/libs/core/tmtr.h @@ -19,12 +19,10 @@ #ifndef TMTR_H #define TMTR_H - #include "nootkacoreglobal.h" -#include <QtWidgets/qapplication.h> -#include <QtGui/qscreen.h> #include <QtGui/qfont.h> - +#include <QtGui/qscreen.h> +#include <QtWidgets/qapplication.h> /** * These are static measurement units depend on available screen size, @@ -33,28 +31,27 @@ */ class NOOTKACORE_EXPORT Tmtr { - public: - static void init(QApplication* a); + static void init(QApplication *a); - /** size of finger (1 cm) in pixels */ - static int fingerPixels() { return m_fingerPixels; } + /** size of finger (1 cm) in pixels */ + static int fingerPixels() { return m_fingerPixels; } - /** height or width of a screen (less of them) */ - static int shortScreenSide() { return m_shortScreenSide; } + /** height or width of a screen (less of them) */ + static int shortScreenSide() { return m_shortScreenSide; } - /** height or width of a screen (bigger of them) */ - static int longScreenSide() { return m_longScreenSide; } + /** height or width of a screen (bigger of them) */ + static int longScreenSide() { return m_longScreenSide; } - static int screenWidth() { return qApp->screens().first()->geometry().width(); } - static int screenHeight() { return qApp->screens().first()->geometry().height(); } + static int screenWidth() { return qApp->screens().first()->geometry().width(); } + static int screenHeight() { return qApp->screens().first()->geometry().height(); } - static QFont systemFont; + static QFont systemFont; private: - static int m_fingerPixels; - static int m_shortScreenSide; - static int m_longScreenSide; + static int m_fingerPixels; + static int m_shortScreenSide; + static int m_longScreenSide; }; #endif // TMTR_H diff --git a/src/libs/core/tnoofont.cpp b/src/libs/core/tnoofont.cpp index 7fc28ea5a8f745d385324d89a1808fcc439c1cce..65f68af71fb6a8053c8b043ae5c9c868173b40a0 100644 --- a/src/libs/core/tnoofont.cpp +++ b/src/libs/core/tnoofont.cpp @@ -53,48 +53,41 @@ * digits [0-9] starts from 0x0180 */ - #include "tnoofont.h" #include <QtCore/qmath.h> - -TnooFont::TnooFont(int pointSize) : - QFont(QStringLiteral("nootka"), pointSize) +TnooFont::TnooFont(int pointSize) + : QFont(QStringLiteral("nootka"), pointSize) { - setPixelSize(pointSize); - setBold(false); - setWeight(50); // Normal + setPixelSize(pointSize); + setBold(false); + setWeight(50); // Normal } -//################################################################################################# -//################### STATIC ############################################ -//################################################################################################# -QString TnooFont::tag(const QString& tag, const QString& text, int fontSize, const QString& extraStyle) { - QString fSize; - if (fontSize) - fSize = QString("font-size: %1px;").arg(fontSize); - QString ex = extraStyle; - if (!extraStyle.isEmpty() && !extraStyle.endsWith(QLatin1String(";"))) - ex = extraStyle + QLatin1String(";"); - return QLatin1String("<") + tag + QLatin1String(" style=\"font-family: nootka;") + fSize + ex + QLatin1String("\">") - + text + QLatin1String("</") + tag + QLatin1String(">"); +// ################################################################################################# +// ################### STATIC ############################################ +// ################################################################################################# +QString TnooFont::tag(const QString &tag, const QString &text, int fontSize, const QString &extraStyle) +{ + QString fSize; + if (fontSize) + fSize = QString("font-size: %1px;").arg(fontSize); + QString ex = extraStyle; + if (!extraStyle.isEmpty() && !extraStyle.endsWith(QLatin1String(";"))) + ex = extraStyle + QLatin1String(";"); + return QLatin1String("<") + tag + QLatin1String(" style=\"font-family: nootka;") + fSize + ex + QLatin1String("\">") + text + QLatin1String("</") + tag + + QLatin1String(">"); } - -quint16 TnooFont::getCharFromRhythm(quint16 rhythm, bool stemUp, bool rest) { - int baseChar = 67, stemGap = 0; - if (rest) - baseChar = 0xe107; - else if (!stemUp && rhythm > 1) // stem down only if no rest and half note at least - stemGap = 6; - if (rhythm) - return baseChar + qRound(qLn(static_cast<qreal>(rhythm) / qLn(2.0))) + stemGap; - else - return 0xe193; +quint16 TnooFont::getCharFromRhythm(quint16 rhythm, bool stemUp, bool rest) +{ + int baseChar = 67, stemGap = 0; + if (rest) + baseChar = 0xe107; + else if (!stemUp && rhythm > 1) // stem down only if no rest and half note at least + stemGap = 6; + if (rhythm) + return baseChar + qRound(qLn(static_cast<qreal>(rhythm) / qLn(2.0))) + stemGap; + else + return 0xe193; } - - - - - - diff --git a/src/libs/core/tnoofont.h b/src/libs/core/tnoofont.h index e1874b88f5f4626f981dd694e4f4be31c117a146..64a23e573ac0a70395e8c16d75e61fa600698f88 100644 --- a/src/libs/core/tnoofont.h +++ b/src/libs/core/tnoofont.h @@ -19,9 +19,8 @@ #ifndef TNOOFONT_H #define TNOOFONT_H -#include <nootkacoreglobal.h> #include <QtGui/qfont.h> - +#include <nootkacoreglobal.h> /** * This is QFont with nootka.ttf initialized with size of 20 pixels @@ -29,41 +28,41 @@ */ class NOOTKACORE_EXPORT TnooFont : public QFont { - public: - TnooFont(int pointSize = 20); - - /** - * Returns given text wrapped with given HTML tag and: - * - font size (if set) - * - extra Style (if set) - * like: - * <tag style="font-family: nootka; extraStyle; font-size: XXpx;">text</tag> - */ - static QString tag(const QString& tag, const QString& text, int fontSize = 0, const QString& extraStyle = QString()); + TnooFont(int pointSize = 20); - /** - * tag() method with span tag - */ - static QString span(const QString& text, int fontSize = 0, const QString& extraStyle = QString()) { - return tag(QLatin1String("span"), text, fontSize, extraStyle); } + /** + * Returns given text wrapped with given HTML tag and: + * - font size (if set) + * - extra Style (if set) + * like: + * <tag style="font-family: nootka; extraStyle; font-size: XXpx;">text</tag> + */ + static QString tag(const QString &tag, const QString &text, int fontSize = 0, const QString &extraStyle = QString()); - /** - * Bare digits starts from UNI-0x180 in nootka.ttf - * due to glyphs of ordinary numbers are used for strings (circled). - * This method returns proper string of given digit - */ - static QString digit(quint8 d) { return (d / 10 ? QString(QChar(0x0180 + d / 10)) : QString()) + QString(QChar(0x0180 + d % 10)); } + /** + * tag() method with span tag + */ + static QString span(const QString &text, int fontSize = 0, const QString &extraStyle = QString()) + { + return tag(QLatin1String("span"), text, fontSize, extraStyle); + } - /** - * Converts rhythm value (0, 1, 2, 4 - 16) into uni-code char number in Nootka font - * 0 (no rhythm returns full note head symbol) - * set @p stemUp to false to get symbols with stem down. - * Returned characters are optimized for score (staff lines height), - * to get just note symbol use 'n' and 'N' (stem down) instead - */ - static quint16 getCharFromRhythm(quint16 rhythm, bool stemUp = true, bool rest = false); + /** + * Bare digits starts from UNI-0x180 in nootka.ttf + * due to glyphs of ordinary numbers are used for strings (circled). + * This method returns proper string of given digit + */ + static QString digit(quint8 d) { return (d / 10 ? QString(QChar(0x0180 + d / 10)) : QString()) + QString(QChar(0x0180 + d % 10)); } + /** + * Converts rhythm value (0, 1, 2, 4 - 16) into uni-code char number in Nootka font + * 0 (no rhythm returns full note head symbol) + * set @p stemUp to false to get symbols with stem down. + * Returned characters are optimized for score (staff lines height), + * to get just note symbol use 'n' and 'N' (stem down) instead + */ + static quint16 getCharFromRhythm(quint16 rhythm, bool stemUp = true, bool rest = false); }; #endif // TNOOFONT_H diff --git a/src/libs/core/tnootkaqml.cpp b/src/libs/core/tnootkaqml.cpp old mode 100755 new mode 100644 index 4e7859f11f53a51316cfb43f2fa09d1578fcbfa4..334ecba902d33b73a6665304cbb19d6530027130 --- a/src/libs/core/tnootkaqml.cpp +++ b/src/libs/core/tnootkaqml.cpp @@ -17,562 +17,577 @@ ***************************************************************************/ #include "tnootkaqml.h" -#include "qtr.h" -#include "nootkaconfig.h" -#include "tglobals.h" -#include "tpath.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/tnoteitem.h" #include "score/tstafflines.h" -#include "score/taddnoteitem.h" -#include "score/tmelodypreview.h" -#include "score/tdummychord.h" -#include "instruments/tguitarbg.h" -#include "instruments/tpianobg.h" -#include "instruments/tbandoneonbg.h" -#include "instruments/tsaxbg.h" #include "taction.h" -#include "music/ttuneobject.h" -#include "music/timportscore.h" -#include "tmtr.h" #include "tcolor.h" -#if defined (Q_OS_ANDROID) - #include <Android/tandroid.h> +#include "tglobals.h" +#include "tmtr.h" +#include "tpath.h" +#if defined(Q_OS_ANDROID) +#include <Android/tandroid.h> #endif - -#include <QtQml/qqmlapplicationengine.h> -#include <QtCore/qtimer.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qdatetime.h> #include <QtCore/qbuffer.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qfileinfo.h> #include <QtCore/qrandom.h> -#include <QtWidgets/qapplication.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::m_instance = nullptr; - - -TnootkaQML::TnootkaQML(QObject* parent) : - QObject(parent) +TnootkaQML::TnootkaQML(QObject *parent) + : QObject(parent) { - if (m_instance) { - qDebug() << "TnootkaQML instance already exists!"; - return; - } - m_instance = this; - - qRegisterMetaType<Tclef>(); - qmlRegisterUncreatableType<Tclef>("Score", 1, 0, "Tclef", QStringLiteral("You cannot create an instance of the Tclef.")); - qRegisterMetaType<Tmeter>(); - qmlRegisterUncreatableType<Tmeter>("Score", 1, 0, "Tmeter", QStringLiteral("You cannot create an instance of the Tmeter.")); - qmlRegisterUncreatableType<Trhythm>("Score", 1, 0, "Trhythm", QStringLiteral("You cannot create an instance of the Trhythm.")); - qRegisterMetaType<Trhythm>(); - - 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.")); - qRegisterMetaType<Tinstrument>(); - qmlRegisterUncreatableType<Tinstrument>("Nootka", 1, 0, "Tinstrument", QStringLiteral("You cannot create an instance of the Tinstrument.")); - qRegisterMetaType<Ttune>(); - qmlRegisterUncreatableType<Ttune>("Nootka", 1, 0, "Ttune", QStringLiteral("You cannot create an instance of the Ttune.")); - qmlRegisterType<TtuneObject>("Nootka", 1, 0, "TtuneObject"); + if (m_instance) { + qDebug() << "TnootkaQML instance already exists!"; + return; + } + m_instance = this; + + qRegisterMetaType<Tclef>(); + qmlRegisterUncreatableType<Tclef>("Score", 1, 0, "Tclef", QStringLiteral("You cannot create an instance of the Tclef.")); + qRegisterMetaType<Tmeter>(); + qmlRegisterUncreatableType<Tmeter>("Score", 1, 0, "Tmeter", QStringLiteral("You cannot create an instance of the Tmeter.")); + qmlRegisterUncreatableType<Trhythm>("Score", 1, 0, "Trhythm", QStringLiteral("You cannot create an instance of the Trhythm.")); + qRegisterMetaType<Trhythm>(); + + 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.")); + qRegisterMetaType<Tinstrument>(); + qmlRegisterUncreatableType<Tinstrument>("Nootka", 1, 0, "Tinstrument", QStringLiteral("You cannot create an instance of the Tinstrument.")); + qRegisterMetaType<Ttune>(); + qmlRegisterUncreatableType<Ttune>("Nootka", 1, 0, "Ttune", QStringLiteral("You cannot create an instance of the Ttune.")); + qmlRegisterType<TtuneObject>("Nootka", 1, 0, "TtuneObject"); } - TnootkaQML::~TnootkaQML() { - m_instance = nullptr; + m_instance = nullptr; } -//################################################################################################# -//################### INVOKABLE METHODS ############################################ -//################################################################################################# +// ################################################################################################# +// ################### INVOKABLE METHODS ############################################ +// ################################################################################################# -QString TnootkaQML::version() { - if (qApp->arguments().last().contains(QLatin1String("--no-version"))) - return QString(); - else - return NOOTKA_VERSION; +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)); +Tclef TnootkaQML::clef(int type) +{ + return Tclef(static_cast<Tclef::EclefType>(type)); } - -Tmeter TnootkaQML::meter(int m) { - return Tmeter(static_cast<Tmeter::Emeter>(m)); +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(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, 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(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::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::setUpperStaff(Tnote n, bool onUpper) +{ + n.setOnUpperStaff(onUpper); + return n; } - -Tnote TnootkaQML::emptyNote() { - return Tnote(); +Tnote TnootkaQML::emptyNote() +{ + return Tnote(); } - -Tnote TnootkaQML::transpose(Tnote n, int semitones) { - n.transpose(semitones); - return n; +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); +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; +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::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::majorKeyName(int key) +{ + return TkeySignature(static_cast<char>(key)).getMajorName(); } - -QString TnootkaQML::minorKeyName(int key) { - return TkeySignature(static_cast<char>(key)).getMinorName(); +QString TnootkaQML::minorKeyName(int key) +{ + return TkeySignature(static_cast<char>(key)).getMinorName(); } - -QString TnootkaQML::majAndMinKeyName(int key) { - return majorKeyName(key) + QLatin1String("<br>") + minorKeyName(key); +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; +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))); +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; +bool TnootkaQML::isAndroid() +{ +#if defined(Q_OS_ANDROID) + return true; #else - return false; + return false; #endif } - -bool TnootkaQML::isWindows() { -#if defined (Q_OS_WIN) - return true; +bool TnootkaQML::isWindows() +{ +#if defined(Q_OS_WIN) + return true; #else - return false; + return false; #endif } - -bool TnootkaQML::isMac() { -#if defined (Q_OS_MACOS) - return true; +bool TnootkaQML::isMac() +{ +#if defined(Q_OS_MACOS) + return true; #else - return false; + 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::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::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; +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)) { +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(); - } + 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(); +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::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)) { +Ttune TnootkaQML::defaultScale(int instr) +{ + switch (static_cast<Tinstrument::Etype>(instr)) { case Tinstrument::ClassicalGuitar: case Tinstrument::ElectricGuitar: - return Ttune::stdTune; + return Ttune::stdTune; case Tinstrument::BassGuitar: - return Ttune::bassTunes[0]; + return Ttune::bassTunes[0]; case Tinstrument::Piano: - return tuning(Tnote(-11), Tnote(49), Tnote(), Tnote(), Tnote(), Tnote()); + return tuning(Tnote(-11), Tnote(49), Tnote(), Tnote(), Tnote(), Tnote()); case Tinstrument::Bandoneon: - return tuning(Tnote(-11), Tnote(48), Tnote(), Tnote(), Tnote(), Tnote()); + 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 tuning(Tnote(11), Tnote(49), Tnote(), Tnote(), Tnote(), Tnote()); case Tinstrument::Ukulele: - return Ttune::ukuleleGCEA; + return Ttune::ukuleleGCEA; default: // NoInstrument and any unexpected case - return tuning(Tnote(10), Tnote(54), Tnote(), Tnote(), Tnote(), Tnote()); - } + 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)); +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")); +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")); #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);;")); + 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);;")); #endif - if (!openFile.isEmpty()) - GLOB->setLastXmlDir(QFileInfo(openFile).absoluteDir().path()); - return openFile; + 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")); +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")); #else - saveFile = TfileDialog::getSaveFileName(qApp->translate("TmainScoreObject", "Save melody as:"), GLOB->lastXmlDir() + QDir::separator() + fileName, + 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)"), + + qTR("TmainScoreObject", "MusicXML file") + QLatin1String(" (*.musicxml *.xml)"), &filter); #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"); + 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; + return saveFile; } - -QString TnootkaQML::pix(const QString& imageFileName) { - return Tpath::pix(imageFileName); +QString TnootkaQML::pix(const QString &imageFileName) +{ + return Tpath::pix(imageFileName); } - -QString TnootkaQML::pix(const char* imageName, int height) { - return pixToHtml(QString(imageName), height); +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::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>"); +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); +QColor TnootkaQML::alpha(const QColor &c, int a) +{ + return Tcolor::alpha(c, a); } - -QColor TnootkaQML::randomColor(int alpha, int level) { - return QColor(QRandomGenerator::global()->bounded(level), QRandomGenerator::global()->bounded(level), - QRandomGenerator::global()->bounded(level), alpha); +QColor TnootkaQML::randomColor(int alpha, int level) +{ + return QColor(QRandomGenerator::global()->bounded(level), QRandomGenerator::global()->bounded(level), QRandomGenerator::global()->bounded(level), alpha); } - -QColor TnootkaQML::invert(const QColor& c) { - return Tcolor::invert(c); +QColor TnootkaQML::invert(const QColor &c) +{ + return Tcolor::invert(c); } - -qreal TnootkaQML::hue(const QColor& c) const { - return c.hueF(); +qreal TnootkaQML::hue(const QColor &c) const +{ + return c.hueF(); } - -qreal TnootkaQML::saturation(const QColor& c) const { - return c.saturationF(); +qreal TnootkaQML::saturation(const QColor &c) const +{ + return c.saturationF(); } - -qreal TnootkaQML::lightness(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()); +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()); + // 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(); +QString TnootkaQML::fontFamily() +{ + return Tmtr::systemFont.family(); } +int TnootkaQML::fingerPixels() +{ + return Tmtr::fingerPixels(); +} -int TnootkaQML::fingerPixels() { return Tmtr::fingerPixels(); } - -int TnootkaQML::shortScreenSide() { return Tmtr::shortScreenSide(); } - -int TnootkaQML::longScreenSide() { return Tmtr::longScreenSide(); } +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); +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(); + 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() + "\"/>"; + 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)); +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); +qreal TnootkaQML::bound(qreal min, qreal val, qreal max) +{ + return qBound(min, val, max); } - -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(); - - connect(qApp, &QGuiApplication::paletteChanged, this, [=]{ - setMessageColor(qApp->palette().highlight().color()); +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(); + + connect(qApp, &QGuiApplication::paletteChanged, this, [=] { + setMessageColor(qApp->palette().highlight().color()); + m_scoreAct->setBgColor(qApp->palette().highlight().color()); + }); } - /** * 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, @@ -583,321 +598,335 @@ void TnootkaQML::setQmlEngine(QQmlEngine* e) { * - 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); }); +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); + }); + } } - } } - -void TnootkaQML::setMessageColor(const QColor& mc) { - if (m_messageColor != mc) { - m_messageColor = mc; - emit messageColorChanged(); - } +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::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(); -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); + emit statusTip(message, pos, richText); + m_messageTimer->start(time); } - -bool TnootkaQML::messageTimerActive() const { - return m_messageTimer ? m_messageTimer->isActive() : false; +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::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::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"); +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); - } +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 ############################################ +// ################################################################################################# -//################################################################################################# -//################### CONNECTIONS NODE ############################################ -//################################################################################################# +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::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()) { +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_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); + } 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); } - 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()) { + } + 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); - note.setRhythm(r); - m_scoreObject->setNote(m_scoreObject->selectedItem(), note); + instrNote.setRhythm(r); + m_scoreObject->setNote(m_scoreObject->selectedItem(), instrNote); } - } - } - 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; + 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()); } - seg->setTechnical(instrData.data()); - } - } - m_ignoreScore = false; // Reset the switch in case it is not consumed + } + 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::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; + } -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); + 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::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; +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(); } - technical = dataToSet.data(); - } - return technical; + return technical; } - -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 +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 } - -int TnootkaQML::scoreNotesCount() const { - return m_scoreObject->notesCount(); +int TnootkaQML::scoreNotesCount() const +{ + return m_scoreObject->notesCount(); } - -QList<Tnote>& TnootkaQML::scoreNoteList() const { - return m_scoreObject->noteList(); +QList<Tnote> &TnootkaQML::scoreNoteList() const +{ + return m_scoreObject->noteList(); } - diff --git a/src/libs/core/tnootkaqml.h b/src/libs/core/tnootkaqml.h index 48d51a8334961edebaa7bac54105ebc59d598993..e8ba162eec949bf2c7b43b5a26c4488f7e3b5fbd 100644 --- a/src/libs/core/tnootkaqml.h +++ b/src/libs/core/tnootkaqml.h @@ -19,17 +19,15 @@ #ifndef TNOOTKAQML_H #define TNOOTKAQML_H - -#include <nootkacoreglobal.h> -#include "music/tmeter.h" #include "music/tclef.h" -#include "music/ttune.h" #include "music/tinstrument.h" +#include "music/tmeter.h" +#include "music/ttune.h" +#include <nootkacoreglobal.h> #include <QtCore/qobject.h> #include <QtGui/qcolor.h> - class TcommonInstrument; class TscoreObject; class QQuickItem; @@ -37,8 +35,7 @@ class QQmlEngine; class Taction; class QTimer; - -#define NOO TnootkaQML::instance() +#define NOO TnootkaQML::instance() /** * Singleton object to manage (create) custom types from QML @@ -46,288 +43,294 @@ class QTimer; * Access it through @p Noo object * Also it provides many helper functions through @p Noo * - * Also it acts as node to tie note changes of sound, instrument and score + * Also it acts as node to tie note changes of sound, instrument and score */ class NOOTKACORE_EXPORT TnootkaQML : public QObject { + Q_OBJECT - Q_OBJECT + Q_PROPERTY(Taction *settingsAct READ settingsAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *scoreAct READ scoreAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *melodyAct READ melodyAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *levelAct READ levelAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *chartsAct READ chartsAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *examAct READ examAct NOTIFY dummyActionSignal) + Q_PROPERTY(Taction *aboutAct READ aboutAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* settingsAct READ settingsAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* scoreAct READ scoreAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* melodyAct READ melodyAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* levelAct READ levelAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* chartsAct READ chartsAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* examAct READ examAct NOTIFY dummyActionSignal) - Q_PROPERTY(Taction* aboutAct READ aboutAct NOTIFY dummyActionSignal) + Q_PROPERTY(QQuickItem *mainScore READ mainScore WRITE setMainScore) + Q_PROPERTY(TcommonInstrument *instrument READ instrument WRITE setInstrument) - Q_PROPERTY(QQuickItem* mainScore READ mainScore WRITE setMainScore) - Q_PROPERTY(TcommonInstrument* instrument READ instrument WRITE setInstrument) - - Q_PROPERTY(QColor messageColor READ messageColor WRITE setMessageColor NOTIFY messageColorChanged) + Q_PROPERTY(QColor messageColor READ messageColor WRITE setMessageColor NOTIFY messageColorChanged) public: - explicit TnootkaQML(QObject* parent = nullptr); - ~TnootkaQML() override; - - static TnootkaQML* instance() { return m_instance; } - - Taction* settingsAct() { return m_settingsAct; } - Taction* scoreAct() { return m_scoreAct; } - Taction* melodyAct() { return m_melodyAct; } - Taction* levelAct() { return m_levelAct; } - Taction* chartsAct() { return m_chartsAct; } - Taction* examAct() { return m_examAct; } - Taction* aboutAct() { return m_aboutAct; } - - QColor messageColor() { return m_messageColor; } - void setMessageColor(const QColor& mc); - - /** - * Dialogues recognized by main QML Dialog instance of main window - * @p NoDialog only creates 'dialog loader' instance but does nothing - */ - enum Edialogs { - NoDialog = 0, Settings = 1, About = 2, LevelCreator = 3, ExamStart = 4, - ExamSummary = 5, Charts = 6, Tuner = 7, MelodyGenerator = 8, Updater = 9, - ScoreImport = 10 - }; - Q_ENUM(Edialogs) - - enum EnameStyle { - Norsk_Hb = 0, /**< for letters with signs f.e. C# Cx or Cb !! THIS IS DEFAULT !! */ - Deutsch_His = 1, /**< for letters with names f.e. Cis Cisis or Ces H and B (H with flat) */ - Italiano_Si = 2, /**< for classical Do Re Mi Fa Sol La Si */ - English_Bb = 3, /**< like @p e_norsk_Hb but with B and Bb (B flat) */ - Nederl_Bis = 4, /**< like @p e_deutsch_His but with B ens Bes */ - Russian_Ci = 5 /**< classical but in Russian: До Ре Ми Фа Соль Ð›Ñ Ð¡Ð¸ */ - }; - Q_ENUM(EnameStyle) - - Q_INVOKABLE QString version(); - Q_INVOKABLE Tclef clef(int type); - Q_INVOKABLE Tmeter meter(int m); - Q_INVOKABLE Tnote note(int pitch, int octave, int alter, int rhythm = 3, bool rest = false, bool dot = false); - Q_INVOKABLE Tnote note(const Tnote& n, int rhythm = 3, bool rest = false, bool dot = false); - Q_INVOKABLE Tnote note(const Tnote& n, const Trhythm& r); - Q_INVOKABLE Tnote note(int chroma, bool sharp = true); - Q_INVOKABLE Tnote setUpperStaff(Tnote n, bool onUpper); - Q_INVOKABLE Tnote emptyNote(); - Q_INVOKABLE int octave(const Tnote& n) { return static_cast<int>(n.octave()); } - Q_INVOKABLE int pitch(const Tnote& n) { return static_cast<int>(n.note()); } - Q_INVOKABLE int alter(const Tnote& n) { return static_cast<int>(n.alter()); } - Q_INVOKABLE Tnote transpose(Tnote n, int semitones); - Q_INVOKABLE Trhythm rhythm(int rtm, bool rest, bool dot, bool triplet); - Q_INVOKABLE QString rhythmText(const Trhythm& r); - Q_INVOKABLE QString noteName(const Tnote& n, int style, bool showOctave = true); - Q_INVOKABLE QString styledName(const Tnote& n, int style, bool showOctave = true); - Q_INVOKABLE QString majorKeyName(int key); - Q_INVOKABLE QString minorKeyName(int key); - Q_INVOKABLE QString majAndMinKeyName(int key); - Q_INVOKABLE QStringList keyComboModel(); - Q_INVOKABLE int keysDiff(int key1, int key2); - Q_INVOKABLE bool isAndroid(); - Q_INVOKABLE bool isWindows(); - Q_INVOKABLE bool isMac(); - Q_INVOKABLE QStringList guitarTunings(); - Q_INVOKABLE QStringList bassTunings(); - Q_INVOKABLE QStringList ukuleleTunings(); - Q_INVOKABLE QStringList tuningModel(int instr); - Q_INVOKABLE Ttune tuning(int tuningType); - Q_INVOKABLE Ttune tuning(const Tnote& s1, const Tnote& s2, const Tnote& s3, const Tnote& s4, const Tnote& s5, const Tnote& s6); - - /** - * Returns default scale (or tuning if any kind of guitar) - * for given @p instr instrument - */ - Q_INVOKABLE Ttune defaultScale(int instr); - Q_INVOKABLE Tinstrument instr(int type); - Q_INVOKABLE QString getXmlToOpen(); - Q_INVOKABLE QString getXmlToSave(const QString& fileName = QString()); - Q_INVOKABLE QString pix(const QString& imageFileName); - QString pix(const char* imageName, int height = 32); - Q_INVOKABLE QString TR(const QString& context, const QString& text, const QString& disambiguation = QString(), int n = -1); - - /** - * Returns HTML paragraph aligned to the right with - * link to Nootka online documenation: @p post topic - * (Newer format - Nootka 2.0 and above) - */ - Q_INVOKABLE QString getOnlineDoc(const QString& post); - - /** - * Returns given color @p c with alpha channel @p a - */ - Q_INVOKABLE QColor alpha(const QColor& c, int a); - - /** - * Returns randomized color, @p alpha is alpha level - * @p level (220 by default) determines maximal value of color [0 - 255]. - * Using smaller value avoids generating dark colors - */ - Q_INVOKABLE QColor randomColor(int alpha = 255, int level = 220); - Q_INVOKABLE QColor invert(const QColor& c); - - Q_INVOKABLE qreal hue(const QColor& c) const; - Q_INVOKABLE qreal saturation(const QColor& c) const; - Q_INVOKABLE qreal lightness(const QColor& c) const; - - Q_INVOKABLE int factor(); - Q_INVOKABLE QString fontFamily(); - Q_INVOKABLE int fingerPixels(); - - /** - * height or width of a screen (less of them) - */ - Q_INVOKABLE int shortScreenSide(); - - /** - * height or width of a screen (bigger of them) - */ - Q_INVOKABLE int longScreenSide(); - - Q_INVOKABLE QString pixToHtml(const QString& pixName, int height = 0); - - /** - * Newer documentation address: - * at 'https://nootka.sourceforge.io/index.php/' - */ - Q_INVOKABLE void openDocLink(const QString& lnk); - - Q_INVOKABLE qreal bound(qreal min, qreal val, qreal max); - - bool resetConfig() const { return m_resetConfig; } - Q_INVOKABLE void setResetConfig(bool rc) { m_resetConfig = rc; } - - /** - * Main @p QQmlEngine created at very beginning of Nootka launch - */ - QQmlEngine* qmlEngine() { return m_qmlEngine; } - void setQmlEngine(QQmlEngine* e); - - void openFile(const QString& runArg); - - /** - * Emits signal with given text to QML to display status message on Nootka window at given position - * @p tipPos value represents QQuickItem::TransformOrigin enumerator. - * @p default 1 is top center of window - */ - Q_INVOKABLE void setStatusTip(const QString& statusText, int tipPos = 1, bool richText = false); - - /** - * Displays status message in declared period @p time - * at position @p pos - top center by default (see @p QQuickItem::TransformOrigin) - */ - void showTimeMessage(const QString& message, int time, int pos = 1, bool richText = false); - bool messageTimerActive() const; - - /** - * Returns text of question/ answer kind depends on given value of @p qaType. - * It could be: - * @li 0 - as note on the staff - * @li 1 - as note name - * @li 2 - on instrument - * @li 3 - as played sound - */ - Q_INVOKABLE QString qaTypeText(int qaType); - - /** - * Name of seventh note according to localization. - * Returns 'b' or 'h' depends on translation. - */ - Q_INVOKABLE QString note7translated() const; - - /** - * Default style grab from translation 'letters' or 'solfege' - */ - Q_INVOKABLE QString keyNameTranslated() const; - -/** - * All stuff below is responsible for handling note changes in score, instrument and sound in/out. - * @p TnootkaQML has score and instrument pointers to handle theirs signals when note is changed, - * but sound is managed outside (through @p Tsound) due to all audio stuff is in sound library layer above. - * So @p playNote() signal is emitted apparently and @p noteStarted(), @p noteFinished() methods are invoked by @p Tscound - * - * TRANSPOSITION - * Only notes to be played have to be transposed according @p GLOB->transposition() - * to sound properly for current transposing instrument. - * Pitch detected notes are already transposed back to score visual notation. - */ - QQuickItem* mainScore() { return m_mainScore; } - void setMainScore(QQuickItem* ms); - - TscoreObject* scoreObj() { return m_scoreObject; } - - TcommonInstrument* instrument() { return m_instrument; } - void setInstrument(TcommonInstrument* ci); - - void noteStarted(const Tnote& n); - void noteFinished(const Tnote& n); - - /** - * Id (number) of selected note on the score or @p -1 if none - */ - int selectedNoteId() const; - - /** - * This method is invoked by @p Tsound when playing note changes, - * and it is used to select corresponding note on the score and on the instrument - */ - void selectPlayingNote(int id); - - /** - * Number of notes on the score - */ - int scoreNotesCount() const; - - /** - * Reference to the notes list on the score - */ - QList<Tnote>& scoreNoteList() const; + explicit TnootkaQML(QObject *parent = nullptr); + ~TnootkaQML() override; + + static TnootkaQML *instance() { return m_instance; } + + Taction *settingsAct() { return m_settingsAct; } + Taction *scoreAct() { return m_scoreAct; } + Taction *melodyAct() { return m_melodyAct; } + Taction *levelAct() { return m_levelAct; } + Taction *chartsAct() { return m_chartsAct; } + Taction *examAct() { return m_examAct; } + Taction *aboutAct() { return m_aboutAct; } + + QColor messageColor() { return m_messageColor; } + void setMessageColor(const QColor &mc); + + /** + * Dialogues recognized by main QML Dialog instance of main window + * @p NoDialog only creates 'dialog loader' instance but does nothing + */ + enum Edialogs { + NoDialog = 0, + Settings = 1, + About = 2, + LevelCreator = 3, + ExamStart = 4, + ExamSummary = 5, + Charts = 6, + Tuner = 7, + MelodyGenerator = 8, + Updater = 9, + ScoreImport = 10 + }; + Q_ENUM(Edialogs) + + enum EnameStyle { + Norsk_Hb = 0, /**< for letters with signs f.e. C# Cx or Cb !! THIS IS DEFAULT !! */ + Deutsch_His = 1, /**< for letters with names f.e. Cis Cisis or Ces H and B (H with flat) */ + Italiano_Si = 2, /**< for classical Do Re Mi Fa Sol La Si */ + English_Bb = 3, /**< like @p e_norsk_Hb but with B and Bb (B flat) */ + Nederl_Bis = 4, /**< like @p e_deutsch_His but with B ens Bes */ + Russian_Ci = 5 /**< classical but in Russian: До Ре Ми Фа Соль Ð›Ñ Ð¡Ð¸ */ + }; + Q_ENUM(EnameStyle) + + Q_INVOKABLE QString version(); + Q_INVOKABLE Tclef clef(int type); + Q_INVOKABLE Tmeter meter(int m); + Q_INVOKABLE Tnote note(int pitch, int octave, int alter, int rhythm = 3, bool rest = false, bool dot = false); + Q_INVOKABLE Tnote note(const Tnote &n, int rhythm = 3, bool rest = false, bool dot = false); + Q_INVOKABLE Tnote note(const Tnote &n, const Trhythm &r); + Q_INVOKABLE Tnote note(int chroma, bool sharp = true); + Q_INVOKABLE Tnote setUpperStaff(Tnote n, bool onUpper); + Q_INVOKABLE Tnote emptyNote(); + Q_INVOKABLE int octave(const Tnote &n) { return static_cast<int>(n.octave()); } + Q_INVOKABLE int pitch(const Tnote &n) { return static_cast<int>(n.note()); } + Q_INVOKABLE int alter(const Tnote &n) { return static_cast<int>(n.alter()); } + Q_INVOKABLE Tnote transpose(Tnote n, int semitones); + Q_INVOKABLE Trhythm rhythm(int rtm, bool rest, bool dot, bool triplet); + Q_INVOKABLE QString rhythmText(const Trhythm &r); + Q_INVOKABLE QString noteName(const Tnote &n, int style, bool showOctave = true); + Q_INVOKABLE QString styledName(const Tnote &n, int style, bool showOctave = true); + Q_INVOKABLE QString majorKeyName(int key); + Q_INVOKABLE QString minorKeyName(int key); + Q_INVOKABLE QString majAndMinKeyName(int key); + Q_INVOKABLE QStringList keyComboModel(); + Q_INVOKABLE int keysDiff(int key1, int key2); + Q_INVOKABLE bool isAndroid(); + Q_INVOKABLE bool isWindows(); + Q_INVOKABLE bool isMac(); + Q_INVOKABLE QStringList guitarTunings(); + Q_INVOKABLE QStringList bassTunings(); + Q_INVOKABLE QStringList ukuleleTunings(); + Q_INVOKABLE QStringList tuningModel(int instr); + Q_INVOKABLE Ttune tuning(int tuningType); + Q_INVOKABLE Ttune tuning(const Tnote &s1, const Tnote &s2, const Tnote &s3, const Tnote &s4, const Tnote &s5, const Tnote &s6); + + /** + * Returns default scale (or tuning if any kind of guitar) + * for given @p instr instrument + */ + Q_INVOKABLE Ttune defaultScale(int instr); + Q_INVOKABLE Tinstrument instr(int type); + Q_INVOKABLE QString getXmlToOpen(); + Q_INVOKABLE QString getXmlToSave(const QString &fileName = QString()); + Q_INVOKABLE QString pix(const QString &imageFileName); + QString pix(const char *imageName, int height = 32); + Q_INVOKABLE QString TR(const QString &context, const QString &text, const QString &disambiguation = QString(), int n = -1); + + /** + * Returns HTML paragraph aligned to the right with + * link to Nootka online documenation: @p post topic + * (Newer format - Nootka 2.0 and above) + */ + Q_INVOKABLE QString getOnlineDoc(const QString &post); + + /** + * Returns given color @p c with alpha channel @p a + */ + Q_INVOKABLE QColor alpha(const QColor &c, int a); + + /** + * Returns randomized color, @p alpha is alpha level + * @p level (220 by default) determines maximal value of color [0 - 255]. + * Using smaller value avoids generating dark colors + */ + Q_INVOKABLE QColor randomColor(int alpha = 255, int level = 220); + Q_INVOKABLE QColor invert(const QColor &c); + + Q_INVOKABLE qreal hue(const QColor &c) const; + Q_INVOKABLE qreal saturation(const QColor &c) const; + Q_INVOKABLE qreal lightness(const QColor &c) const; + + Q_INVOKABLE int factor(); + Q_INVOKABLE QString fontFamily(); + Q_INVOKABLE int fingerPixels(); + + /** + * height or width of a screen (less of them) + */ + Q_INVOKABLE int shortScreenSide(); + + /** + * height or width of a screen (bigger of them) + */ + Q_INVOKABLE int longScreenSide(); + + Q_INVOKABLE QString pixToHtml(const QString &pixName, int height = 0); + + /** + * Newer documentation address: + * at 'https://nootka.sourceforge.io/index.php/' + */ + Q_INVOKABLE void openDocLink(const QString &lnk); + + Q_INVOKABLE qreal bound(qreal min, qreal val, qreal max); + + bool resetConfig() const { return m_resetConfig; } + Q_INVOKABLE void setResetConfig(bool rc) { m_resetConfig = rc; } + + /** + * Main @p QQmlEngine created at very beginning of Nootka launch + */ + QQmlEngine *qmlEngine() { return m_qmlEngine; } + void setQmlEngine(QQmlEngine *e); + + void openFile(const QString &runArg); + + /** + * Emits signal with given text to QML to display status message on Nootka window at given position + * @p tipPos value represents QQuickItem::TransformOrigin enumerator. + * @p default 1 is top center of window + */ + Q_INVOKABLE void setStatusTip(const QString &statusText, int tipPos = 1, bool richText = false); + + /** + * Displays status message in declared period @p time + * at position @p pos - top center by default (see @p QQuickItem::TransformOrigin) + */ + void showTimeMessage(const QString &message, int time, int pos = 1, bool richText = false); + bool messageTimerActive() const; + + /** + * Returns text of question/ answer kind depends on given value of @p qaType. + * It could be: + * @li 0 - as note on the staff + * @li 1 - as note name + * @li 2 - on instrument + * @li 3 - as played sound + */ + Q_INVOKABLE QString qaTypeText(int qaType); + + /** + * Name of seventh note according to localization. + * Returns 'b' or 'h' depends on translation. + */ + Q_INVOKABLE QString note7translated() const; + + /** + * Default style grab from translation 'letters' or 'solfege' + */ + Q_INVOKABLE QString keyNameTranslated() const; + + /** + * All stuff below is responsible for handling note changes in score, instrument and sound in/out. + * @p TnootkaQML has score and instrument pointers to handle theirs signals when note is changed, + * but sound is managed outside (through @p Tsound) due to all audio stuff is in sound library layer above. + * So @p playNote() signal is emitted apparently and @p noteStarted(), @p noteFinished() methods are invoked by @p Tscound + * + * TRANSPOSITION + * Only notes to be played have to be transposed according @p GLOB->transposition() + * to sound properly for current transposing instrument. + * Pitch detected notes are already transposed back to score visual notation. + */ + QQuickItem *mainScore() { return m_mainScore; } + void setMainScore(QQuickItem *ms); + + TscoreObject *scoreObj() { return m_scoreObject; } + + TcommonInstrument *instrument() { return m_instrument; } + void setInstrument(TcommonInstrument *ci); + + void noteStarted(const Tnote &n); + void noteFinished(const Tnote &n); + + /** + * Id (number) of selected note on the score or @p -1 if none + */ + int selectedNoteId() const; + + /** + * This method is invoked by @p Tsound when playing note changes, + * and it is used to select corresponding note on the score and on the instrument + */ + void selectPlayingNote(int id); + + /** + * Number of notes on the score + */ + int scoreNotesCount() const; + + /** + * Reference to the notes list on the score + */ + QList<Tnote> &scoreNoteList() const; signals: - void playNote(const Tnote&); - void dummyActionSignal(); - void settingsActTriggered(); - void scoreActTriggered(); - void melodyActTriggered(); - void levelActTriggered(); - void chartsActTriggered(); - void examActTriggered(); - void aboutActTriggered(); - void statusTip(const QString& statusText, int tipPos, bool richText); - void messageColorChanged(); - void wantOpenXml(const QString& xmlFile); + void playNote(const Tnote &); + void dummyActionSignal(); + void settingsActTriggered(); + void scoreActTriggered(); + void melodyActTriggered(); + void levelActTriggered(); + void chartsActTriggered(); + void examActTriggered(); + void aboutActTriggered(); + void statusTip(const QString &statusText, int tipPos, bool richText); + void messageColorChanged(); + void wantOpenXml(const QString &xmlFile); protected: - void connectInstrument(); - void instrumentChangesNoteSlot(); - void examStartStop(); - void scoreChangedNoteSlot(); + void connectInstrument(); + void instrumentChangesNoteSlot(); + void examStartStop(); + void scoreChangedNoteSlot(); - int getTechicalFromScore(); + int getTechicalFromScore(); - void warnNewerVersionSlot(const QString& fileName); + void warnNewerVersionSlot(const QString &fileName); private: - static TnootkaQML *m_instance; - - TcommonInstrument *m_instrument = nullptr; - QQuickItem *m_mainScore = nullptr; - TscoreObject *m_scoreObject = nullptr; - int m_startedNoteId = -1; /**< Index of pitch detected note */ - bool m_nodeConnected = false; - bool m_ignoreScore = false; /**< Becomes @p TRUE to ignore @p scoreChangedNote() slot */ - bool m_resetConfig = false; - QQmlEngine *m_qmlEngine = nullptr; - Taction *m_scoreAct = nullptr; - Taction *m_settingsAct, *m_levelAct, *m_examAct, *m_melodyAct, *m_chartsAct, *m_aboutAct; - - QColor m_messageColor; - QTimer *m_messageTimer = nullptr; - int m_messagePos; - QObject *m_warnNewerPopup = nullptr; + static TnootkaQML *m_instance; + + TcommonInstrument *m_instrument = nullptr; + QQuickItem *m_mainScore = nullptr; + TscoreObject *m_scoreObject = nullptr; + int m_startedNoteId = -1; /**< Index of pitch detected note */ + bool m_nodeConnected = false; + bool m_ignoreScore = false; /**< Becomes @p TRUE to ignore @p scoreChangedNote() slot */ + bool m_resetConfig = false; + QQmlEngine *m_qmlEngine = nullptr; + Taction *m_scoreAct = nullptr; + Taction *m_settingsAct, *m_levelAct, *m_examAct, *m_melodyAct, *m_chartsAct, *m_aboutAct; + + QColor m_messageColor; + QTimer *m_messageTimer = nullptr; + int m_messagePos; + QObject *m_warnNewerPopup = nullptr; }; #endif // TNOOTKAQML_H - diff --git a/src/libs/core/tpath.cpp b/src/libs/core/tpath.cpp index 4137492ea2ff0e03c8223fb69867799c6c23434f..a2bd7824f93613619d744602cc923971ee3d80d3 100644 --- a/src/libs/core/tpath.cpp +++ b/src/libs/core/tpath.cpp @@ -16,18 +16,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - #include "tpath.h" - QString Tpath::main = QString(); - -QString Tpath::img(const char* imageFileName, const char* ext) { - return QString("%1picts/%2%3").arg(main, imageFileName, ext); +QString Tpath::img(const char *imageFileName, const char *ext) +{ + return QString("%1picts/%2%3").arg(main, imageFileName, ext); } - -QString Tpath::pix(const QString& imageFileName) { - return QString("file:%1picts/%2.png").arg(main, imageFileName); +QString Tpath::pix(const QString &imageFileName) +{ + return QString("file:%1picts/%2.png").arg(main, imageFileName); } diff --git a/src/libs/core/tpath.h b/src/libs/core/tpath.h index 5bdffa569a3819bee737073eb96ff5f62db2250c..4efe8f8b581a018c54ca7c17ac8fe72f7dd43a96 100644 --- a/src/libs/core/tpath.h +++ b/src/libs/core/tpath.h @@ -19,11 +19,9 @@ #ifndef TPATH_H #define TPATH_H - #include "nootkacoreglobal.h" #include <QtCore/qstring.h> - /** * Set of static methods returning appropriate paths * @p main - this is default path in Nootka - @@ -37,67 +35,65 @@ * @p img() for images * @p sound() for sounds * @p lang() for translations -*/ + */ class NOOTKACORE_EXPORT Tpath { - public: - Tpath() {} - ~Tpath() {} - - static QString main; /**< Path with Nootka resources (/usr/share -Linux /Resources - MacOs) */ - - /** - * Returns path to Nootka images (picts) with given image name. - * By default a '.png' extensions is added - * but it can be changed through @p ext parameter. - */ - static QString img(const char* imageFileName, const char* ext = ".png"); - - /** - * Returns path to Nootka images (picts) with given image name. - * It is accessible from QML through @p Noo.pix("image_name") - */ - static QString pix(const QString& imageFileName); - - - /** - * Returns a path to given ogg file with samples in sound resource directory - */ - static QString sound(const char* soundFileName, const char* ext = ".ogg") { -#if defined (Q_OS_ANDROID) - return QString("assets:/sounds/%1%2").arg(soundFileName, ext); + Tpath() { } + ~Tpath() { } + + static QString main; /**< Path with Nootka resources (/usr/share -Linux /Resources - MacOs) */ + + /** + * Returns path to Nootka images (picts) with given image name. + * By default a '.png' extensions is added + * but it can be changed through @p ext parameter. + */ + static QString img(const char *imageFileName, const char *ext = ".png"); + + /** + * Returns path to Nootka images (picts) with given image name. + * It is accessible from QML through @p Noo.pix("image_name") + */ + static QString pix(const QString &imageFileName); + + /** + * Returns a path to given ogg file with samples in sound resource directory + */ + static QString sound(const char *soundFileName, const char *ext = ".ogg") + { +#if defined(Q_OS_ANDROID) + return QString("assets:/sounds/%1%2").arg(soundFileName, ext); #else - return QString("%1sounds/%2%3").arg(main, soundFileName, ext); + return QString("%1sounds/%2%3").arg(main, soundFileName, ext); #endif - } - - /** - * Returns path where Nootka language files (*.qm) are located. - * (Under Mac/Win/Android Qt translations as well). - * WARNING! there is no trailing slash at the end. - */ - static QString lang() { -#if defined (Q_OS_ANDROID) - return QStringLiteral("assets:/lang"); + } + + /** + * Returns path where Nootka language files (*.qm) are located. + * (Under Mac/Win/Android Qt translations as well). + * WARNING! there is no trailing slash at the end. + */ + static QString lang() + { +#if defined(Q_OS_ANDROID) + return QStringLiteral("assets:/lang"); #else - return main + QLatin1String("lang"); + return main + QLatin1String("lang"); #endif - } - - /** - * Path with defined Nootka levels *.nel files - */ - static QString levels() { -#if defined (Q_OS_ANDROID) - return QStringLiteral("assets:/levels"); + } + + /** + * Path with defined Nootka levels *.nel files + */ + static QString levels() + { +#if defined(Q_OS_ANDROID) + return QStringLiteral("assets:/levels"); #else - return main + QLatin1String("levels"); + return main + QLatin1String("levels"); #endif - } - + } }; - - #endif // TPATH_H diff --git a/src/libs/core/translationHeader.h b/src/libs/core/translationHeader.h index 96a02a3b613d370e536d4a6ad83c7332ed8ed1a5..1261afb8dca1925a7a1341c9df311943ae26fc68 100644 --- a/src/libs/core/translationHeader.h +++ b/src/libs/core/translationHeader.h @@ -2,9 +2,8 @@ * Fake header with texts to translate not ocurr in code but in mime types and desktop */ - QApplication::translate("File association entries", "to play scores"); -QApplication::translate("File association entries", "Application for learning musical score notation"); +QApplication::translate("File association entries", "Application for learning musical score notation"); QApplication::translate("File association entries", "Nootka level file", "for file browsers"); QApplication::translate("File association entries", "Open with Nootka"); QApplication::translate("File association entries", "Nootka exam file"); diff --git a/src/libs/core/tscoreparams.h b/src/libs/core/tscoreparams.h index 6d81c80b943006d0bfe9b7361adc50ce3ebddfda..1bcaae9b93fdd5f77b548150f14e77da4cd1f73d 100644 --- a/src/libs/core/tscoreparams.h +++ b/src/libs/core/tscoreparams.h @@ -19,76 +19,75 @@ #ifndef TSCOREPARAMS_H #define TSCOREPARAMS_H -#include "nootkacoreglobal.h" -#include "music/tnote.h" #include "music/tclef.h" +#include "music/tnote.h" +#include "nootkacoreglobal.h" #include <QtGui/qcolor.h> - /** * This is set of parameters related to score and note names */ class NOOTKACORE_EXPORT TscoreParams { public: + TscoreParams() + : clef(Tclef::defaultType) + { + } - TscoreParams() : clef(Tclef::defaultType) {} - -//============ score widget settings ============================================================= - /** - * if true shows other similar (enharmonic) notes on the staff: - * for C# - Db, for G - Fx and Abb. In Tnotename widget also. - */ - bool keySignatureEnabled; - bool showKeySignName; /**< default true */ - Tnote::EnameStyle nameStyleInKeySign; - - /** - * Convention is: - * if keyNameSuffix == " " constructor of Tglobals sets its default and - * constructor of TkeySignatureView sets translatable value "major" and "minor" - * otherwise it is overridden by loading settings - * if keyNameSuffix == "" user prefers without suffix. - * If keyNameSuffix has some text - is translated by user himself - */ - QString majKeyNameSufix; - QString minKeyNameSufix; - QColor pointerColor; - bool rhythmsEnabled; - Tclef::EclefType& clef; /**< preferred clef - treble by default, reference to static Tclef::defaultType */ - bool isSingleNoteMode; /**< score mode (single or multi) */ - qreal scoreScale; /**< score scale - user preferred staff size */ - int tempo; /**< playback tempo */ - QString lastXmlDir; - -//============= common with score widget and note name ========================================== - bool doubleAccidentalsEnabled; /**< default @p TRUE */ - bool showEnharmNotes; /**< default @p TRUE */ + //============ score widget settings ============================================================= + /** + * if true shows other similar (enharmonic) notes on the staff: + * for C# - Db, for G - Fx and Abb. In Tnotename widget also. + */ + bool keySignatureEnabled; + bool showKeySignName; /**< default true */ + Tnote::EnameStyle nameStyleInKeySign; - /** - * On the very beginning it is -1 and then it is set in TscoreWidget constructor - * as inversion of highlight color from palette() and put to TnoteName, - * otherwise is taken from saved settings. - */ - QColor enharmNotesColor; - bool seventhIs_B; /**< To determine note names - default @p TRUE */ + /** + * Convention is: + * if keyNameSuffix == " " constructor of Tglobals sets its default and + * constructor of TkeySignatureView sets translatable value "major" and "minor" + * otherwise it is overridden by loading settings + * if keyNameSuffix == "" user prefers without suffix. + * If keyNameSuffix has some text - is translated by user himself + */ + QString majKeyNameSufix; + QString minKeyNameSufix; + QColor pointerColor; + bool rhythmsEnabled; + Tclef::EclefType &clef; /**< preferred clef - treble by default, reference to static Tclef::defaultType */ + bool isSingleNoteMode; /**< score mode (single or multi) */ + qreal scoreScale; /**< score scale - user preferred staff size */ + int tempo; /**< playback tempo */ + QString lastXmlDir; + //============= common with score widget and note name ========================================== + bool doubleAccidentalsEnabled; /**< default @p TRUE */ + bool showEnharmNotes; /**< default @p TRUE */ -//======== note name settings =================================================================== - Tnote::EnameStyle nameStyleInNoteName; - bool octaveInNoteNameFormat; /**< default @p TRUE */ - Tnote::EnameStyle solfegeStyle; /**< e_italiano_Si is default */ - bool namesOnScore; /**< show/hide note names on the score */ - QColor nameColor; /**< color of note name highlight */ + /** + * On the very beginning it is -1 and then it is set in TscoreWidget constructor + * as inversion of highlight color from palette() and put to TnoteName, + * otherwise is taken from saved settings. + */ + QColor enharmNotesColor; + bool seventhIs_B; /**< To determine note names - default @p TRUE */ - /** - * Determines whether to use International Pitch Notation, - * when octaves are numbered from 0 (-3 here, sub contra octave). - * By default it is @p FALSE - */ - bool scientificOctaves; + //======== note name settings =================================================================== + Tnote::EnameStyle nameStyleInNoteName; + bool octaveInNoteNameFormat; /**< default @p TRUE */ + Tnote::EnameStyle solfegeStyle; /**< e_italiano_Si is default */ + bool namesOnScore; /**< show/hide note names on the score */ + QColor nameColor; /**< color of note name highlight */ + /** + * Determines whether to use International Pitch Notation, + * when octaves are numbered from 0 (-3 here, sub contra octave). + * By default it is @p FALSE + */ + bool scientificOctaves; }; #endif // TSCOREPARAMS_H diff --git a/src/libs/sound/tsound.cpp b/src/libs/sound/tsound.cpp index 47ec3c377bd89ba889f50b3ed41ad4b34f6a5af4..8e82459d5ae8b1653936e74a4f6cf93c6c122c61 100644 --- a/src/libs/sound/tsound.cpp +++ b/src/libs/sound/tsound.cpp @@ -73,7 +73,7 @@ Tsound::Tsound(QObject* parent) : qmlRegisterType<TnotesBarItem>("Nootka", 1, 0, "TnotesBarItem"); #endif - setQuantization(GLOB->A->quantization); + setQuantization(GLOB->audioParams->quantization); } Tsound::~Tsound() @@ -83,7 +83,7 @@ Tsound::~Tsound() m_instance = nullptr; #if !defined (Q_OS_ANDROID) if (!m_dumpPath.isEmpty()) - GLOB->A->dumpPath.clear(); + GLOB->audioParams->dumpPath.clear(); #endif } @@ -94,11 +94,11 @@ Tsound::~Tsound() void Tsound::init() { QTimer::singleShot(500, this, [=]{ #if !defined (Q_OS_ANDROID) && (defined (Q_OS_LINUX) || defined (Q_OS_WIN)) - TrtAudio::initJACKorASIO(GLOB->A->JACKorASIO); + TrtAudio::initJACKorASIO(GLOB->audioParams->JACKorASIO); #endif - if (GLOB->A->OUTenabled) + if (GLOB->audioParams->OUTenabled) createPlayer(); - if (GLOB->A->INenabled) + if (GLOB->audioParams->INenabled) createSniffer(); connect(NOO, &TnootkaQML::playNote, this, &Tsound::play); @@ -206,12 +206,12 @@ qreal Tsound::pitchDeviation() { void Tsound::acceptSettings() { bool doParamsUpdated = false; // for output - if (GLOB->A->OUTenabled) { + if (GLOB->audioParams->OUTenabled) { if (!player) createPlayer(); else { #if !defined (Q_OS_ANDROID) - if (GLOB->A->midiEnabled) { + if (GLOB->audioParams->midiEnabled) { deletePlayer(); // it is safe to delete midi createPlayer(); // and create it again } else @@ -233,7 +233,7 @@ void Tsound::acceptSettings() { deletePlayer(); // for input - if (GLOB->A->INenabled) { + if (GLOB->audioParams->INenabled) { if (!sniffer) { createSniffer(); } else { @@ -359,7 +359,7 @@ void Tsound::runMetronome(int preTicksNr) { void Tsound::setQuantization(int q) { if ((/*q == 4 || */q == 6 || q == 12) && q != m_quantVal) { m_quantVal = q; - GLOB->A->quantization = m_quantVal; + GLOB->audioParams->quantization = m_quantVal; emit quantizationChanged(); } } @@ -516,7 +516,7 @@ void Tsound::setTunerMode(bool isTuner) { m_tunerMode = isTuner; emit tunerModeChanged(); if (!m_tunerMode && player) // approve changed middle A frequency (if any) - player->setPitchOffset(GLOB->A->a440diff - static_cast<qreal>(static_cast<int>(GLOB->A->a440diff))); + player->setPitchOffset(GLOB->audioParams->a440diff - static_cast<qreal>(static_cast<int>(GLOB->audioParams->a440diff))); } } @@ -556,14 +556,14 @@ void Tsound::setTouchHandling(bool th) { void Tsound::changeDumpPath(const QString& path) { if (QFileInfo(path).exists()) { m_dumpPath = path; - GLOB->A->dumpPath = path; + GLOB->audioParams->dumpPath = path; } else qDebug() << "[Tsound] dump path" << path << "does not exist!"; } void Tsound::setDumpFileName(const QString& fName) { - if (sniffer && !GLOB->A->dumpPath.isEmpty()) + if (sniffer && !GLOB->audioParams->dumpPath.isEmpty()) sniffer->setDumpFileName(fName); } #endif @@ -574,7 +574,7 @@ void Tsound::setDumpFileName(const QString& fName) { //################################################################################################# void Tsound::createPlayer() { - player = new TaudioOUT(GLOB->A); + player = new TaudioOUT(GLOB->audioParams); connect(player, &TaudioOUT::playingStarted, this, &Tsound::playingStartedSlot); connect(player, &TaudioOUT::nextNoteStarted, this, &Tsound::selectNextNote); connect(player, &TaudioOUT::playingFinished, this, &Tsound::playingFinishedSlot); @@ -588,7 +588,7 @@ void Tsound::createSniffer() { sniffer = TaudioIN::instance(); else #endif - sniffer = new TaudioIN(GLOB->A); + sniffer = new TaudioIN(GLOB->audioParams); setDefaultAmbitus(); // sniffer->setAmbitus(Tnote(-31), Tnote(82)); // fixed ambitus bounded Tartini capacities connect(sniffer, &TaudioIN::noteStarted, this, &Tsound::noteStartedSlot); diff --git a/src/main/texamexecutor.cpp b/src/main/texamexecutor.cpp index f832d43edaae6eca5d6959e0fdc9cdcb2ca493fc..c7c2bd13e3d78e472f2859ef2c2141fc4aa6d93a 100644 --- a/src/main/texamexecutor.cpp +++ b/src/main/texamexecutor.cpp @@ -86,7 +86,7 @@ QString getExamFileName(Texam* e) { if (GLOB->E->examsDir.isEmpty()) GLOB->E->examsDir = Tandroid::getExternalPath(); #endif - auto fName = QDir::toNativeSeparators(GLOB->E->examsDir + QLatin1String("/") + e->userName() + QLatin1String("-") + e->level()->name); + auto fName = QDir::toNativeSeparators(GLOB->examParams->examsDir + QLatin1String("/") + e->userName() + QLatin1String("-") + e->level()->name); fName = fName.replace(QLatin1String("."), QString()); //HACK: file dialogues don't like dots in the names if (QFileInfo::exists(fName + QLatin1String(".noo"))) fName += QLatin1String("-") + QDateTime::currentDateTime().toString(QLatin1String("(dd-MMM-hhmmss)")); @@ -126,7 +126,7 @@ void TexamExecutor::init(TexamExecutor::EexecOrigin whatToDo, const QVariant& ar } if (whatToDo == NewExam || whatToDo == StartExercise) { - m_exam = new Texam(&m_level, GLOB->E->studentName); + m_exam = new Texam(&m_level, GLOB->examParams->studentName); m_exam->setTune(*GLOB->Gtune()); if (whatToDo == StartExercise) m_exercise = new Texercises(m_exam); @@ -169,8 +169,8 @@ TexamExecutor::~TexamExecutor() { void TexamExecutor::continueInit() { m_summaryReason = NoReason; - if (GLOB->E->studentName.isEmpty()) - GLOB->E->studentName = GLOB->systemUserName(); + if (GLOB->examParams->studentName.isEmpty()) + GLOB->examParams->studentName = GLOB->systemUserName(); m_glStore = new TglobalExamStore(GLOB); /** In @p TglobalExamStore constructor We check are guitar's params suitable for an exam */ #if !defined (Q_OS_ANDROID) auto mess = TexecutorSupply::checkInstrumentParamsChange(m_exam); @@ -237,7 +237,7 @@ void TexamExecutor::initializeExecuting() { m_penalty = new Tpenalty(m_exam, m_supp); connect(m_penalty, &Tpenalty::certificate, this, &TexamExecutor::displayCertificate, Qt::UniqueConnection); if (m_exercise) { - if (GLOB->E->suggestExam) + if (GLOB->examParams->suggestExam) m_exercise->setSuggestionEnabled(m_supp->qaPossibilities(), m_exam->melodies()); } else { connect(m_tipHandler, &TtipHandler::certificateMagicKeys, this, &TexamExecutor::displayCertificate, Qt::UniqueConnection); @@ -245,11 +245,11 @@ void TexamExecutor::initializeExecuting() { // SCORE->enableAccidToKeyAnim(false); } if (m_level.requireStyle) { - m_prevQuestStyle = m_supp->randomNameStyle(GLOB->S->nameStyleInNoteName); + m_prevQuestStyle = m_supp->randomNameStyle(GLOB->scoreParams->nameStyleInNoteName); m_prevAnswStyle = m_supp->randomNameStyle(m_prevQuestStyle); } else { - m_prevQuestStyle = GLOB->S->nameStyleInNoteName; - m_prevAnswStyle = GLOB->S->nameStyleInNoteName; + m_prevQuestStyle = GLOB->scoreParams->nameStyleInNoteName; + m_prevAnswStyle = GLOB->scoreParams->nameStyleInNoteName; } m_level.questionAs.randNext(); // Randomize question and answer type @@ -273,7 +273,7 @@ void TexamExecutor::askQuestion(bool isAttempt) { // return; // m_lockRightButt = false; // release mouse button events - if (m_exercise && !GLOB->E->showCorrected && m_correctAct) // hide correct action button + if (m_exercise && !GLOB->examParams->showCorrected && m_correctAct) // hide correct action button m_correctAct->setEnabled(false); // if (m_exam->count() && m_exercise && m_exam->melodies()) // disconnect(SCORE, &TmainScore::lockedNoteClicked, this, &TexamExecutor::correctNoteOfMelody); @@ -300,7 +300,7 @@ void TexamExecutor::askQuestion(bool isAttempt) { deleteExam(); return; } - if (!GLOB->E->autoNextQuest) { + if (!GLOB->examParams->autoNextQuest) { if (!m_exercise) m_stopExamAct->setEnabled(false); m_tipHandler->clearTips(); @@ -311,8 +311,8 @@ void TexamExecutor::askQuestion(bool isAttempt) { m_answRequire.key = false; if (NOTENAME) { - NOTENAME->setNameStyle(GLOB->S->nameStyleInNoteName); - NOTENAME->setButtonNameStyle(GLOB->S->nameStyleInNoteName); + NOTENAME->setNameStyle(GLOB->scoreParams->nameStyleInNoteName); + NOTENAME->setButtonNameStyle(GLOB->scoreParams->nameStyleInNoteName); } m_penalty->nextQuestion(); @@ -472,17 +472,17 @@ void TexamExecutor::askQuestion(bool isAttempt) { curQ->setStyle(m_prevQuestStyle, m_supp->randomNameStyle(m_prevQuestStyle)); // randomize style m_prevAnswStyle= curQ->styleOfAnswer(); } else { // enharmonic notes in the same style - curQ->setStyle(GLOB->S->nameStyleInNoteName, GLOB->S->nameStyleInNoteName); - m_prevAnswStyle = GLOB->S->nameStyleInNoteName; - m_prevQuestStyle = GLOB->S->nameStyleInNoteName; + curQ->setStyle(GLOB->scoreParams->nameStyleInNoteName, GLOB->scoreParams->nameStyleInNoteName); + m_prevAnswStyle = GLOB->scoreParams->nameStyleInNoteName; + m_prevQuestStyle = GLOB->scoreParams->nameStyleInNoteName; } } else { // note name only in question if (m_level.requireStyle) { // switch previous used style - curQ->setStyle(m_supp->randomNameStyle(m_prevQuestStyle), GLOB->S->nameStyleInNoteName); + curQ->setStyle(m_supp->randomNameStyle(m_prevQuestStyle), GLOB->scoreParams->nameStyleInNoteName); m_prevQuestStyle = curQ->styleOfQuestion(); } else { - curQ->setStyle(GLOB->S->nameStyleInNoteName, curQ->styleOfAnswer()); - m_prevQuestStyle = GLOB->S->nameStyleInNoteName; + curQ->setStyle(GLOB->scoreParams->nameStyleInNoteName, curQ->styleOfAnswer()); + m_prevQuestStyle = GLOB->scoreParams->nameStyleInNoteName; } } } @@ -665,7 +665,7 @@ void TexamExecutor::checkAnswer(bool showResults) { if (m_exam->melodies() && SOUND->melodyIsPlaying()) SOUND->stopPlaying(); - if (!GLOB->E->autoNextQuest || m_exercise) + if (!GLOB->examParams->autoNextQuest || m_exercise) m_stopExamAct->setEnabled(true); m_isAnswered = true; // Let's check @@ -788,8 +788,8 @@ void TexamExecutor::checkAnswer(bool showResults) { m_penalty->checkAnswer(); disableWidgets(); - bool autoNext = GLOB->E->autoNextQuest; - if (GLOB->E->afterMistake == TexamParams::e_stop && !curQ->isCorrect()) + bool autoNext = GLOB->examParams->autoNextQuest; + if (GLOB->examParams->afterMistake == TexamParams::e_stop && !curQ->isCorrect()) autoNext = false; // when mistake and e_stop - the same like autoNext = false; if (showResults) { @@ -807,12 +807,12 @@ void TexamExecutor::checkAnswer(bool showResults) { } markAnswer(curQ); - int waitTime = GLOB->E->questionDelay; + int waitTime = GLOB->examParams->questionDelay; if (m_melody) // increase minimal delay before next question for melodies to 500ms waitTime = qMax(waitTime, 500); if (m_exercise) { - if ((GLOB->E->autoNextQuest && GLOB->E->afterMistake != TexamParams::e_continue) || !GLOB->E->autoNextQuest || GLOB->E->showCorrected) - waitTime = GLOB->E->correctPreview; // user has to have time to see his mistake and correct answer + if ((GLOB->examParams->autoNextQuest && GLOB->examParams->afterMistake != TexamParams::e_continue) || !GLOB->examParams->autoNextQuest || GLOB->examParams->showCorrected) + waitTime = GLOB->examParams->correctPreview; // user has to have time to see his mistake and correct answer if (!curQ->isCorrect()) { // correcting wrong answer if (GLOB->correctAnswers()) // TODO for dictation it should always stop and show mistakes correctAnswer(); @@ -834,9 +834,9 @@ void TexamExecutor::checkAnswer(bool showResults) { stopExamSlot(); else { if (curQ->isCorrect()) { - m_askingTimer->start(m_melody ? qMax(GLOB->E->questionDelay, 500) : waitTime /*GLOB->E->questionDelay*/); + m_askingTimer->start(m_melody ? qMax(GLOB->examParams->questionDelay, 500) : waitTime /*GLOB->E->questionDelay*/); } else { - if (GLOB->E->repeatIncorrect && !m_incorrectRepeated) { + if (GLOB->examParams->repeatIncorrect && !m_incorrectRepeated) { if (curQ->melody()) QTimer::singleShot(waitTime, this, [=]{ newAttempt(); }); else { @@ -1035,9 +1035,9 @@ void TexamExecutor::markAnswer(TQAunit* curQ) { auto n = INSTRUMENT->note(); // Take it from user answer if (GLOB->preferFlats()) n = n.showWithFlat(); - INSTRUMENT->showNoteName(GLOB->S->nameStyleInNoteName, n, INSTRUMENT->technical(), markColor); + INSTRUMENT->showNoteName(GLOB->scoreParams->nameStyleInNoteName, n, INSTRUMENT->technical(), markColor); } else if (curQ->answerAsSound() && curQ->questionOnInstr()) - INSTRUMENT->showNoteName(GLOB->S->nameStyleInNoteName, curQ->qa.note, curQ->qa.technical.data(), markColor); + INSTRUMENT->showNoteName(GLOB->scoreParams->nameStyleInNoteName, curQ->qa.note, curQ->qa.technical.data(), markColor); } else { // cases when name was an question if (curQ->questionAsName()) { if (curQ->answerOnScore()) @@ -1067,7 +1067,7 @@ void TexamExecutor::repeatQuestion() { // for melodies it never comes here - questions are newer repeated - copying of TQAunit is safe TQAunit curQ(*CURR_Q); // copy last unit as a new one - if (!GLOB->E->autoNextQuest) + if (!GLOB->examParams->autoNextQuest) m_tipHandler->clearTips(); curQ.setMistake(TQAunit::e_correct); @@ -1208,13 +1208,13 @@ void TexamExecutor::afterMessage() { void TexamExecutor::restoreAfterExam() { #if !defined (Q_OS_ANDROID) - if (!GLOB->A->dumpPath.isEmpty()) + if (!GLOB->audioParams->dumpPath.isEmpty()) SOUND->setDumpFileName(QLatin1String("nootka_dump")); #endif m_glStore->restoreSettings(); if (m_exercise) { - GLOB->E->suggestExam = m_exercise->suggestInFuture(); + GLOB->examParams->suggestExam = m_exercise->suggestInFuture(); } @@ -1224,7 +1224,7 @@ void TexamExecutor::restoreAfterExam() { if (NOTENAME) { NOTENAME->setDisabled(false); - NOTENAME->setButtonNameStyle(GLOB->S->nameStyleInNoteName); + NOTENAME->setButtonNameStyle(GLOB->scoreParams->nameStyleInNoteName); } if (INSTRUMENT) { INSTRUMENT->restoreAfterExam(); @@ -1264,7 +1264,7 @@ void TexamExecutor::clearWidgets() { INSTRUMENT->setNote(Tnote()); INSTRUMENT->markSelected(Qt::transparent); if (!INSTRUMENT->extraNoteName().isEmpty()) - INSTRUMENT->showNoteName(GLOB->S->nameStyleInNoteName, Tnote(), NO_TECHNICALS, Qt::transparent); + INSTRUMENT->showNoteName(GLOB->scoreParams->nameStyleInNoteName, Tnote(), NO_TECHNICALS, Qt::transparent); } } @@ -1394,8 +1394,8 @@ void TexamExecutor::stopExerciseSlot() { if (m_isAnswered && CURR_Q->melody() && CURR_Q->answerOnScore() && !CURR_Q->isCorrect()) CURR_Q->melody()->setTitle(CURR_Q->melody()->title() + QLatin1String(";skip")); m_penalty->updateExamTimes(); - m_exerciseTmpStyle = GLOB->S->nameStyleInNoteName; - GLOB->S->nameStyleInNoteName = m_glStore->nameStyleInNoteName; // restore to show charts in user defined style + m_exerciseTmpStyle = GLOB->scoreParams->nameStyleInNoteName; + GLOB->scoreParams->nameStyleInNoteName = m_glStore->nameStyleInNoteName; // restore to show charts in user defined style m_summaryReason = SumFinishExer; if (!m_goingClosed) @@ -1422,7 +1422,7 @@ void TexamExecutor::restoreExerciseAfterSummary() { CURR_Q->melody()->setTitle(CURR_Q->melody()->title().remove(QLatin1String(";skip"))); if (m_isAnswered) CURR_Q->setAnswered(); - GLOB->S->nameStyleInNoteName = m_exerciseTmpStyle; + GLOB->scoreParams->nameStyleInNoteName = m_exerciseTmpStyle; // #if !defined (Q_OS_ANDROID) // qApp->installEventFilter(m_supp); // #endif @@ -1446,7 +1446,7 @@ void TexamExecutor::continueExercise() { void TexamExecutor::stopExamSlot() { - if (!m_isAnswered && !GLOB->E->closeWithoutConfirm) { + if (!m_isAnswered && !GLOB->examParams->closeWithoutConfirm) { m_shouldBeTerminated = true; int messageDuration = 3000; NOO->setMessageColor(GLOB->EquestionColor); @@ -1465,19 +1465,19 @@ void TexamExecutor::stopExamSlot() { } } if (m_exam->fileName().isEmpty()) { - if (GLOB->E->closeWithoutConfirm) { + if (GLOB->examParams->closeWithoutConfirm) { m_exam->setFileName(getExamFileName(m_exam) + QLatin1String(".noo")); } else { m_exam->setFileName(saveExamToFile()); if (!m_exam->fileName().isEmpty()) - GLOB->E->examsDir = QFileInfo(m_exam->fileName()).absoluteDir().absolutePath(); + GLOB->examParams->examsDir = QFileInfo(m_exam->fileName()).absoluteDir().absolutePath(); } } if (!m_exam->fileName().isEmpty()) { if (m_exam->melodies()) // summarize answer if not summarized yet (melodies can have such cases) m_penalty->setMelodyPenalties(); m_penalty->updateExamTimes(); - GLOB->S->nameStyleInNoteName = m_glStore->nameStyleInNoteName; // restore to show in user defined style + GLOB->scoreParams->nameStyleInNoteName = m_glStore->nameStyleInNoteName; // restore to show in user defined style if (m_exam->saveToFile() == Texam::e_file_OK) { QStringList recentExams = GLOB->config->value(QLatin1String("recentExams")).toStringList(); recentExams.removeAll(m_exam->fileName()); @@ -1521,12 +1521,12 @@ void TexamExecutor::prepareToSettings() { void TexamExecutor::settingsAccepted() { if (m_exercise) { - if (GLOB->E->suggestExam) + if (GLOB->examParams->suggestExam) m_exercise->setSuggestionEnabled(m_supp->qaPossibilities(), m_exam->melodies()); else m_exercise->setSuggestionEnabled(0); } else { - if (GLOB->E->autoNextQuest) + if (GLOB->examParams->autoNextQuest) m_stopExamAct->setEnabled(true); } if (m_exam->count() && CURR_Q->answerAsSound() && !SOUND->isSnifferPaused())//!SOUND->pitchView()->isPaused()) @@ -1579,9 +1579,9 @@ bool TexamExecutor::closeNootka() { "or <b>%2</b> to continue.<br>").arg(qTR("QShortcut", "Save"), qTR("QPlatformTheme", "Retry"))); msg->setStandardButtons(QMessageBox::Retry | QMessageBox::Save); msg->setWindowTitle(QStringLiteral("Psssst...")); - if (!GLOB->E->closeWithoutConfirm) + if (!GLOB->examParams->closeWithoutConfirm) msg->exec(); - if (!GLOB->E->closeWithoutConfirm && msg->clickedButton() == msg->button(QMessageBox::Retry)) { + if (!GLOB->examParams->closeWithoutConfirm && msg->clickedButton() == msg->button(QMessageBox::Retry)) { m_snifferLocked = false; // #if !defined (Q_OS_ANDROID) // qApp->installEventFilter(m_supp); @@ -1721,7 +1721,7 @@ void TexamExecutor::noteOfMelodyFinished(const TnoteStruct& n) { if ((waitForCorrect && m_melody->currentIndex() == CURR_Q->melody()->length() - 1) || m_melodySelectionIndex > CURR_Q->melody()->length()) { if (waitForCorrect && !m_melody->wasLatestNoteSet()) return; - if (GLOB->E->expertsAnswerEnable) + if (GLOB->examParams->expertsAnswerEnable) checkAnswer(); else { m_tipHandler->playMelodyAgainMessage(); @@ -1783,7 +1783,7 @@ void TexamExecutor::startSniffing() { if (m_soundTimer->isActive()) m_soundTimer->stop(); #if !defined (Q_OS_ANDROID) - if (CURR_Q->answerAsSound() && !GLOB->A->dumpPath.isEmpty()) { + if (CURR_Q->answerAsSound() && !GLOB->audioParams->dumpPath.isEmpty()) { QString dumpFileName = QString("Question-%1").arg(m_exam->count(), 3, 'i', 0, '0'); if (m_melody) dumpFileName += QString("-attempt%1").arg(CURR_Q->attemptsCount()); @@ -1798,7 +1798,7 @@ void TexamExecutor::startSniffing() { void TexamExecutor::expertAnswersSlot() { - if (!GLOB->E->expertsAnswerEnable && !m_exam->melodies()) { // no expert and no melodies + if (!GLOB->examParams->expertsAnswerEnable && !m_exam->melodies()) { // no expert and no melodies m_tipHandler->showConfirmTip(1500); return; } @@ -1946,18 +1946,18 @@ void TexamExecutor::correctionFinishedSlot() { // } // } m_nextQuestAct->setEnabled(true); - if (GLOB->E->autoNextQuest && GLOB->E->afterMistake != TexamParams::e_stop && !CURR_Q->melody()) { - m_askingTimer->start(GLOB->E->correctPreview); // new question will be started after preview time + if (GLOB->examParams->autoNextQuest && GLOB->examParams->afterMistake != TexamParams::e_stop && !CURR_Q->melody()) { + m_askingTimer->start(GLOB->examParams->correctPreview); // new question will be started after preview time } if (CURR_Q->melody()) { // despite of 'auto' settings when melody - auto next question will not work m_tipHandler->showWhatNextTip(false, false); // connect(SCORE, &TmainScore::lockedNoteClicked, this, &TexamExecutor::correctNoteOfMelody); // only once per answer - } else if (!GLOB->E->autoNextQuest || GLOB->E->afterMistake == TexamParams::e_stop) - m_tipHandler->showWhatNextTip(!(!m_exercise && GLOB->E->repeatIncorrect && !m_incorrectRepeated)); + } else if (!GLOB->examParams->autoNextQuest || GLOB->examParams->afterMistake == TexamParams::e_stop) + m_tipHandler->showWhatNextTip(!(!m_exercise && GLOB->examParams->repeatIncorrect && !m_incorrectRepeated)); if (INSTRUMENT && m_exercise && GLOB->extraNames() && !CURR_Q->melody()) { bool isQa2 = CURR_Q->questionOnInstr() && CURR_Q->answerOnInstr(); - INSTRUMENT->showNoteName(GLOB->S->nameStyleInNoteName, + INSTRUMENT->showNoteName(GLOB->scoreParams->nameStyleInNoteName, isQa2 ? CURR_Q->qa_2.note : CURR_Q->qa.note, isQa2 ? CURR_Q->qa_2.technical.data() : CURR_Q->qa.technical.data(), GLOB->correctColor()); diff --git a/src/main/texecutorsupply.cpp b/src/main/texecutorsupply.cpp index 03d545168e47d01e74e6f30af327741be102eb21..ecd52921ee0ec3de981546a06adfe7ab185d290b 100644 --- a/src/main/texecutorsupply.cpp +++ b/src/main/texecutorsupply.cpp @@ -437,12 +437,12 @@ Tnote::EnameStyle TexecutorSupply::randomNameStyle(int style) { if (m_isSolfege) { m_isSolfege = false; if (QRandomGenerator::global()->bounded(2)) { // full name like cis, gisis - if (GLOB->S->seventhIs_B) + if (GLOB->scoreParams->seventhIs_B) return Tnote::e_nederl_Bis; else return Tnote::e_deutsch_His; } else { // name and sign like c#, gx - if (GLOB->S->seventhIs_B) + if (GLOB->scoreParams->seventhIs_B) return Tnote::e_english_Bb; else return Tnote::e_norsk_Hb; diff --git a/src/main/tglobalexamstore.cpp b/src/main/tglobalexamstore.cpp index 9adb9bd9be9271846fd14a6f1002be7014479295..d15134a09a70e1f9ddfedf5c2661c3c0f2cf23ba 100644 --- a/src/main/tglobalexamstore.cpp +++ b/src/main/tglobalexamstore.cpp @@ -36,22 +36,22 @@ TglobalExamStore::TglobalExamStore(Tglobals* globals) : void TglobalExamStore::storeSettings() { - nameStyleInNoteName = m_globals->S->nameStyleInNoteName; - showEnharmNotes = m_globals->S->showEnharmNotes; - showKeySignName = m_globals->S->showKeySignName; + nameStyleInNoteName = m_globals->scoreParams->nameStyleInNoteName; + showEnharmNotes = m_globals->scoreParams->showEnharmNotes; + showKeySignName = m_globals->scoreParams->showKeySignName; showOtherPos = m_globals->GshowOtherPos; - useDblAccids = m_globals->S->doubleAccidentalsEnabled; - useKeySign = m_globals->S->keySignatureEnabled; - octaveInName = m_globals->S->octaveInNoteNameFormat; - clef = Tclef(m_globals->S->clef); - intonation = m_globals->A->intonation; - if (m_globals->A->midiEnabled) - playbackInstr = m_globals->A->midiInstrNr; + useDblAccids = m_globals->scoreParams->doubleAccidentalsEnabled; + useKeySign = m_globals->scoreParams->keySignatureEnabled; + octaveInName = m_globals->scoreParams->octaveInNoteNameFormat; + clef = Tclef(m_globals->scoreParams->clef); + intonation = m_globals->audioParams->intonation; + if (m_globals->audioParams->midiEnabled) + playbackInstr = m_globals->audioParams->midiInstrNr; else - playbackInstr = m_globals->A->audioInstrNr; - namesOnScore = m_globals->S->namesOnScore; - OUTenabled = m_globals->A->OUTenabled; - INenabled = m_globals->A->INenabled; + playbackInstr = m_globals->audioParams->audioInstrNr; + namesOnScore = m_globals->scoreParams->namesOnScore; + OUTenabled = m_globals->audioParams->OUTenabled; + INenabled = m_globals->audioParams->INenabled; enableRhythms = m_globals->rhythmsEnabled(); quantization = SOUND->quantization(); showNotesDiff = m_globals->showNotesDiff(); @@ -63,19 +63,19 @@ void TglobalExamStore::restoreSettings() { m_globals->setShowKeyName(showKeySignName); m_globals->setEnableDoubleAccids(useDblAccids); m_globals->setKeySignatureEnabled(useKeySign); - m_globals->S->octaveInNoteNameFormat = octaveInName; + m_globals->scoreParams->octaveInNoteNameFormat = octaveInName; m_globals->setClefType(static_cast<int>(clef.type())); m_globals->setSingleNote(isSingleNoteMode); m_globals->setNamesOnScore(namesOnScore); - m_globals->A->intonation = intonation; + m_globals->audioParams->intonation = intonation; m_globals->GshowOtherPos = showOtherPos; m_globals->setGuitarParams(fretsNumber, tune); - if (m_globals->A->midiEnabled) - m_globals->A->midiInstrNr = playbackInstr; + if (m_globals->audioParams->midiEnabled) + m_globals->audioParams->midiInstrNr = playbackInstr; else - m_globals->A->audioInstrNr = playbackInstr; - m_globals->A->INenabled = INenabled; - m_globals->A->OUTenabled = OUTenabled; + m_globals->audioParams->audioInstrNr = playbackInstr; + m_globals->audioParams->INenabled = INenabled; + m_globals->audioParams->OUTenabled = OUTenabled; m_globals->setRhythmsEnabled(enableRhythms); SOUND->setQuantization(quantization); m_globals->setShowNotesDiff(showNotesDiff); @@ -91,14 +91,14 @@ void TglobalExamStore::prepareGlobalsToExam(const Tlevel& level) { m_globals->setClefType(static_cast<int>(level.clef.type())); m_globals->setNamesOnScore(false); if (level.answerIsSound()) { - if (!m_globals->A->INenabled) { - m_globals->A->INenabled = true; + if (!m_globals->audioParams->INenabled) { + m_globals->audioParams->INenabled = true; } } - if (level.questionAs.isSound() && !m_globals->A->OUTenabled) { - m_globals->A->OUTenabled = true; + if (level.questionAs.isSound() && !m_globals->audioParams->OUTenabled) { + m_globals->audioParams->OUTenabled = true; } - m_globals->A->intonation = level.intonation; + m_globals->audioParams->intonation = level.intonation; m_globals->setShowNotesDiff(false); // m_globals->setRhythmsEnabled(false); // TODO: Read it from level when will be implemented // change output instrument type when necessary (exam instrument differs from user) TODO diff --git a/src/main/tstartexamitem.cpp b/src/main/tstartexamitem.cpp index e9ba792b219129de654846eab2bac60ee5e02b43..d60c9eba5901a8e2d42b0d9ed0a86fae37ce9e34 100644 --- a/src/main/tstartexamitem.cpp +++ b/src/main/tstartexamitem.cpp @@ -128,7 +128,7 @@ void TstartExamItem::examFromFileDialog() { GLOB->E->examsDir = Tandroid::getExternalPath(); QString fileName = TfileDialog::getOpenFileName(GLOB->E->examsDir, QStringLiteral("noo")); #else - QString fileName = TfileDialog::getOpenFileName(TexTrans::loadExamFileTxt(), GLOB->E->examsDir, TexTrans::examFilterTxt()); + QString fileName = TfileDialog::getOpenFileName(TexTrans::loadExamFileTxt(), GLOB->examParams->examsDir, TexTrans::examFilterTxt()); #endif if (!fileName.isEmpty()) examToContSelected(fileName); @@ -137,7 +137,7 @@ void TstartExamItem::examFromFileDialog() { void TstartExamItem::examToContSelected(const QString& eFile) { if (!eFile.isEmpty()) { - GLOB->E->examsDir = QFileInfo(eFile).absoluteDir().absolutePath(); + GLOB->examParams->examsDir = QFileInfo(eFile).absoluteDir().absolutePath(); m_recentExams.prepend(eFile); m_selectedExamFile = eFile; emit continueExam(eFile); diff --git a/src/main/ttiphandler.cpp b/src/main/ttiphandler.cpp index 020c4cc8bdef26e0516f17c3104ca6e04b88455c..d5aa99bba09ea2d278bc7c69cb40a0b8607f9576 100644 --- a/src/main/ttiphandler.cpp +++ b/src/main/ttiphandler.cpp @@ -209,14 +209,14 @@ void TtipHandler::showResultTip(TQAunit* answer, int time) { deleteResultTip(); deleteConfirmTip(); - bool autoNext = GLOB->E->autoNextQuest; - if (GLOB->E->afterMistake == TexamParams::e_stop && !answer->isCorrect()) + bool autoNext = GLOB->examParams->autoNextQuest; + if (GLOB->examParams->afterMistake == TexamParams::e_stop && !answer->isCorrect()) autoNext = false; // when mistake and e_stop - the same like autoNext = false; if (autoNext) { // determine time of displaying - if (answer->isCorrect() || GLOB->E->afterMistake == TexamParams::e_continue) + if (answer->isCorrect() || GLOB->examParams->afterMistake == TexamParams::e_continue) time = 2500; // hard-coded else - time = GLOB->E->mistakePreview; // user defined wait time + time = GLOB->examParams->mistakePreview; // user defined wait time } emit wantResultTip(wasAnswerOKtext(answer), TexecutorSupply::answerColor(answer->mistake())); @@ -362,7 +362,7 @@ void TtipHandler::showQuestionTip() { #endif deleteWhatNextTip(); - if (!GLOB->E->autoNextQuest) + if (!GLOB->examParams->autoNextQuest) deleteResultTip(); deleteQuestionTip();