From 3abf6774039af45e9ab911592a3696a7c4d5ade2 Mon Sep 17 00:00:00 2001
From: SeeLook <945374+SeeLook@users.noreply.github.com>
Date: Tue, 31 Jan 2017 23:26:37 +0100
Subject: [PATCH] Implemented note segment pure in c++, by creating QML items
 from TnoteObject class. Staff and Score have corresponding c++ objects to
 manage score with c++

---
 src/libs/core/CMakeLists.txt         |  20 +--
 src/libs/core/score/tnoteobject.cpp  | 197 +++++++++++++++++++++++++++
 src/libs/core/score/tnoteobject.h    |  84 ++++++++++++
 src/libs/core/score/tscoreobject.cpp |  64 +++++++++
 src/libs/core/score/tscoreobject.h   |  83 +++++++++++
 src/libs/core/score/tstaffobject.cpp |  66 +++++++++
 src/libs/core/score/tstaffobject.h   |  73 ++++++++++
 src/libs/core/tnootkaqml.cpp         |  13 +-
 src/libs/core/tnootkaqml.h           |   2 +
 src/nootka.qrc                       |   1 -
 src/qml/MainWindow.qml               |   5 +
 src/qml/score/NoteSegment.qml        |  70 +++++++---
 src/qml/score/Score.qml              |   5 +
 src/qml/score/Staff.qml              |  21 +--
 14 files changed, 668 insertions(+), 36 deletions(-)
 create mode 100644 src/libs/core/score/tnoteobject.cpp
 create mode 100644 src/libs/core/score/tnoteobject.h
 create mode 100644 src/libs/core/score/tscoreobject.cpp
 create mode 100644 src/libs/core/score/tscoreobject.h
 create mode 100644 src/libs/core/score/tstaffobject.cpp
 create mode 100644 src/libs/core/score/tstaffobject.h

