Skip to content
Snippets Groups Projects
tnootkaqml.cpp 23.9 KiB
Newer Older
/***************************************************************************
 *   Copyright (C) 2017-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/>.  *
 ***************************************************************************/

#include "score/taddnoteitem.h"
#include "instruments/tguitarbg.h"
#include "instruments/tpianobg.h"
#include "instruments/tbandoneonbg.h"
#include <QtCore/qbuffer.h>
#include <QtWidgets/qapplication.h>
#include <QtGui/qdesktopservices.h>
#if defined (Q_OS_ANDROID)
  #include "Android/tfiledialog.h"
  #include "Android/tmobilemenu.h"
#else
  #include <QtWidgets/qfiledialog.h>
#endif
TnootkaQML* TnootkaQML::m_instance = nullptr;
TnootkaQML::TnootkaQML(QObject* parent) :
  QObject(parent)
  if (m_instance) {
    qDebug() << "TnootkaQML instance already exists!";
    return;
  }
  m_instance = this;
  qsrand(QDateTime::currentDateTime().toTime_t());

  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."));

  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");
  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");
#if defined (Q_OS_ANDROID)
  qmlRegisterType<TmobileMenu>("Nootka", 1, 0, "TmobileMenu");
#endif

  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;
}

//#################################################################################################
//###################       INVOKABLE METHODS          ############################################
//#################################################################################################
QString TnootkaQML::version() { return NOOTKA_VERSION; }
Tclef TnootkaQML::clef(int type) {
  return Tclef(static_cast<Tclef::EclefType>(type));
}
Tmeter TnootkaQML::meter(int m) {
  return Tmeter(static_cast<Tmeter::Emeter>(m));
}


