Skip to content
Snippets Groups Projects
tmelodylistview.cpp 7.73 KiB
Newer Older
/***************************************************************************
 *   Copyright (C) 2018-2021 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 "tmelodylistview.h"
#include "tmelodywrapper.h"
#include <exam/tlevel.h>
#include <music/timportscore.h>
#include <music/tmelody.h>
#include <score/tscoreobject.h>
#include <tglobals.h>
#include <tnootkaqml.h>

#include <QtCore/qfileinfo.h>
#include <QtCore/qtimer.h>
#include <QtQml/qqmlapplicationengine.h>
#if !defined(Q_OS_ANDROID)
#include <QtGui/qfont.h>
#include <QtGui/qguiapplication.h>
#include <QtWidgets/qfiledialog.h>
#include <qtr.h>
#include <tmtr.h>
TmelodyListView::TmelodyListView(QQuickItem *parent)
    : QQuickItem(parent)
{
}
TmelodyListView::~TmelodyListView()
{
void TmelodyListView::setMelodyModel(QObject *mm)
{
    if (mm != m_melodyModel) {
        m_melodyModel = mm;
    }
}
/**
 * Load melodies with delay to give time for score creation,
 * otherwise score complains with warnings.
 * But for score itself 50ms is sufficient,
 * 250ms delay is for displaying 'entering melody page' animation,
 * then melodies show off.
void TmelodyListView::setLevel(Tlevel *l)
{
    QMetaObject::invokeMethod(m_melodyModel, "clear");
    m_lastMelodyId = -1; // reset
    m_level = l;
    QTimer::singleShot(250, this, [=] {
        loadMelodies();
    });
void TmelodyListView::loadMelody()
{
#if defined(Q_OS_ANDROID)
    QString musicXMLfile = NOO->getXmlToOpen();
    if (musicXMLfile.isEmpty())
        return;
    else
        m_xmlFiles << musicXMLfile;
    auto f = qApp->font();
    qApp->setFont(Tmtr::systemFont);
    m_xmlFiles = QFileDialog::getOpenFileNames(nullptr,
                                               qTR("TmainScoreObject", "Open melody file"),
                                               GLOB->lastXmlDir(),
                                               qTR("TmainScoreObject", "MusicXML file") + QLatin1String(": *.xml, *.musicxml, *.mxl (*.xml *.musicxml *.mxl);;")
                                                   + QLatin1String(" *.xml (*.xml);;") + QLatin1String(" *.musicxml (*.musicxml);;")
                                                   + qTR("TmainScoreObject", "Compressed MusicXML file") + QLatin1String(" *.mxl  (*.mxl);;"));
    qApp->setFont(f);
    if (m_xmlFiles.isEmpty())
        return;
    GLOB->setLastXmlDir(QFileInfo(m_xmlFiles.first()).absoluteDir().path());
    m_lastMelodyId = m_level->melodySet.count() - 1;
    processNextXmlFile();
void TmelodyListView::removeMelody(int id)
{
    if (id >= 0 && id < m_level->melodySet.count()) {
        m_level->melodySet.removeAt(id);
        emit melodiesChanged();
    }
void TmelodyListView::swapMelodies(int from, int to)
{
    if (from > -1 && from < m_level->melodySet.count() && to > -1 && to < m_level->melodySet.count()) {
        m_level->melodySet.move(from, to);
        emit melodiesChanged();
    } else
        qDebug() << "[TmelodyListView] FIXME! Wrong melodies to swap";
QVariant TmelodyListView::getMelody(int melId)
{
    return QVariant::fromValue(melId > -1 && melId < m_level->melodySet.count() ? &m_level->melodySet[melId] : nullptr);
void TmelodyListView::divideMelody(int melId, int bars)
{
    if (melId > -1 && melId < m_level->melodySet.count()) {
        QList<Tmelody *> parts;
        m_level->melodySet[melId].split(bars, parts);
        if (!parts.isEmpty()) {
            int i = 1;
            for (auto m : parts) {
                m_level->melodySet.insert(melId + i, *m);
                emit insertMelody(melId + i);
                i++;
            }
        }
void TmelodyListView::transpose(int semis, bool outScaleToRes, bool inInstrScale, TmelodyWrapper *wrapp)
{
    if (wrapp && wrapp->melody() && wrapp->score()) {
        wrapp->melody()->transpose(semis,
                                   outScaleToRes,
                                   inInstrScale ? GLOB->loNote() : wrapp->score()->lowestNote(),
                                   inInstrScale ? GLOB->hiNote() : wrapp->score()->highestNote());
        wrapp->reload();
    }
// #################################################################################################
// ###################              PROTECTED           ############################################
// #################################################################################################
void TmelodyListView::loadMelodies()
{
    m_lastMelodyId++;
    for (int m = m_lastMelodyId; m < m_level->melodySet.count(); ++m)
        emit appendMelody();
    m_lastMelodyId = m_level->melodySet.count() - 1;
void TmelodyListView::processNextXmlFile()
{
    if (m_xmlFiles.isEmpty())
        return;

    auto musicXMLfile = m_xmlFiles.takeFirst();
    m_level->melodySet << Tmelody();
    auto m = &m_level->melodySet.last();
    auto melImport = new TimportScore(musicXMLfile, m);
    melImport->setMultiSelect(true);
    connect(melImport, &TimportScore::wantDialog, this, [=] {
        auto nootWin = qobject_cast<QQmlApplicationEngine *>(NOO->qmlEngine())->rootObjects().first();
        if (nootWin && QString(nootWin->metaObject()->className()).contains("MainWindow_QMLTYPE")) {
            QMetaObject::invokeMethod(nootWin, "showDialog", Q_ARG(QVariant, TnootkaQML::ScoreImport));
            connect(melImport, &TimportScore::importReady, this, &TmelodyListView::melodyImportSlot);
        }
    });
    connect(melImport, &TimportScore::xmlWasRead, this, [=] {
        if (IMPORT_SCORE->hasMoreParts()) {
            m_level->melodySet.removeLast();
            IMPORT_SCORE->deleteLater();
            if (m_xmlFiles.isEmpty()) {
                QTimer::singleShot(100, this, [=] {
                    loadMelodies();
                });
                emit melodiesChanged();
            } else {
                QTimer::singleShot(50, this, [=] {
                    processNextXmlFile();
                });
            }
    });
    melImport->runXmlThread();
void TmelodyListView::melodyImportSlot()
{
    for (auto mi : IMPORT_SCORE->model()) {
        auto voicePart = qobject_cast<TmelodyPart *>(mi);
        if (voicePart && !voicePart->parts.isEmpty()) {
            for (auto snip : voicePart->parts) {
                if (snip->selected()) // append all selected parts
                    m_level->melodySet << *snip->melody();
            }
        }
    if (m_xmlFiles.isEmpty()) {
        QTimer::singleShot(100, this, [=] {
            loadMelodies();
        });
        emit melodiesChanged();
    } else
        QTimer::singleShot(100, this, [=] {
            processNextXmlFile();
        });