diff --git a/src/libs/core/CMakeLists.txt b/src/libs/core/CMakeLists.txt
index e1159f7d2..52335caf8 100644
--- a/src/libs/core/CMakeLists.txt
+++ b/src/libs/core/CMakeLists.txt
@@ -31,14 +31,18 @@ set(LIB_NOOTKACORE_SRC
   music/tmelody.cpp
   music/tmeter.cpp
 
-  exam/tqatype.cpp
-  exam/tqaunit.cpp
-  exam/tqagroup.cpp
-  exam/tattempt.cpp
-  exam/tlevel.cpp
-  exam/texam.cpp
-  exam/textrans.h
-  exam/tresulttext.cpp
+  score/tscoreobject.cpp
+  score/tstaffobject.cpp
+  score/tnoteobject.cpp
+
+#   exam/tqatype.cpp
+#   exam/tqaunit.cpp
+#   exam/tqagroup.cpp
+#   exam/tattempt.cpp
+#   exam/tlevel.cpp
+#   exam/texam.cpp
+#   exam/textrans.h
+#   exam/tresulttext.cpp
 
 
   touch/ttouchproxy.cpp
diff --git a/src/libs/core/score/tnoteobject.cpp b/src/libs/core/score/tnoteobject.cpp
new file mode 100644
index 000000000..7b10dbfcd
--- /dev/null
+++ b/src/libs/core/score/tnoteobject.cpp
@@ -0,0 +1,197 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 "tnoteobject.h"
+#include "tstaffobject.h"
+#include "tscoreobject.h"
+#include "music/tnote.h"
+#include <QtQml/qqmlengine.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpalette.h>
+
+#include <QtCore/qdebug.h>
+
+
+/** Unicode numbers of accidentals in Scorek font. */
+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
+};
+
+/**
+ * Width of every accidental for Scorek font size set to 7.0
+ * It was measured by QML and corresponds to QFont size @p QFont::setPointSizeF(5.5) (except of neutral)
+ */
+static const qreal accWidthTable[6] = { 2.78125, 1.671875, 0.0, 1.765625, 2.03125, 2.34375 };
+
+
+TnoteObject::TnoteObject(TstaffObject* staffObj, QQuickItem* parent) :
+  QQuickItem(parent),
+  m_staff(staffObj),
+  m_index(-1),
+  m_stemHeight(6.0)
+{
+  m_note = new Tnote();
+  QQmlEngine engine;
+  QQmlComponent comp(&engine, this);
+
+  comp.setData("import QtQuick 2.7; Rectangle {}", QUrl());
+  m_stem = qobject_cast<QQuickItem*>(comp.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_upperLines << createAddLine(comp);
+    m_upperLines.last()->setY(2 * (i + 1) - 0.1);
+    m_lowerLines << createAddLine(comp);
+    m_lowerLines.last()->setY(staff()->upperLine() + 10.0 + 2 * i - 0.1);
+  }
+
+  comp.setData("import QtQuick 2.7; Text { font { family: \"Scorek\"; pixelSize: 7 }}", QUrl());
+  m_head = qobject_cast<QQuickItem*>(comp.create());
+  m_head->setParentItem(this);
+
+  m_alter = qobject_cast<QQuickItem*>(comp.create());
+  m_alter->setParentItem(m_head);
+
+  m_flag = qobject_cast<QQuickItem*>(comp.create());
+  m_flag->setParentItem(m_stem);
+  m_flag->setX(0.1);
+
+
+  setColor(qApp->palette().text().color());
+}
+
+
+TnoteObject::~TnoteObject() {
+  delete m_note;
+}
+
+
+void TnoteObject::setColor(const QColor& c) {
+  m_head->setProperty("color", c);
+  m_alter->setProperty("color", c);
+  m_flag->setProperty("color", c);
+  m_stem->setProperty("color", c);
+}
+
+
+void TnoteObject::setNote(const Tnote& n) {
+  *m_note = n;
+
+  m_note->rtm.setRhythm(static_cast<Trhythm::Erhythm>(4 + m_index % 2)); // TODO fake
+
+  m_head->setProperty("text", getHeadText());
+  setNotePosY(staff()->score()->clefOffset().total() + staff()->upperLine() - (n.octave * 7 + (n.note - 1)));
+  if (m_notePosY < 1.0 || m_notePosY > 38.0)
+    m_notePosY = 0.0;
+  if (static_cast<int>(m_notePosY) != static_cast<int>(m_head->y())) {
+    if (m_notePosY) {
+        m_head->setVisible(true);
+        m_head->setY(m_notePosY - 15.0);
+        for (int i = 0; i < 7; ++i) {
+          m_upperLines[i]->setVisible(m_notePosY > 0.0 && i >= qFloor((m_notePosY - 1.0) / 2.0));
+          m_lowerLines[i]->setVisible(staff()->upperLine() + 10.0 + i * 2 <= m_notePosY);
+        }
+    } else {
+        m_head->setVisible(false);
+    }
+  }
+
+  if (m_notePosY && !m_note->isRest() && m_note->rhythm() > Trhythm::e_whole ) {
+      if (m_note->rtm.beam() == Trhythm::e_noBeam) {
+        m_note->rtm.setStemDown(m_notePosY < staff()->upperLine() + 4.0);
+        m_stem->setHeight(qMax(6.0, qAbs(m_notePosY - (staff()->upperLine() + 4.0))));
+      }
+      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);
+
+  m_alter->setProperty("text", getAccidText());
+  if (m_note->alter)
+    m_alter->setX(-m_alter->width() - 0.1);
+
+  if (m_stem->isVisible()) {
+    QString flag = getFlagText();
+    m_flag->setProperty("text", flag);
+    if (!flag.isEmpty()) {
+      m_flag->setY((m_note->rtm.stemDown() ? m_stem->height() : 0.0) - 15.0);
+    }
+  }
+
+  qDebug() << "[TnoteObject] set note" << m_note->toText() << m_note->rtm.string();
+}
+
+
+/** Overrides standard @p setX() method to shift note segment about accidental symbol width (if it is set) */
+void TnoteObject::setX(qreal xx) {
+  QQuickItem::setX(xx + (m_note->alter ? m_alter->width() : 0.0));
+}
+
+
+//#################################################################################################
+//###################              PROTECTED           ############################################
+//#################################################################################################
+
+QString TnoteObject::getAccidText() {
+  return accCharTable[m_note->alter + 2];
+}
+
+
+/** Used glyphs are note heads: @p 0xf4be @p 0xf4bd (half and black over-sized) and @p 0xf486 (whole smaller) */
+QString TnoteObject::getHeadText() {
+  if (m_note->rhythm() == Trhythm::e_none)
+    return QStringLiteral("\uf4be"); // just black note-head
+  if (m_note->isRest()) {
+    
+  } else {
+      if (m_note->rhythm() == Trhythm::e_whole)
+          return QStringLiteral("\uf468");
+      else if (m_note->rhythm() == Trhythm::e_half)
+          return QStringLiteral("\uf4bd");
+      else
+          return QStringLiteral("\uf4be"); // black note head
+  }
+}
+
+
+QString TnoteObject::getFlagText() {
+  if (m_note->rhythm() < Trhythm::e_eighth || m_note->isRest() || m_note->rtm.beam() != Trhythm::e_noBeam)
+    return QString();
+  return QString(QChar(0xe240 + (static_cast<int>(m_note->rhythm()) - 4) * 2 + (m_note->rtm.stemDown() ? 1 : 0)));
+}
+
+
+QQuickItem* TnoteObject::createAddLine(QQmlComponent& comp) {
+  auto line = qobject_cast<QQuickItem*>(comp.create());
+  line->setParentItem(this);
+  line->setWidth(3.5);
+  line->setHeight(0.2);
+  line->setX(-0.5);
+  line->setVisible(false);
+  line->setProperty("color", qApp->palette().text().color());
+  return line;
+}
diff --git a/src/libs/core/score/tnoteobject.h b/src/libs/core/score/tnoteobject.h
new file mode 100644
index 000000000..48e096714
--- /dev/null
+++ b/src/libs/core/score/tnoteobject.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 TNOTEOBJECT_H
+#define TNOTEOBJECT_H
+
+
+#include "nootkacoreglobal.h"
+#include <QtQuick/qquickitem.h>
+
+
+class TstaffObject;
+class Tnote;
+
+
+class NOOTKACORE_EXPORT TnoteObject : public QQuickItem
+{
+
+  Q_OBJECT
+
+  Q_PROPERTY(qreal notePosY READ notePosY WRITE setNotePosY NOTIFY notePosYchanged)
+
+public:
+  explicit TnoteObject(TstaffObject* staffObj = nullptr, QQuickItem* parent = nullptr);
+  ~TnoteObject();
+
+  TstaffObject* staff() const { return m_staff; }
+
+  Tnote* note() { return m_note; }
+  void setNote(const Tnote& n);
+
+  qreal notePosY() const { return m_notePosY; }
+  qreal setNotePosY(qreal y) { m_notePosY = y; emit notePosYchanged(); }
+
+  int index() const { return m_index; }
+  void setIndex(int id) { m_index = id; }
+
+  qreal stemHeight() const { return m_stemHeight; }
+  void setStemHEight(qreal sh);
+
+  QColor color() { return m_head->property("color").value<QColor>(); }
+  void setColor(const QColor& c);
+
+  void setX(qreal xx);
+
+signals:
+  void notePosYchanged();
+
+protected:
+  QString getAccidText();
+  QString getHeadText();
+  QString getFlagText();
+
+private:
+
+  TstaffObject                *m_staff;
+  Tnote                       *m_note;
+  int                          m_index;
+  qreal                        m_notePosY;
+  qreal                        m_x;
+  QQuickItem                  *m_head, *m_alter, *m_stem, *m_flag;
+  QList<QQuickItem*>           m_upperLines, m_lowerLines;
+  qreal                        m_stemHeight;
+
+private:
+  QQuickItem* createAddLine(QQmlComponent& comp);
+};
+
+#endif // TNOTEOBJECT_H
diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp
new file mode 100644
index 000000000..1b6f6ff0a
--- /dev/null
+++ b/src/libs/core/score/tscoreobject.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 "tscoreobject.h"
+#include "tstaffobject.h"
+#include "music/tmeter.h"
+#include "music/tnote.h"
+#include <QtCore/qdebug.h>
+
+
+
+TscoreObject::TscoreObject(QObject* parent) :
+  QObject(parent),
+  m_keySignEnabled(false),
+  m_clefOffset(TclefOffset(3, 1))
+{
+  m_meter = new Tmeter(Tmeter::Meter_4_4);
+}
+
+
+TscoreObject::~TscoreObject()
+{
+  delete m_meter;
+  qDebug() << "[TscoreObject] deleted";
+}
+
+
+void TscoreObject::setParent(QObject* p) {
+  QObject::setParent(p);
+  qDebug() << "[TscoreObject] parent changed to" << p;
+}
+
+
+void TscoreObject::addNote(const Tnote& n) {
+  m_staves.last()->addNote(n);
+}
+
+
+void TscoreObject::setKeySignatureEnabled(bool enKey) {
+  m_keySignEnabled = enKey;
+}
+
+
+//#################################################################################################
+//###################              PROTECTED           ############################################
+//#################################################################################################
+void TscoreObject::addStaff(TstaffObject* st) {
+  m_staves.append(st);
+}
diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h
new file mode 100644
index 000000000..103d4662e
--- /dev/null
+++ b/src/libs/core/score/tscoreobject.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 TSCOREOBJECT_H
+#define TSCOREOBJECT_H
+
+
+#include "nootkacoreglobal.h"
+#include <QtCore/qobject.h>
+
+
+/**
+ * Describes offset of a note.
+ */
+class TclefOffset
+{
+public:
+  TclefOffset(qint8 noteOff = 0, qint8 octaveOff = 0) : note(noteOff), octave(octaveOff) {}
+
+  qint8 note;
+  qint8 octave;
+  int total() { return octave * 7 + note; }
+};
+
+
+
+class Tnote;
+class TstaffObject;
+class TnoteObject;
+class Tmeter;
+
+
+
+class NOOTKACORE_EXPORT  TscoreObject : public QObject
+{
+
+  Q_OBJECT
+
+  Q_PROPERTY(QObject* parent READ parent WRITE setParent)
+
+  friend class TstaffObject;
+  friend class TnoteObject;
+
+public:
+  explicit TscoreObject(QObject* parent = nullptr);
+  ~TscoreObject();
+
+  void setParent(QObject* p);
+
+  Q_INVOKABLE void addNote(const Tnote& n);
+
+  bool keySignatureEnabled() const { return m_keySignEnabled; }
+  void setKeySignatureEnabled(bool enKey);
+
+protected:
+  void addStaff(TstaffObject* st);
+
+  TclefOffset clefOffset() const { return m_clefOffset; }
+
+private:
+  Tmeter                           *m_meter;
+  bool                              m_keySignEnabled;
+  TclefOffset                       m_clefOffset;
+  QList<TstaffObject*>              m_staves;
+
+};
+
+#endif // TSCOREOBJECT_H
diff --git a/src/libs/core/score/tstaffobject.cpp b/src/libs/core/score/tstaffobject.cpp
new file mode 100644
index 000000000..97b145e9e
--- /dev/null
+++ b/src/libs/core/score/tstaffobject.cpp
@@ -0,0 +1,66 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 "tstaffobject.h"
+#include <QtCore/qdebug.h>
+#include "tscoreobject.h"
+#include "tnoteobject.h"
+#include "music/tnote.h"
+
+
+TstaffObject::TstaffObject(QObject* parent) :
+  QObject(parent),
+  m_score(nullptr),
+  m_upperLine(16.0),
+  m_staffItem(nullptr)
+{
+}
+
+
+TstaffObject::~TstaffObject() { qDebug() << "[TstaffObject] is going delete"; }
+
+
+void TstaffObject::setScore(TscoreObject* s) {
+  m_score = s;
+  setParent(s);
+  qDebug() << "TstaffObject got a score parent" << s;
+  m_score->addStaff(this);
+}
+
+
+void TstaffObject::addNote(const Tnote& n) {
+  auto noteObj = new TnoteObject(this, m_staffItem);
+  noteObj->setIndex(m_notes.count());
+  noteObj->setNote(n);
+  noteObj->setX(notesIndent() + noteObj->index() * 7.0);
+  m_notes.append(noteObj);
+  emit noteAdded(noteObj);
+}
+
+
+void TstaffObject::setUpperLine(qreal upLine) {
+  m_upperLine = upLine;
+  emit upperLineChanged();
+}
+
+
+void TstaffObject::setStaffItem(QQuickItem* si) {
+  m_staffItem = si;
+}
+
+
diff --git a/src/libs/core/score/tstaffobject.h b/src/libs/core/score/tstaffobject.h
new file mode 100644
index 000000000..707fdf25f
--- /dev/null
+++ b/src/libs/core/score/tstaffobject.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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 TSTAFFOBJECT_H
+#define TSTAFFOBJECT_H
+
+
+#include "nootkacoreglobal.h"
+#include <QtCore/qobject.h>
+
+
+class QQuickItem;
+class TscoreObject;
+class TnoteObject;
+class Tnote;
+
+
+class NOOTKACORE_EXPORT  TstaffObject : public QObject
+{
+
+  Q_OBJECT
+
+  Q_PROPERTY(TscoreObject* score READ score WRITE setScore)
+  Q_PROPERTY(qreal upperLine READ upperLine WRITE setUpperLine NOTIFY upperLineChanged)
+  Q_PROPERTY(QQuickItem* staffItem READ staffItem WRITE setStaffItem)
+  Q_PROPERTY(qreal notesIndent READ notesIndent WRITE setNotesIndent)
+
+public:
+  explicit TstaffObject(QObject* parent = nullptr);
+  ~TstaffObject();
+
+  TscoreObject* score() { return m_score; }
+  void setScore(TscoreObject* s);
+
+  void addNote(const Tnote& n);
+
+  qreal upperLine() { return m_upperLine; }
+  void setUpperLine(qreal upLine);
+
+  QQuickItem* staffItem() { return m_staffItem; }
+  void setStaffItem(QQuickItem* si);
+
+  qreal notesIndent() { return m_notesIndent; }
+  void setNotesIndent(qreal ni) { m_notesIndent = ni; }
+
+signals:
+  void noteAdded(TnoteObject* note);
+  void upperLineChanged();
+
+private:
+  TscoreObject                  *m_score;
+  qreal                          m_upperLine;
+  QList<TnoteObject*>            m_notes;
+  QQuickItem                    *m_staffItem;
+  qreal                          m_notesIndent;
+};
+
+#endif // TSTAFFOBJECT_H
diff --git a/src/libs/core/tnootkaqml.cpp b/src/libs/core/tnootkaqml.cpp
index 75d112ac8..9e1838f31 100644
--- a/src/libs/core/tnootkaqml.cpp
+++ b/src/libs/core/tnootkaqml.cpp
@@ -19,10 +19,12 @@
 #include "tnootkaqml.h"
 #include "ttickcolors.h"
 #include "nootkaconfig.h"