Tnote TnootkaQML::note(int pitch, int octave, int alter, int rhythm, bool rest, bool dot) {
  return Tnote(static_cast<char>(pitch), static_cast<char>(octave), static_cast<char>(alter),
               Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
Tnote TnootkaQML::note(const Tnote& n, int rhythm, bool rest, bool dot) {
  return Tnote(n, Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
}


Tnote TnootkaQML::note(const Tnote& n, const Trhythm& r) {
  return Tnote(n, r);
}


Tnote TnootkaQML::note(int chroma,  bool sharp) {
  Tnote n(static_cast<short>(chroma));
  if (!sharp && (n.alter() != 0 || (n.alter() == 0 && n.note() != 3 && n.note() != 7))) // but skip e and b - otherwise they become fb or cb
    n = n.showWithFlat();
  return n;
Tnote TnootkaQML::transpose(Tnote n, int semitones) {
  n.transpose(semitones);
  return n;
}



Trhythm TnootkaQML::rhythm(int rtm, bool rest, bool dot, bool triplet) {
  return Trhythm(static_cast<Trhythm::Erhythm>(rtm), rest, dot, triplet);
}


/**
 * It returns glyphs of 'Nootka' font in contrary to TnoteObject::getHeadText()
 */
QString TnootkaQML::rhythmText(const Trhythm& r) {
  if (r.rhythm() == Trhythm::NoRhythm)
    return QStringLiteral("z"); // just black note-head
  QString out;
  if (r.isRest())
    out = QString(QChar(0xe106 + static_cast<int>(r.rhythm())));
  else
    out = QString(QChar(66 + static_cast<int>(r.rhythm())));
  if (r.hasDot())
    out += QStringLiteral(".");
  return out;
}


QString TnootkaQML::noteName(const Tnote& n, int style, bool showOctave) {
  // Tnote::toText() method returns only names in user preferred 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::majorKeyName(int key) {
  return TkeySignature(static_cast<char>(key)).getMajorName();
}


QString TnootkaQML::minorKeyName(int key) {
  return TkeySignature(static_cast<char>(key)).getMinorName();
}


QString TnootkaQML::majAndMinKeyName(int key) {
  return majorKeyName(key) + QLatin1String("<br>") + minorKeyName(key);
QString TnootkaQML::getLicense() {
  QFile file(Tpath::main + QLatin1String("gpl"));
  QString license;
  QTextStream in;
  if (!file.exists()) { // Debian based
      QDir d(Tpath::main);
      d.cdUp();
      file.setFileName(d.path() + QLatin1String("/doc/nootka/copyright"));
  }
  if(file.open(QFile::ReadOnly | QFile::Text)) {
      QTextStream in(&file);
      in.setCodec("UTF-8");
      license = in.readAll();
  }
  file.close();
  return license;
}
QString TnootkaQML::getChanges() {
  QFile file(Tpath::main + QLatin1String("changes"));
  QString chLog;
  QTextStream in;
  if(file.open(QFile::ReadOnly | QFile::Text)) {
      QTextStream in(&file);
      in.setCodec("UTF-8");
      QStringList htmlText = in.readAll().replace(QLatin1String("  "), QLatin1String("&nbsp;&nbsp;")).split(QLatin1String("\n"));
      for (int i = 0; i < htmlText.size(); i++) {
        if (htmlText[i].startsWith(QLatin1String("0.")) || htmlText[i].startsWith(QLatin1String("1.")))
          htmlText[i] = QLatin1String("<span style=\"font-size: x-large; color: #0000ff\"><b>&nbsp;") + htmlText[i] + QLatin1String("</b></span>");
        else if (htmlText[i].contains(QLatin1String("======")))
          htmlText[i] = QStringLiteral("<br><hr><b><big><center>Nootka ONE</big></b></center><hr>");
        else if (htmlText[i].contains(QLatin1String("BUG")))
          htmlText[i] = QStringLiteral("&nbsp;&nbsp;<u>BUG FIXES</u>");
        else if (htmlText[i].contains(QLatin1String("Under the hood")))
          htmlText[i] = QStringLiteral("&nbsp;&nbsp;<u>Under the hood</u>");
        else if (!htmlText[i].contains(QLatin1String("&nbsp;&nbsp; - ")))
          htmlText[i] = QLatin1String("<b>") + htmlText[i] + QLatin1String("</b>");
        htmlText[i].append(QLatin1String("<br>"));
      }
      chLog = htmlText.join(QString());
  }
  file.close();
  return chLog;
}


bool TnootkaQML::isAndroid()  {
#if defined (Q_OS_ANDROID)
  return true;
#else
  return false;
#endif
}
bool TnootkaQML::isWindows() {
#if defined (Q_OS_WIN)
  return true;
#else
  return false;
#endif
}


bool TnootkaQML::isMac() {
#if defined (Q_OS_MACOS)
  return true;
#else
  return false;
#endif
}


QStringList TnootkaQML::guitarTunings() {
  QStringList tunList;
  int start = static_cast<int>(Ttune::Standard_EADGBE);
  for (int t = start; t < start + 5; ++t)
    tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
  tunList << QApplication::translate("InstrumentPage", "Custom tuning");
  return tunList;
}


QStringList TnootkaQML::bassTunings() {
  QStringList tunList;
  int start = static_cast<int>(Ttune::Bass4_EADG);
  for (int t = start; t < start + 4; ++t)
    tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
  tunList << QApplication::translate("InstrumentPage", "Custom tuning");
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];
  }
  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);
}


Tinstrument TnootkaQML::instr(int type) {
  return Tinstrument(static_cast<Tinstrument::Etype>(type < 0 || type > INSTR_COUNT - 1 ? 0 : type));
  QString openFile;
  openFile = TfileDialog::getOpenFileName(nullptr, GLOB->lastXmlDir(), QStringLiteral("xml"));
  openFile = QFileDialog::getOpenFileName(nullptr, qApp->translate("TmelMan", "Open melody file"), GLOB->lastXmlDir(),
                                      qApp->translate("TmelMan", "MusicXML file") + QLatin1String(" (*.xml)"));
  if (!openFile.isEmpty())
    GLOB->setLastXmlDir(QFileInfo(openFile).absoluteDir().path());
  return openFile;
  QString saveFile;
  saveFile = TfileDialog::getSaveFileName(nullptr, GLOB->lastXmlDir(), QStringLiteral("xml"));
  saveFile = QFileDialog::getSaveFileName(nullptr, qApp->translate("TmelMan", "Save melody as:"), GLOB->lastXmlDir(),
                                      qTR("TmelMan", "MusicXML file") + QLatin1String(" (*.xml)"));
  if (!saveFile.isEmpty())
    GLOB->setLastXmlDir(QFileInfo(saveFile).absoluteDir().path());
  return saveFile;
SeeLook's avatar
SeeLook committed


QString TnootkaQML::pix(const QString& imageFileName) {
  return Tpath::pix(imageFileName);
}
QString TnootkaQML::TR(const QString& context, const QString& text, const QString& disambiguation, int n) {
  return qTR(qPrintable(context), qPrintable(text), qPrintable(disambiguation), n);
}


QColor TnootkaQML::alpha(const QColor& c, int a) {
}


QColor TnootkaQML::randomColor(int alpha, int level) {
  return QColor(qrand() % level, qrand() % level, qrand() % level, alpha);
}
QColor TnootkaQML::invert(const QColor& c) {
  return Tcolor::invert(c);
}


  return (qApp->font().pixelSize() > 0 ? qApp->font().pixelSize() : qApp->font().pointSize()) * 0.8 * GLOB->guiScale();
  return Tmtr::fingerPixels() * 0.45 * GLOB->guiScale();
QString TnootkaQML::pixToHtml(const QString& pixName, int height) {
  if (height == 0)
    return QString("<img src=\"%1\">").arg(pixName);

  QPixmap pix;
  if (!pix.load(Tpath::img(qPrintable(pixName))))
    return QString();

  QByteArray byteArray;
  QBuffer buffer(&byteArray);
  pix.scaled(qRound(height * (static_cast<qreal>(pix.height()) / static_cast<qreal>(pix.width()))),
                               height, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "PNG");
  return QString("<img src=\"data:image/png;base64,") + byteArray.toBase64() + "\"/>";
}


void TnootkaQML::openHelpLink(const QString& hash) {
  QDesktopServices::openUrl(QUrl(QString("https://nootka.sourceforge.io/index.php?L=%1&C=doc#" + hash).
    arg(QString(qgetenv("LANG")).left(2).toLower()), QUrl::TolerantMode));
}


qreal TnootkaQML::bound(qreal min, qreal val, qreal max) {
  return qBound(min, val, max);
}


void TnootkaQML::setQmlEngine(QQmlEngine* e) {
  m_qmlEngine = e;
  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", "Levels 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("TtoolBar", "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);
  connect(m_scoreAct, &Taction::triggered, this, &TnootkaQML::scoreActTriggered);
  m_scoreAct->setTip(QApplication::translate("TtoolBar", "Manage and navigate the score."), QQuickItem::TopRight);
  m_melodyAct = new Taction(QApplication::translate("TtoolBar", "Melody"), QStringLiteral("melody"), this);
  connect(m_melodyAct, &Taction::triggered, this, &TnootkaQML::melodyActTriggered);
  m_melodyAct->setTip(tr("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);
/**
 * Opening files from command line argument starts here, but it is a bit clumsy:
 * - for Music XML is fine, we have @p TscoreObject here so just call @p openMusicXml()
 * - but for exam and level files only @p wantOpenFile() signal is emitted
 * - then @p MainWindow.qml handles it and creates @p DialogLoader.qml
 * - 'dialog loader' invokes @p TdialogLoaderObject::openFile() of its object
 * - and there the file is distinguished (exam or level) and appropriate signal is emitted
 * - again, 'dialog loader' handles those signals and creates 'exam executor' or 'level creator' apparently
 */
void TnootkaQML::openFile(const QString& runArg) {
  if (GLOB->isExam()) {
    qDebug() << "--- Exam or exercise is running. File cannot be opened! ---";
    return;
  }
  if (QFile::exists(runArg)) {
    QFile file(runArg);
    auto ext = QFileInfo(file).suffix();
    if (ext == QLatin1String("xml")) { // TODO mxl - compressed format
        auto fullPath = QDir(file.fileName()).absolutePath();
        m_scoreObject->openMusicXml(fullPath);
    } else {
        emit GLOB->wantOpenFile(runArg);
    }
  }
}


void TnootkaQML::setStatusTip(const QString& statusText, int tipPos) {
  if (GLOB->showHints())
    emit statusTip(statusText, tipPos);
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();
  }
}


//#################################################################################################
//###################     CONNECTIONS  NODE            ############################################
//#################################################################################################
void TnootkaQML::noteStarted(const Tnote& n) {
  Tnote transNote = n;
  if (transNote.isValid())
    transNote.transpose(-GLOB->transposition());
  if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
    transNote = transNote.showWithFlat();
  m_ignoreScore = true;
  if (m_scoreObject->singleNote()) {
      if (!transNote.isRest()) {
        transNote.setRhythm(Trhythm::NoRhythm);
        m_scoreObject->setNote(0, transNote);
      m_scoreObject->addNote(transNote, true);
}


void TnootkaQML::noteFinished(const Tnote& n) {
  m_instrument->setNote(n);
  Tnote transNote = n;
  if (transNote.isValid())
    transNote.transpose(-GLOB->transposition());
  if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
    transNote = transNote.showWithFlat();
  m_ignoreScore = true;
  if (m_scoreObject->singleNote())
    transNote.setRhythm(Trhythm::NoRhythm);
  if (m_scoreObject->selectedItem() && (!m_scoreObject->singleNote() || (m_scoreObject->singleNote() && !transNote.isRest())))
    m_scoreObject->setNote(m_scoreObject->selectedItem(), transNote);
//   else // naughty user pressed arrow key
  // TODO remember to treat tied notes as a single one when setNote will be implemented
}


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)
  }
}


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
    if (m_scoreObject && !m_nodeConnected && !GLOB->isExam())
void TnootkaQML::connectInstrument() {
  m_nodeConnected = true;
  connect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
}


void TnootkaQML::instrumentChangesNoteSlot() {
  Tnote rawNote = m_instrument->note();
  qDebug() << "instrument send note" << rawNote.toText();
  m_ignoreScore = true;
  emit playNote(m_instrument->note()); // not yet transposed - to sound properly
  rawNote.transpose(-GLOB->transposition());
  if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
    rawNote = rawNote.showWithFlat();

  if (m_scoreObject->singleNote()) {
      m_scoreObject->setNote(0, rawNote);
      m_scoreObject->setTechnical(0, m_instrument->technical());
  } else {
      if (m_scoreObject->selectedItem()) {
          rawNote.setRhythm(m_scoreObject->selectedItem()->note()->rtm);
          m_scoreObject->setNote(m_scoreObject->selectedItem(), rawNote);
      } else {
          rawNote.setRhythm(m_scoreObject->workRhythm());
      if (GLOB->instrument().type() == Tinstrument::Bandoneon) {
        auto seg = m_scoreObject->selectedItem() ? m_scoreObject->noteSegment(m_scoreObject->selectedItem()->index()) : m_scoreObject->lastSegment();
        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
  }
}


void TnootkaQML::examStartStop() {
  if (GLOB->isExam()) {
      disconnect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
      disconnect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
  } else {
      m_nodeConnected = false;
      connectInstrument();
      connect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
void TnootkaQML::scoreChangedNoteSlot() {
  if (m_ignoreScore) {
    m_ignoreScore = false;
    return;
  }
  auto n = m_scoreObject->selectedNote();
  if (n.isValid())
    n.transpose(GLOB->transposition());
  m_instrument->setNote(n, getTechicalFromScore());
  emit playNote(n);
  qDebug() << "Got note from score" << n.toText() << n.chromatic();
}


int TnootkaQML::selectedNoteId() const {
  return m_scoreObject->selectedItem() ? m_scoreObject->selectedItem()->index() : -1;
}


int TnootkaQML::getTechicalFromScore() {
  quint32 technical = 255; // empty by default
  if (GLOB->instrument().type() == Tinstrument::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;
        }
      }
    }
}


void TnootkaQML::selectPlayingNote(int id) {
  m_ignoreScore = true;
  m_scoreObject->setSelectedItem(m_scoreObject->note(id));
  auto n = m_scoreObject->selectedNote();
  if (n.isValid())
    n.transpose(GLOB->transposition());
  m_instrument->setNote(n, getTechicalFromScore());
}


int TnootkaQML::scoreNotesCount() const {
  return m_scoreObject->notesCount();
}


QList<Tnote>& TnootkaQML::scoreNoteList() const {
  return m_scoreObject->noteList();
}