-#include "music/tmeter.h"
 #include "tpath.h"
+#include "music/tmeter.h"
 #include "music/tclef.h"
 #include "music/tkeysignature.h"
+#include "score/tscoreobject.h"
+#include "score/tstaffobject.h"
 #include <QtQml/qqmlengine.h>
 #include <QtCore/qfile.h>
 #include <QtCore/qdir.h>
@@ -45,6 +47,10 @@ TnootkaQML::TnootkaQML(QObject* parent) :
   qmlRegisterUncreatableType<Tclef>("Score", 1, 0, "Tclef", "You cannot create an instance of the Tclef.");
   qRegisterMetaType<Tmeter>();
   qmlRegisterUncreatableType<Tmeter>("Score", 1, 0, "Tmeter", "You cannot create an instance of the Tmeter.");
+
+  qmlRegisterType<TscoreObject>("Score", 1, 0, "TscoreObject");
+  qmlRegisterType<TstaffObject>("Score", 1, 0, "TstaffObject");
+
   qmlRegisterType<TtickColors>("TtickColors", 1, 0, "TtickColors");
 }
 
@@ -71,6 +77,11 @@ Tmeter TnootkaQML::meter(int m) {
 }
 
 
+Tnote TnootkaQML::note(int pitch, int octave, int alter) {
+  return Tnote(static_cast<char>(pitch), static_cast<char>(octave), static_cast<char>(alter), Trhythm(Trhythm::e_quarter));
+}
+
+
 QString TnootkaQML::majorKeyName(int key) {
   return TkeySignature(static_cast<char>(key)).getMajorName();
 }
diff --git a/src/libs/core/tnootkaqml.h b/src/libs/core/tnootkaqml.h
index 075d7cb0d..f34db4d9d 100644
--- a/src/libs/core/tnootkaqml.h
+++ b/src/libs/core/tnootkaqml.h
@@ -26,6 +26,7 @@
 
 class Tclef;
 class Tmeter;
+class Tnote;
 
 
 /**
@@ -45,6 +46,7 @@ public:
   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);
   Q_INVOKABLE QString majorKeyName(int key);
   Q_INVOKABLE QString minorKeyName(int key);
   Q_INVOKABLE QString getLicense();
diff --git a/src/nootka.qrc b/src/nootka.qrc
index afc96c16e..03d52a7f8 100644
--- a/src/nootka.qrc
+++ b/src/nootka.qrc
@@ -16,7 +16,6 @@
     <file alias="Clef.qml">qml/score/Clef.qml</file>
     <file alias="KeySignature.qml">qml/score/KeySignature.qml</file>
     <file alias="Meter.qml">qml/score/Meter.qml</file>
-    <file alias="NoteSegment.qml">qml/score/NoteSegment.qml</file>
 
     <file alias="TaboutNootka.qml">qml/about/TaboutNootka.qml</file>
     <file alias="AboutPage.qml">qml/about/AboutPage.qml</file>
diff --git a/src/qml/MainWindow.qml b/src/qml/MainWindow.qml
index 95e03fdf8..433297acf 100644
--- a/src/qml/MainWindow.qml
+++ b/src/qml/MainWindow.qml
@@ -76,8 +76,13 @@ ApplicationWindow {
         id: score
         Layout.fillWidth: true
         Layout.fillHeight: true
+        Component.onCompleted: {
+          for (var n = 1; n < 8; ++n)
+            addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2)))
+        }
       }
 
+      // space for an instrument
       Rectangle { height: nootkaWindow.height / 4; Layout.fillWidth: true; color: activPal.window; border { width: 1; color: activPal.text } }
   }
 
diff --git a/src/qml/score/NoteSegment.qml b/src/qml/score/NoteSegment.qml
index 9ecce8285..4e2bab6bd 100644
--- a/src/qml/score/NoteSegment.qml
+++ b/src/qml/score/NoteSegment.qml
@@ -19,6 +19,8 @@
 import QtQuick 2.7
 import QtQuick.Controls 2.0
 
+import Score 1.0
+
 
 Item {
   id: noteSegment
@@ -26,40 +28,76 @@ Item {
   height: parent.height
   width: 7
 
-  property real notePos: 0
+  property TnoteObject noteObj: null
   property color color: activPal.text
 
+  onNoteObjChanged: noteObj.changed.connect(update)
+
+  Text {
+      id: head
+      font { family: "Scorek"; pixelSize: 7 }
+      y: noteObj.notePosY - 15
+      color: noteSegment.color
+      visible: noteObj.notePosY > 0
+  }
+
   Text {
-      id: noteHead
+      id: alter
       font { family: "Scorek"; pixelSize: 7 }
-      y: notePos - staff.upperLine
-      text: "\ue1d5"
+      y: noteObj.notePosY - 15
       color: noteSegment.color
-      visible: notePos > 0
+      visible: noteObj.notePosY > 0
+      x: head.x - width - 0.1
+  }
+
+  Rectangle {
+      id: stem
+      width: 0.3
+      height: 6
+      color: activPal.text
+      visible: noteObj.stemVisible()
+  }
+
+  Text {
+      id: flag
+      font { family: "Scorek"; pixelSize: 7 }
+      color: activPal.text
+      x: stem.x + 0.1
+      y: stem.y - 15
   }
 
   Repeater { // upper lines
-      id: upRep
       model: 7
       Rectangle {
-        x: noteHead.x - 0.5
+        x: head.x - 0.5
         y: 2 * (index + 1) - 0.1
-        height: 0.18
+        height: 0.2
         width: 3.5
         color: noteSegment.color
-        visible: notePos > 0 && (index) >= Math.floor(notePos / 2) - 1
+        visible: noteObj.notePosY > 0 && index >= Math.floor((noteObj.notePosY - 1) / 2)
       }
   }
-  Repeater { // upper lines
-      id: lowRep
-      model: (staff.height - staff.upperLine - 12) / 2
+  Repeater { // lower lines
+      model: (staff.height - noteObj.upperLinePos() - 12) / 2
       Rectangle {
-        x: noteHead.x - 0.5
-        y: staff.upperLine + 9.9 + 2 * index
-        height: 0.18
+        x: head.x - 0.5
+        y: noteObj.upperLinePos() + 10.0 + 2 * index - 0.1
+        height: 0.2
         width: 3.5
         color: noteSegment.color
-        visible: staff.upperLine + 10 + index * 2 <= notePos - 1
+        visible: noteObj.upperLinePos() + 10 + index * 2 <= noteObj.notePosY
       }
   }
+
+  function update() {
+    alter.text = noteObj.alter()
+    head.text = noteObj.head()
+    stem.visible = noteObj.stemVisible()
+    if (noteObj.stemVisible()) {
+      stem.x = noteObj.stemDown() ? head.x : head.x + 2.0
+      stem.y = noteObj.notePosY - (noteObj.stemDown() ? 0 : stem.height)
+      flag.y = noteObj.stemDown() ? stem.y - stem.height - 3 : stem.y - 15
+    }
+    flag.text = noteObj.flag()
+  }
 }
diff --git a/src/qml/score/Score.qml b/src/qml/score/Score.qml
index dab880b13..5d84aba93 100644
--- a/src/qml/score/Score.qml
+++ b/src/qml/score/Score.qml
@@ -25,6 +25,8 @@ import Score 1.0
 Flickable {
   id: score
 
+  TscoreObject { id: scoreObj; /*parent: score*/ }
+
   property int clef: Tclef.Treble_G_8down
   property int meter: Tmeter.Meter_4_4
   property alias bgColor: bgRect.color
@@ -71,4 +73,7 @@ Flickable {
     staff0.enableKeySignature(enableKeySign)
   }
 
+  function addNote(n) {
+    scoreObj.addNote(n)
+  }
 }
diff --git a/src/qml/score/Staff.qml b/src/qml/score/Staff.qml
index e1de8bdb3..dc2858006 100644
--- a/src/qml/score/Staff.qml
+++ b/src/qml/score/Staff.qml
@@ -29,7 +29,6 @@ Item {
 
   property real linesCount: 40
   property int number: -1
-  property real upperLine: 16.0
   property KeySignature keySignature: null
   property Meter meter: null
   property real firstNoteX: clef.width + (keySignature ? keySignature.width : 0) + (meter ? meter.width : 0) + 1
@@ -39,12 +38,14 @@ Item {
   scale: score.height / linesCount
   transformOrigin: Item.TopLeft
 
+  TstaffObject { id: staffObj; score: scoreObj; staffItem: staff; notesIndent: firstNoteX }
+
   Repeater { // staff lines
       model: 5
       Rectangle {
         x: 0.5
-        y: upperLine + 2 * index - 0.1
-        height: 0.18
+        y: staffObj.upperLine + 2 * index - 0.1
+        height: 0.2
         width: staff.width - 1.0
         color: activPal.text
       }
@@ -86,13 +87,13 @@ Item {
       }
   }
 
-  Repeater {
-      model: 8
-      NoteSegment {
-        notePos: upperLine + 11 - index
-        x: firstNoteX + index * width
-      }
-  }
+//   Repeater {
+//       model: 8
+//       NoteSegment {
+//         notePos: upperLine + 11// - index
+//         x: firstNoteX //+ index * width
+//       }
+//   }
 
   function updateMeterPos() {
     meter.x = keySignature.x + keySignature.width
-- 
GitLab