2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
|
|
|
// Copyright (C) 2002-2011 Werner Schweer
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License version 2
|
|
|
|
// as published by the Free Software Foundation and appearing in
|
|
|
|
// the file LICENCE.GPL
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
/**
|
|
|
|
\file
|
|
|
|
Implementation of class Score (partial).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include "score.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "sig.h"
|
|
|
|
#include "clef.h"
|
|
|
|
#include "tempo.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "page.h"
|
|
|
|
#include "undo.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "select.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "xml.h"
|
|
|
|
#include "text.h"
|
|
|
|
#include "note.h"
|
|
|
|
#include "chord.h"
|
|
|
|
#include "rest.h"
|
|
|
|
#include "slur.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "part.h"
|
|
|
|
#include "style.h"
|
|
|
|
#include "tuplet.h"
|
|
|
|
#include "lyrics.h"
|
|
|
|
#include "pitchspelling.h"
|
|
|
|
#include "line.h"
|
|
|
|
#include "volta.h"
|
|
|
|
#include "repeat.h"
|
|
|
|
#include "ottava.h"
|
|
|
|
#include "barline.h"
|
|
|
|
#include "box.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "excerpt.h"
|
|
|
|
#include "stafftext.h"
|
|
|
|
#include "repeatlist.h"
|
|
|
|
#include "keysig.h"
|
|
|
|
#include "beam.h"
|
|
|
|
#include "stafftype.h"
|
|
|
|
#include "tempotext.h"
|
|
|
|
#include "articulation.h"
|
|
|
|
#include "revisions.h"
|
|
|
|
#include "tiemap.h"
|
|
|
|
#include "layoutbreak.h"
|
|
|
|
#include "harmony.h"
|
|
|
|
#include "mscore.h"
|
2012-07-06 17:42:20 +02:00
|
|
|
#ifdef OMR
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "omr/omr.h"
|
2012-07-06 17:42:20 +02:00
|
|
|
#endif
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "bracket.h"
|
|
|
|
#include "audio.h"
|
2012-06-07 09:24:05 +02:00
|
|
|
#include "instrtemplate.h"
|
2012-06-28 10:03:19 +02:00
|
|
|
#include "cursor.h"
|
2013-11-11 16:53:03 +01:00
|
|
|
#include "sym.h"
|
2014-11-15 16:29:48 +01:00
|
|
|
#include "rehearsalmark.h"
|
2015-01-29 22:37:39 +01:00
|
|
|
#include "breath.h"
|
2015-03-05 16:46:37 +01:00
|
|
|
#include "instrchange.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Score* gscore; ///< system score, used for palettes etc.
|
|
|
|
|
|
|
|
bool scriptDebug = false;
|
|
|
|
bool noSeq = false;
|
|
|
|
bool noMidi = false;
|
|
|
|
bool midiInputTrace = false;
|
|
|
|
bool midiOutputTrace = false;
|
|
|
|
bool showRubberBand = true;
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// MeasureBaseList
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
MeasureBaseList::MeasureBaseList()
|
|
|
|
{
|
|
|
|
_first = 0;
|
|
|
|
_last = 0;
|
|
|
|
_size = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// push_back
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::push_back(MeasureBase* e)
|
|
|
|
{
|
|
|
|
++_size;
|
|
|
|
if (_last) {
|
|
|
|
_last->setNext(e);
|
|
|
|
e->setPrev(_last);
|
|
|
|
e->setNext(0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_first = e;
|
|
|
|
e->setPrev(0);
|
|
|
|
e->setNext(0);
|
|
|
|
}
|
|
|
|
_last = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// push_front
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::push_front(MeasureBase* e)
|
|
|
|
{
|
|
|
|
++_size;
|
|
|
|
if (_first) {
|
|
|
|
_first->setPrev(e);
|
|
|
|
e->setNext(_first);
|
|
|
|
e->setPrev(0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_last = e;
|
|
|
|
e->setPrev(0);
|
|
|
|
e->setNext(0);
|
|
|
|
}
|
|
|
|
_first = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// add
|
|
|
|
// insert e before e->next()
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::add(MeasureBase* e)
|
|
|
|
{
|
|
|
|
MeasureBase* el = e->next();
|
|
|
|
if (el == 0) {
|
|
|
|
push_back(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (el == _first) {
|
|
|
|
push_front(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
++_size;
|
|
|
|
e->setPrev(el->prev());
|
|
|
|
el->prev()->setNext(e);
|
|
|
|
el->setPrev(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// erase
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::remove(MeasureBase* el)
|
|
|
|
{
|
|
|
|
--_size;
|
|
|
|
if (el->prev())
|
|
|
|
el->prev()->setNext(el->next());
|
|
|
|
else
|
|
|
|
_first = el->next();
|
|
|
|
if (el->next())
|
|
|
|
el->next()->setPrev(el->prev());
|
|
|
|
else
|
|
|
|
_last = el->prev();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// insert
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::insert(MeasureBase* fm, MeasureBase* lm)
|
|
|
|
{
|
|
|
|
++_size;
|
|
|
|
for (MeasureBase* m = fm; m != lm; m = m->next())
|
|
|
|
++_size;
|
|
|
|
MeasureBase* pm = fm->prev();
|
|
|
|
if (pm)
|
|
|
|
pm->setNext(fm);
|
|
|
|
else
|
|
|
|
_first = fm;
|
|
|
|
MeasureBase* nm = lm->next();
|
|
|
|
if (nm)
|
|
|
|
nm->setPrev(lm);
|
|
|
|
else
|
|
|
|
_last = lm;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// remove
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::remove(MeasureBase* fm, MeasureBase* lm)
|
|
|
|
{
|
|
|
|
--_size;
|
|
|
|
for (MeasureBase* m = fm; m != lm; m = m->next())
|
|
|
|
--_size;
|
|
|
|
MeasureBase* pm = fm->prev();
|
|
|
|
MeasureBase* nm = lm->next();
|
|
|
|
if (pm)
|
|
|
|
pm->setNext(nm);
|
|
|
|
else
|
|
|
|
_first = nm;
|
|
|
|
if (nm)
|
|
|
|
nm->setPrev(pm);
|
|
|
|
else
|
|
|
|
_last = pm;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// change
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void MeasureBaseList::change(MeasureBase* ob, MeasureBase* nb)
|
|
|
|
{
|
|
|
|
nb->setPrev(ob->prev());
|
|
|
|
nb->setNext(ob->next());
|
|
|
|
if (ob->prev())
|
|
|
|
ob->prev()->setNext(nb);
|
|
|
|
if (ob->next())
|
|
|
|
ob->next()->setPrev(nb);
|
|
|
|
if (ob == _last)
|
|
|
|
_last = nb;
|
|
|
|
if (ob == _first)
|
|
|
|
_first = nb;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (nb->type() == Element::Type::HBOX || nb->type() == Element::Type::VBOX
|
|
|
|
|| nb->type() == Element::Type::TBOX || nb->type() == Element::Type::FBOX)
|
2012-05-26 14:26:10 +02:00
|
|
|
nb->setSystem(ob->system());
|
2015-02-17 20:22:24 +01:00
|
|
|
foreach(Element* e, nb->el())
|
2012-05-26 14:26:10 +02:00
|
|
|
e->setParent(nb);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// init
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::init()
|
|
|
|
{
|
|
|
|
_linkId = 0;
|
|
|
|
_currentLayer = 0;
|
2014-05-30 10:16:38 +02:00
|
|
|
_playMode = PlayMode::SYNTHESIZER;
|
2012-05-26 14:26:10 +02:00
|
|
|
Layer l;
|
|
|
|
l.name = "default";
|
|
|
|
l.tags = 1;
|
|
|
|
_layer.append(l);
|
|
|
|
_layerTags[0] = "default";
|
|
|
|
|
2013-07-19 18:03:35 +02:00
|
|
|
if (!_parentScore) {
|
2014-03-16 17:05:36 +01:00
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
_metaTags.insert("platform", "Microsoft Windows");
|
|
|
|
#elif defined(Q_OS_MAC)
|
|
|
|
_metaTags.insert("platform", "Apple Macintosh");
|
|
|
|
#elif defined(Q_OS_LINUX)
|
|
|
|
_metaTags.insert("platform", "Linux");
|
|
|
|
#else
|
|
|
|
_metaTags.insert("platform", "Unknown");
|
2012-05-26 14:26:10 +02:00
|
|
|
#endif
|
2013-07-19 18:03:35 +02:00
|
|
|
_metaTags.insert("movementNumber", "");
|
|
|
|
_metaTags.insert("movementTitle", "");
|
|
|
|
_metaTags.insert("workNumber", "");
|
|
|
|
_metaTags.insert("workTitle", "");
|
2013-09-01 11:01:37 +02:00
|
|
|
_metaTags.insert("arranger", "");
|
|
|
|
_metaTags.insert("composer", "");
|
|
|
|
_metaTags.insert("lyricist", "");
|
|
|
|
_metaTags.insert("poet", "");
|
|
|
|
_metaTags.insert("translator", "");
|
2013-07-19 18:03:35 +02:00
|
|
|
_metaTags.insert("source", "");
|
|
|
|
_metaTags.insert("copyright", "");
|
|
|
|
_metaTags.insert("creationDate", QDate::currentDate().toString(Qt::ISODate));
|
|
|
|
_undo = new UndoStack();
|
|
|
|
_repeatList = new RepeatList(this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_undo = 0;
|
|
|
|
_repeatList = 0;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-11-06 15:58:05 +01:00
|
|
|
_revisions = new Revisions;
|
|
|
|
_scoreFont = ScoreFont::fontFactory("emmentaler");
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
_pageNumberOffset = 0;
|
|
|
|
|
2013-10-18 12:21:01 +02:00
|
|
|
_mscVersion = MSCVERSION;
|
|
|
|
_created = false;
|
|
|
|
|
|
|
|
_updateAll = true;
|
|
|
|
_layoutAll = true;
|
|
|
|
layoutFlags = 0;
|
|
|
|
_undoRedo = false;
|
|
|
|
_playNote = false;
|
2015-02-08 21:06:26 +01:00
|
|
|
_playChord = false;
|
2013-10-18 12:21:01 +02:00
|
|
|
_excerptsChanged = false;
|
|
|
|
_instrumentsChanged = false;
|
|
|
|
_selectionChanged = false;
|
|
|
|
|
|
|
|
keyState = 0;
|
|
|
|
_showInvisible = true;
|
|
|
|
_showUnprintable = true;
|
|
|
|
_showFrames = true;
|
|
|
|
_showPageborders = false;
|
|
|
|
_showInstrumentNames = true;
|
|
|
|
_showVBox = true;
|
|
|
|
|
|
|
|
_printing = false;
|
2015-01-30 17:03:51 +01:00
|
|
|
_playlistDirty = true;
|
|
|
|
_autosaveDirty = true;
|
2013-10-18 12:21:01 +02:00
|
|
|
_saved = false;
|
|
|
|
_pos[int(POS::CURRENT)] = 0;
|
|
|
|
_pos[int(POS::LEFT)] = 0;
|
|
|
|
_pos[int(POS::RIGHT)] = 0;
|
|
|
|
_fileDivision = MScore::division;
|
|
|
|
_defaultsRead = false;
|
|
|
|
_omr = 0;
|
|
|
|
_audio = 0;
|
|
|
|
_showOmr = false;
|
|
|
|
_sigmap = 0;
|
|
|
|
_tempomap = 0;
|
2014-05-30 10:15:36 +02:00
|
|
|
_layoutMode = LayoutMode::PAGE;
|
2013-11-11 15:11:28 +01:00
|
|
|
_noteHeadWidth = 0.0; // set in doLayout()
|
2014-07-25 19:29:22 +02:00
|
|
|
_midiPortCount = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Score
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-05-28 19:34:13 +02:00
|
|
|
Score::Score()
|
2014-06-03 21:56:18 +02:00
|
|
|
: QObject(0), _is(this), _selection(this), _selectionFilter(this)
|
2012-05-28 19:34:13 +02:00
|
|
|
{
|
2013-07-19 18:03:35 +02:00
|
|
|
_parentScore = 0;
|
2012-05-28 19:34:13 +02:00
|
|
|
init();
|
|
|
|
_tempomap = new TempoMap;
|
|
|
|
_sigmap = new TimeSigMap();
|
|
|
|
_style = *(MScore::defaultStyle());
|
2014-07-10 14:13:37 +02:00
|
|
|
accInfo = tr("No selection");
|
2012-05-28 19:34:13 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Score::Score(const MStyle* s)
|
2014-06-03 21:56:18 +02:00
|
|
|
: _is(this), _selection(this), _selectionFilter(this)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-07-19 18:03:35 +02:00
|
|
|
_parentScore = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
init();
|
|
|
|
_tempomap = new TempoMap;
|
|
|
|
_sigmap = new TimeSigMap();
|
|
|
|
_style = *s;
|
2014-07-10 14:13:37 +02:00
|
|
|
accInfo = tr("No selection");
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// a linked score shares some properties with parentScore():
|
|
|
|
// _undo
|
|
|
|
// _sigmap
|
|
|
|
// _tempomap
|
|
|
|
// _repeatList
|
|
|
|
// _links
|
|
|
|
// _staffTypes
|
2013-07-19 10:39:32 +02:00
|
|
|
// _metaTags
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2013-07-19 10:39:32 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Score::Score(Score* parent)
|
2014-06-03 21:56:18 +02:00
|
|
|
: _is(this), _selection(this), _selectionFilter(this)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
_parentScore = parent;
|
2013-07-19 18:03:35 +02:00
|
|
|
init();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-02-19 21:33:44 +01:00
|
|
|
if (MScore::defaultStyleForParts())
|
|
|
|
_style = *MScore::defaultStyleForParts();
|
2014-02-19 03:26:31 +01:00
|
|
|
else {
|
2014-02-19 21:33:44 +01:00
|
|
|
// inherit most style settings from parent
|
|
|
|
_style = *parent->style();
|
|
|
|
// but borrow defaultStyle page layout settings
|
2014-02-19 03:26:31 +01:00
|
|
|
const PageFormat* pf = MScore::defaultStyle()->pageFormat();
|
|
|
|
qreal sp = MScore::defaultStyle()->spatium();
|
|
|
|
_style.setPageFormat(*pf);
|
|
|
|
_style.setSpatium(sp);
|
2014-04-10 15:10:28 +02:00
|
|
|
|
|
|
|
//concert pitch is off for parts
|
2014-05-26 15:31:36 +02:00
|
|
|
_style.set(StyleIdx::concertPitch, false);
|
2014-02-19 03:26:31 +01:00
|
|
|
}
|
2014-01-30 13:10:45 +01:00
|
|
|
|
2013-04-02 20:46:07 +02:00
|
|
|
_synthesizerState = parent->_synthesizerState;
|
2014-07-10 14:13:37 +02:00
|
|
|
accInfo = tr("No selection");
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-04-17 22:41:11 +02:00
|
|
|
Score::Score(Score* parent, const MStyle* s)
|
2014-06-03 21:56:18 +02:00
|
|
|
: _is(this), _selection(this), _selectionFilter(this)
|
2014-04-17 22:41:11 +02:00
|
|
|
{
|
|
|
|
_parentScore = parent;
|
|
|
|
init();
|
|
|
|
_style = *s;
|
2014-07-10 14:13:37 +02:00
|
|
|
accInfo = tr("No selection");
|
2014-04-17 22:41:11 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// ~Score
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Score::~Score()
|
|
|
|
{
|
2014-07-25 19:29:22 +02:00
|
|
|
_midiPortCount = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(MuseScoreView* v, viewer)
|
|
|
|
v->removeScore();
|
2013-10-05 14:03:34 +02:00
|
|
|
// deselectAll();
|
2012-05-26 14:26:10 +02:00
|
|
|
for (MeasureBase* m = _measures.first(); m;) {
|
|
|
|
MeasureBase* nm = m->next();
|
|
|
|
delete m;
|
|
|
|
m = nm;
|
|
|
|
}
|
|
|
|
foreach(Part* p, _parts)
|
|
|
|
delete p;
|
|
|
|
foreach(Staff* staff, _staves)
|
|
|
|
delete staff;
|
|
|
|
foreach(System* s, _systems)
|
|
|
|
delete s;
|
|
|
|
foreach(Page* page, _pages)
|
|
|
|
delete page;
|
|
|
|
foreach(Excerpt* e, _excerpts)
|
|
|
|
delete e;
|
|
|
|
delete _revisions;
|
|
|
|
delete _undo; // this also removes _undoStack from Mscore::_undoGroup
|
|
|
|
delete _tempomap;
|
|
|
|
delete _sigmap;
|
|
|
|
delete _repeatList;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// elementAdjustReadPos
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void elementAdjustReadPos(void*, Element* e)
|
|
|
|
{
|
|
|
|
if (e->isMovable())
|
|
|
|
e->adjustReadPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addMeasure(MeasureBase* m, MeasureBase* pos)
|
|
|
|
{
|
|
|
|
m->setNext(pos);
|
|
|
|
_measures.add(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// fixTicks
|
|
|
|
// update:
|
|
|
|
// - measure ticks
|
|
|
|
// - tempo map
|
|
|
|
// - time signature map
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
This is needed after
|
2013-05-14 16:43:21 +02:00
|
|
|
- inserting or removing a measure
|
2012-05-26 14:26:10 +02:00
|
|
|
- changing the sigmap
|
|
|
|
- after inserting/deleting time (changes the sigmap)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Score::fixTicks()
|
|
|
|
{
|
2013-09-19 15:08:54 +02:00
|
|
|
int tick = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* fm = firstMeasure();
|
|
|
|
if (fm == 0)
|
|
|
|
return;
|
|
|
|
|
2014-05-03 11:33:47 +02:00
|
|
|
for (Staff* staff : _staves)
|
|
|
|
staff->clearTimeSig();
|
2014-11-27 13:04:03 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
TimeSigMap* smap = sigmap();
|
|
|
|
Fraction sig(fm->len());
|
|
|
|
Fraction nsig(fm->timesig());
|
2014-11-27 13:04:03 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!parentScore()) {
|
|
|
|
tempomap()->clear();
|
|
|
|
smap->clear();
|
2013-09-19 15:08:54 +02:00
|
|
|
smap->add(0, SigEvent(sig, nsig, 0));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() != Element::Type::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setTick(tick);
|
|
|
|
continue;
|
|
|
|
}
|
2013-09-28 12:05:48 +02:00
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
2012-05-26 14:26:10 +02:00
|
|
|
int mtick = m->tick();
|
|
|
|
int diff = tick - mtick;
|
|
|
|
int measureTicks = m->ticks();
|
|
|
|
m->moveTicks(diff);
|
2013-09-28 12:05:48 +02:00
|
|
|
if (m->mmRest())
|
|
|
|
m->mmRest()->moveTicks(diff);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-11-27 13:04:03 +01:00
|
|
|
// if (!parentScore()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// implement section break rest
|
|
|
|
//
|
2015-01-29 22:37:39 +01:00
|
|
|
if (m->sectionBreak() && m->pause() != 0.0)
|
2012-05-26 14:26:10 +02:00
|
|
|
setPause(m->tick() + m->ticks(), m->pause());
|
|
|
|
|
|
|
|
//
|
|
|
|
// implement fermata as a tempo change
|
|
|
|
//
|
|
|
|
|
2014-06-05 11:57:01 +02:00
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
2015-01-29 22:37:39 +01:00
|
|
|
if (s->segmentType() == Segment::Type::Breath) {
|
|
|
|
qreal length = 0.0;
|
|
|
|
int tick = s->tick();
|
2015-02-06 17:01:41 +01:00
|
|
|
// find longest pause
|
2015-01-29 22:37:39 +01:00
|
|
|
for (int i = 0, n = ntracks(); i < n; ++i) {
|
|
|
|
Element* e = s->element(i);
|
|
|
|
if (e && e->type() == Element::Type::BREATH) {
|
|
|
|
Breath* b = static_cast<Breath*>(e);
|
|
|
|
length = qMax(length, b->pause());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (length != 0.0)
|
|
|
|
setPause(tick, length);
|
|
|
|
}
|
2014-06-25 11:46:10 +02:00
|
|
|
else if (s->segmentType() == Segment::Type::TimeSig) {
|
2014-05-03 11:33:47 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
TimeSig* ts = static_cast<TimeSig*>(s->element(staffIdx * VOICES));
|
|
|
|
if (ts)
|
|
|
|
staff(staffIdx)->addTimeSig(ts);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-11-27 13:04:03 +01:00
|
|
|
else if (!parentScore() && (s->segmentType() == Segment::Type::ChordRest)) {
|
|
|
|
foreach (Element* e, s->annotations()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::TEMPO_TEXT) {
|
2012-12-06 12:46:05 +01:00
|
|
|
const TempoText* tt = static_cast<const TempoText*>(e);
|
|
|
|
setTempo(tt->segment(), tt->tempo());
|
|
|
|
}
|
|
|
|
}
|
2013-06-03 14:39:59 +02:00
|
|
|
qreal stretch = 0.0;
|
|
|
|
for (int i = 0; i < s->elist().size(); ++i) {
|
2012-12-06 12:46:05 +01:00
|
|
|
Element* e = s->elist().at(i);
|
|
|
|
if (!e)
|
|
|
|
continue;
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
|
|
|
int nn = cr->articulations().size();
|
|
|
|
for (int ii = 0; ii < nn; ++ii)
|
|
|
|
stretch = qMax(cr->articulations().at(ii)->timeStretch(), stretch);
|
2013-09-20 13:41:42 +02:00
|
|
|
if (stretch != 0.0 && stretch != 1.0) {
|
2012-12-06 12:46:05 +01:00
|
|
|
qreal otempo = tempomap()->tempo(cr->tick());
|
|
|
|
qreal ntempo = otempo / stretch;
|
|
|
|
setTempo(cr->tick(), ntempo);
|
2013-06-03 17:30:31 +02:00
|
|
|
int etick = cr->tick() + cr->actualTicks() - 1;
|
|
|
|
auto e = tempomap()->find(etick);
|
|
|
|
if (e == tempomap()->end())
|
|
|
|
setTempo(etick, otempo);
|
2012-12-06 12:46:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-27 13:04:03 +01:00
|
|
|
// }
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// update time signature map
|
|
|
|
//
|
|
|
|
if (!parentScore() && (m->len() != sig)) {
|
|
|
|
sig = m->len();
|
2013-09-19 15:08:54 +02:00
|
|
|
smap->add(tick, SigEvent(sig, m->timesig(), m->no()));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-11-27 13:04:03 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
tick += measureTicks;
|
|
|
|
}
|
|
|
|
if (tempomap()->empty())
|
|
|
|
tempomap()->setTempo(0, 2.0);
|
|
|
|
}
|
|
|
|
|
2012-11-08 12:59:30 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// validSegment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool validSegment(Segment* s, int startTrack, int endTrack)
|
|
|
|
{
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
if (s->element(track))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// pos2measure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
Return measure for canvas relative position \a p.
|
|
|
|
*/
|
|
|
|
|
|
|
|
MeasureBase* Score::pos2measure(const QPointF& p, int* rst, int* pitch,
|
|
|
|
Segment** seg, QPointF* offset) const
|
|
|
|
{
|
|
|
|
Measure* m = searchMeasure(p);
|
|
|
|
if (m == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
System* s = m->system();
|
|
|
|
// qreal sy1 = 0;
|
|
|
|
qreal y = p.y() - s->canvasPos().y();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nstaves();) {
|
2013-08-29 18:42:01 +02:00
|
|
|
SysStaff* stff = s->staff(i);
|
|
|
|
if (!stff->show() || !staff(i)->show()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int ni = i;
|
|
|
|
for (;;) {
|
|
|
|
++ni;
|
2013-08-29 18:42:01 +02:00
|
|
|
if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show()))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal sy2;
|
|
|
|
if (ni != nstaves()) {
|
|
|
|
SysStaff* nstaff = s->staff(ni);
|
2013-08-29 18:42:01 +02:00
|
|
|
qreal s1y2 = stff->bbox().y() + stff->bbox().height();
|
2012-05-26 14:26:10 +02:00
|
|
|
sy2 = s1y2 + (nstaff->bbox().y() - s1y2)/2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sy2 = s->page()->height() - s->pos().y(); // s->height();
|
|
|
|
if (y > sy2) {
|
|
|
|
// sy1 = sy2;
|
|
|
|
i = ni;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// search for segment + offset
|
|
|
|
QPointF pppp = p - m->canvasPos();
|
2012-11-08 12:59:30 +01:00
|
|
|
int strack = i * VOICES;
|
|
|
|
int etrack = staff(i)->part()->nstaves() * VOICES + strack;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
SysStaff* sstaff = m->system()->staff(i);
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
2012-11-08 12:59:30 +01:00
|
|
|
for (Segment* segment = m->first(st); segment; segment = segment->next(st)) {
|
|
|
|
if (!validSegment(segment, strack, etrack))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2012-11-08 12:59:30 +01:00
|
|
|
Segment* ns = segment->next(st);
|
|
|
|
for (; ns; ns = ns->next(st)) {
|
|
|
|
if (validSegment(ns, strack, etrack))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!ns || (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0))) {
|
|
|
|
*rst = i;
|
|
|
|
if (pitch) {
|
|
|
|
Staff* s = _staves[i];
|
2013-09-05 16:37:49 +02:00
|
|
|
ClefType clef = s->clef(segment->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
*pitch = y2pitch(pppp.y() - sstaff->bbox().y(), clef, s->spatium());
|
|
|
|
}
|
|
|
|
if (offset)
|
|
|
|
*offset = pppp - QPointF(segment->x(), sstaff->bbox().y());
|
|
|
|
if (seg)
|
|
|
|
*seg = segment;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// staffIdx
|
|
|
|
//
|
|
|
|
/// Return index for the first staff of \a part.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::staffIdx(const Part* part) const
|
|
|
|
{
|
|
|
|
int idx = 0;
|
|
|
|
foreach(Part* p, _parts) {
|
|
|
|
if (p == part)
|
|
|
|
break;
|
|
|
|
idx += p->nstaves();
|
|
|
|
}
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setShowInvisible
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setShowInvisible(bool v)
|
|
|
|
{
|
|
|
|
_showInvisible = v;
|
2014-05-07 12:10:28 +02:00
|
|
|
rebuildBspTree();
|
|
|
|
_updateAll = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setShowUnprintable
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setShowUnprintable(bool v)
|
|
|
|
{
|
|
|
|
_showUnprintable = v;
|
|
|
|
_updateAll = true;
|
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setShowFrames
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setShowFrames(bool v)
|
|
|
|
{
|
|
|
|
_showFrames = v;
|
|
|
|
_updateAll = true;
|
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setShowPageborders
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setShowPageborders(bool v)
|
|
|
|
{
|
|
|
|
_showPageborders = v;
|
|
|
|
_updateAll = true;
|
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
2014-02-25 14:14:59 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// dirty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::dirty() const
|
|
|
|
{
|
2015-01-30 17:03:51 +01:00
|
|
|
return !undo()->isClean();
|
2014-02-25 14:14:59 +01:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// spell
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::spell()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < nstaves(); ++i) {
|
|
|
|
QList<Note*> notes;
|
|
|
|
for (Segment* s = firstSegment(); s; s = s->next1()) {
|
|
|
|
int strack = i * VOICES;
|
|
|
|
int etrack = strack + VOICES;
|
|
|
|
for (int track = strack; track < etrack; ++track) {
|
|
|
|
Element* e = s->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && e->type() == Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
notes.append(static_cast<Chord*>(e)->notes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spellNotelist(notes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Score::spell(int startStaff, int endStaff, Segment* startSegment, Segment* endSegment)
|
|
|
|
{
|
|
|
|
for (int i = startStaff; i < endStaff; ++i) {
|
|
|
|
QList<Note*> notes;
|
|
|
|
for (Segment* s = startSegment; s && s != endSegment; s = s->next()) {
|
|
|
|
int strack = i * VOICES;
|
|
|
|
int etrack = strack + VOICES;
|
|
|
|
for (int track = strack; track < etrack; ++track) {
|
|
|
|
Element* e = s->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && e->type() == Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
notes.append(static_cast<Chord*>(e)->notes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spellNotelist(notes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevNote
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* prevNote(Note* n)
|
|
|
|
{
|
|
|
|
Chord* chord = n->chord();
|
|
|
|
Segment* seg = chord->segment();
|
|
|
|
const QList<Note*> nl = chord->notes();
|
|
|
|
int i = nl.indexOf(n);
|
|
|
|
if (i)
|
|
|
|
return nl[i-1];
|
|
|
|
int staff = n->staffIdx();
|
|
|
|
int startTrack = staff * VOICES + n->voice() - 1;
|
|
|
|
int endTrack = 0;
|
|
|
|
while (seg) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (seg->segmentType() == Segment::Type::ChordRest) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = startTrack; track >= endTrack; --track) {
|
|
|
|
Element* e = seg->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && e->type() == Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Chord*>(e)->upNote();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
seg = seg->prev1();
|
|
|
|
startTrack = staff * VOICES + VOICES - 1;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextNote
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* nextNote(Note* n)
|
|
|
|
{
|
|
|
|
Chord* chord = n->chord();
|
|
|
|
const QList<Note*> nl = chord->notes();
|
|
|
|
int i = nl.indexOf(n);
|
|
|
|
++i;
|
|
|
|
if (i < nl.size())
|
|
|
|
return nl[i];
|
|
|
|
Segment* seg = chord->segment();
|
|
|
|
int staff = n->staffIdx();
|
|
|
|
int startTrack = staff * VOICES + n->voice() + 1;
|
|
|
|
int endTrack = staff * VOICES + VOICES;
|
|
|
|
while (seg) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (seg->segmentType() == Segment::Type::ChordRest) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
Element* e = seg->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && e->type() == Element::Type::CHORD) {
|
2012-05-26 14:26:10 +02:00
|
|
|
return ((Chord*)e)->downNote();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
seg = seg->next1();
|
|
|
|
startTrack = staff * VOICES;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// spell
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::spell(Note* note)
|
|
|
|
{
|
|
|
|
QList<Note*> notes;
|
|
|
|
|
|
|
|
notes.append(note);
|
|
|
|
Note* nn = nextNote(note);
|
|
|
|
notes.append(nn);
|
|
|
|
nn = nextNote(nn);
|
|
|
|
notes.append(nn);
|
|
|
|
nn = nextNote(nn);
|
|
|
|
notes.append(nn);
|
|
|
|
|
|
|
|
nn = prevNote(note);
|
|
|
|
notes.prepend(nn);
|
|
|
|
nn = prevNote(nn);
|
|
|
|
notes.prepend(nn);
|
|
|
|
nn = prevNote(nn);
|
|
|
|
notes.prepend(nn);
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
int opt = Ms::computeWindow(notes, 0, 7);
|
|
|
|
note->setTpc(Ms::tpc(3, note->pitch(), opt));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// isSavable
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::isSavable() const
|
|
|
|
{
|
|
|
|
// TODO: check if file can be created if it does not exist
|
|
|
|
return info.isWritable() || !info.exists();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendPart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::appendPart(Part* p)
|
|
|
|
{
|
|
|
|
_parts.append(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// rebuildMidiMapping
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::rebuildMidiMapping()
|
|
|
|
{
|
|
|
|
_midiMapping.clear();
|
2015-03-05 16:46:37 +01:00
|
|
|
int port = 0;
|
|
|
|
int midiChannel = 0;
|
|
|
|
int idx = 0;
|
|
|
|
int maxport = 0;
|
|
|
|
for (Part* part : _parts) {
|
2015-03-13 13:11:20 +01:00
|
|
|
const InstrumentList* il = part->instruments();
|
2013-05-28 15:42:02 +02:00
|
|
|
for (auto i = il->begin(); i != il->end(); ++i) {
|
2015-03-13 13:11:20 +01:00
|
|
|
const Instrument* instr = i->second;
|
2015-03-05 16:46:37 +01:00
|
|
|
bool drum = instr->useDrumset();
|
|
|
|
|
|
|
|
for (Channel* channel : instr->channel()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
MidiMapping mm;
|
2014-07-25 19:29:22 +02:00
|
|
|
if (port > maxport)
|
|
|
|
maxport = port;
|
2015-02-11 13:12:25 +01:00
|
|
|
if (drum) {
|
2012-05-26 14:26:10 +02:00
|
|
|
mm.port = port;
|
|
|
|
mm.channel = 9;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mm.port = port;
|
2015-03-05 16:46:37 +01:00
|
|
|
mm.channel = midiChannel;
|
|
|
|
if (midiChannel == 15) {
|
|
|
|
midiChannel = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
++port;
|
|
|
|
}
|
|
|
|
else {
|
2015-03-05 16:46:37 +01:00
|
|
|
++midiChannel;
|
|
|
|
if (midiChannel == 9) // skip drum channel
|
|
|
|
++midiChannel;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mm.part = part;
|
2015-03-05 16:46:37 +01:00
|
|
|
mm.articulation = channel;
|
2012-05-26 14:26:10 +02:00
|
|
|
_midiMapping.append(mm);
|
2015-03-05 16:46:37 +01:00
|
|
|
channel->channel = idx;
|
2012-05-26 14:26:10 +02:00
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-25 19:29:22 +02:00
|
|
|
setMidiPortCount(maxport);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// midiPortCount
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::midiPortCount() const {
|
|
|
|
const Score* root = rootScore();
|
|
|
|
if (this == root)
|
|
|
|
return _midiPortCount;
|
|
|
|
else
|
|
|
|
return root->midiPortCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setMidiPortCount
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setMidiPortCount(int maxport) {
|
|
|
|
Score* root = rootScore();
|
|
|
|
if (this == root)
|
|
|
|
_midiPortCount = maxport;
|
|
|
|
else
|
|
|
|
root->setMidiPortCount(maxport);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// midiPort
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::midiPort(int idx) const
|
|
|
|
{
|
|
|
|
return _midiMapping[idx].port;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// midiChannel
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::midiChannel(int idx) const
|
|
|
|
{
|
|
|
|
return _midiMapping[idx].channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchPage
|
|
|
|
// p is in canvas coordinates
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Page* Score::searchPage(const QPointF& p) const
|
|
|
|
{
|
|
|
|
foreach(Page* page, pages()) {
|
|
|
|
if (page->bbox().translated(page->pos()).contains(p))
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchSystem
|
|
|
|
// return list of systems as there may be more than
|
|
|
|
// one system in a row
|
|
|
|
// p is in canvas coordinates
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<System*> Score::searchSystem(const QPointF& pos) const
|
|
|
|
{
|
|
|
|
QList<System*> systems;
|
|
|
|
Page* page = searchPage(pos);
|
|
|
|
if (page == 0)
|
|
|
|
return systems;
|
|
|
|
qreal y = pos.y() - page->pos().y(); // transform to page relative
|
|
|
|
const QList<System*>* sl = page->systems();
|
|
|
|
qreal y2;
|
|
|
|
int n = sl->size();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
System* s = sl->at(i);
|
|
|
|
System* ns = 0; // next system row
|
|
|
|
int ii = i + 1;
|
|
|
|
for (; ii < n; ++ii) {
|
|
|
|
ns = sl->at(ii);
|
|
|
|
if (ns->y() != s->y())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((ii == n) || (ns == 0))
|
|
|
|
y2 = page->height();
|
|
|
|
else {
|
|
|
|
qreal sy2 = s->y() + s->bbox().height();
|
|
|
|
y2 = sy2 + (ns->y() - sy2) * .5;
|
|
|
|
}
|
|
|
|
if (y < y2) {
|
|
|
|
systems.append(s);
|
|
|
|
for (int ii = i+1; ii < n; ++ii) {
|
|
|
|
if (sl->at(ii)->y() != s->y())
|
|
|
|
break;
|
|
|
|
systems.append(sl->at(ii));
|
|
|
|
}
|
|
|
|
return systems;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return systems;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchMeasure
|
|
|
|
// p is in canvas coordinates
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::searchMeasure(const QPointF& p) const
|
|
|
|
{
|
|
|
|
QList<System*> systems = searchSystem(p);
|
|
|
|
if (systems.isEmpty())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
foreach(System* system, systems) {
|
|
|
|
qreal x = p.x() - system->canvasPos().x();
|
|
|
|
foreach(MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
if (x < (mb->x() + mb->bbox().width()))
|
|
|
|
return static_cast<Measure*>(mb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getNextValidInputSegment
|
2014-06-25 11:46:10 +02:00
|
|
|
// - s is of type Segment::Type::ChordRest
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static Segment* getNextValidInputSegment(Segment* s, int track, int voice)
|
|
|
|
{
|
|
|
|
if (s == 0)
|
|
|
|
return 0;
|
2014-06-25 11:46:10 +02:00
|
|
|
Q_ASSERT(s->segmentType() == Segment::Type::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
// Segment* s1 = s;
|
|
|
|
ChordRest* cr1;
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s1 = s; s1; s1 = s1->prev(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
cr1 = static_cast<ChordRest*>(s1->element(track + voice));
|
|
|
|
if (cr1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
int nextTick = (cr1 == 0) ? s->measure()->tick() : cr1->tick() + cr1->actualTicks();
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
static const Segment::Type st { Segment::Type::ChordRest };
|
2012-05-26 14:26:10 +02:00
|
|
|
while (s) {
|
|
|
|
if (s->element(track + voice))
|
|
|
|
break;
|
|
|
|
if (voice && s->tick() == nextTick)
|
|
|
|
return s;
|
|
|
|
#if 0
|
|
|
|
int v;
|
|
|
|
for (v = 0; v < VOICES; ++v) {
|
|
|
|
if (s->element(track + v))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((v != VOICES) && voice) {
|
|
|
|
int ntick;
|
|
|
|
bool skipChord = false;
|
|
|
|
bool ns = false;
|
|
|
|
for (Segment* s1 = s->measure()->first(st); s1; s1 = s1->next(st)) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s1->element(track + voice));
|
|
|
|
if (cr) {
|
|
|
|
if (ns)
|
|
|
|
return s1;
|
|
|
|
ntick = s1->tick() + cr->actualTicks();
|
|
|
|
skipChord = true;
|
|
|
|
}
|
|
|
|
if (s1 == s)
|
|
|
|
ns = true;
|
|
|
|
if (skipChord) {
|
|
|
|
if (s->tick() >= ntick)
|
|
|
|
skipChord = false;
|
|
|
|
}
|
|
|
|
if (!skipChord && ns)
|
|
|
|
return s1;
|
|
|
|
}
|
|
|
|
if (!skipChord)
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
s = s->next(st);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getPosition
|
|
|
|
// return true if valid position found
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::getPosition(Position* pos, const QPointF& p, int voice) const
|
|
|
|
{
|
2012-08-07 12:44:19 +02:00
|
|
|
Measure* measure = searchMeasure(p);
|
|
|
|
if (measure == 0)
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
|
2012-08-29 18:42:28 +02:00
|
|
|
pos->fret = FRET_NONE;
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// search staff
|
|
|
|
//
|
|
|
|
pos->staffIdx = 0;
|
|
|
|
SysStaff* sstaff = 0;
|
2012-08-07 12:44:19 +02:00
|
|
|
System* system = measure->system();
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal y = p.y() - system->pagePos().y();
|
|
|
|
for (; pos->staffIdx < nstaves(); ++pos->staffIdx) {
|
2014-07-31 14:12:34 +02:00
|
|
|
Staff* st = staff(pos->staffIdx);
|
|
|
|
if (st->invisible() || !st->part()->show())
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal sy2;
|
|
|
|
SysStaff* ss = system->staff(pos->staffIdx);
|
2014-07-31 14:12:34 +02:00
|
|
|
SysStaff* nstaff = 0;
|
|
|
|
|
|
|
|
// find next visible staff
|
|
|
|
for (int i = pos->staffIdx + 1; i < nstaves(); ++i) {
|
|
|
|
Staff* st = staff(i);
|
|
|
|
if (st->invisible() || !st->part()->show())
|
|
|
|
continue;
|
|
|
|
nstaff = system->staff(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nstaff) {
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal s1y2 = ss->bbox().y() + ss->bbox().height();
|
2014-07-31 14:12:34 +02:00
|
|
|
sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * .5;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
sy2 = system->page()->height() - system->pos().y(); // system->height();
|
|
|
|
if (y < sy2) {
|
|
|
|
sstaff = ss;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sstaff == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
//
|
|
|
|
// search segment
|
|
|
|
//
|
2012-08-07 12:44:19 +02:00
|
|
|
QPointF pppp(p - measure->canvasPos());
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal x = pppp.x();
|
|
|
|
Segment* segment = 0;
|
|
|
|
pos->segment = 0;
|
|
|
|
|
|
|
|
// int track = pos->staffIdx * VOICES + voice;
|
|
|
|
int track = pos->staffIdx * VOICES;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
for (segment = measure->first(Segment::Type::ChordRest); segment;) {
|
2012-05-26 14:26:10 +02:00
|
|
|
segment = getNextValidInputSegment(segment, track, voice);
|
|
|
|
if (segment == 0)
|
|
|
|
break;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* ns = getNextValidInputSegment(segment->next(Segment::Type::ChordRest), track, voice);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
qreal x1 = segment->x();
|
|
|
|
qreal x2;
|
|
|
|
qreal d;
|
|
|
|
if (ns) {
|
|
|
|
x2 = ns->x();
|
|
|
|
d = x2 - x1;
|
|
|
|
}
|
|
|
|
else {
|
2012-08-07 12:44:19 +02:00
|
|
|
x2 = measure->bbox().width();
|
2012-05-26 14:26:10 +02:00
|
|
|
d = (x2 - x1) * 2.0;
|
|
|
|
x = x1;
|
|
|
|
pos->segment = segment;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x < (x1 + d * .5)) {
|
|
|
|
x = x1;
|
|
|
|
pos->segment = segment;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
segment = ns;
|
|
|
|
}
|
|
|
|
if (segment == 0)
|
|
|
|
return false;
|
|
|
|
//
|
|
|
|
// TODO: restrict to reasonable values (pitch 0-127)
|
|
|
|
//
|
|
|
|
Staff* s = staff(pos->staffIdx);
|
2012-09-07 16:43:29 +02:00
|
|
|
qreal mag = s->mag();
|
|
|
|
// in TABs, step from one string to another; in other staves, step on and between lines
|
|
|
|
qreal lineDist = s->staffType()->lineDistance().val() * (s->isTabStaff() ? 1 : .5) * mag * spatium();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
pos->line = lrint((pppp.y() - sstaff->bbox().y()) / lineDist);
|
2012-08-16 11:09:36 +02:00
|
|
|
if (s->isTabStaff()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (pos->line < -1 || pos->line > s->lines()+1)
|
|
|
|
return false;
|
|
|
|
if (pos->line < 0)
|
|
|
|
pos->line = 0;
|
|
|
|
else if (pos->line >= s->lines())
|
|
|
|
pos->line = s->lines() - 1;
|
|
|
|
}
|
|
|
|
else {
|
2012-08-07 16:05:37 +02:00
|
|
|
int minLine = absStep(0);
|
2012-08-07 12:44:19 +02:00
|
|
|
ClefType clef = s->clef(pos->segment->tick());
|
2012-08-07 16:05:37 +02:00
|
|
|
minLine = relStep(minLine, clef);
|
|
|
|
int maxLine = absStep(127);
|
|
|
|
maxLine = relStep(maxLine, clef);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (pos->line > minLine || pos->line < maxLine)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
y = sstaff->y() + pos->line * lineDist;
|
2012-08-07 12:44:19 +02:00
|
|
|
pos->pos = QPointF(x, y) + measure->canvasPos();
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// checkHasMeasures
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::checkHasMeasures() const
|
|
|
|
{
|
|
|
|
Page* page = pages().front();
|
|
|
|
const QList<System*>* sl = page->systems();
|
|
|
|
if (sl == 0 || sl->empty() || sl->front()->measures().empty()) {
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("first create measure, then repeat operation");
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// moveBracket
|
|
|
|
// columns are counted from right to left
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::moveBracket(int staffIdx, int srcCol, int dstCol)
|
|
|
|
{
|
|
|
|
foreach(System* system, *systems()) {
|
|
|
|
if (system->isVbox())
|
|
|
|
continue;
|
2012-10-12 15:36:57 +02:00
|
|
|
foreach(Bracket* b, system->brackets()) {
|
|
|
|
if (b->staffIdx() == staffIdx && b->level() == srcCol)
|
|
|
|
b->setLevel(dstCol);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// spatiumHasChanged
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void spatiumHasChanged(void* data, Element* e)
|
|
|
|
{
|
|
|
|
qreal* val = (qreal*)data;
|
|
|
|
e->spatiumChanged(val[0], val[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// spatiumChanged
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::spatiumChanged(qreal oldValue, qreal newValue)
|
|
|
|
{
|
|
|
|
qreal data[2];
|
|
|
|
data[0] = oldValue;
|
|
|
|
data[1] = newValue;
|
|
|
|
scanElements(data, spatiumHasChanged, true);
|
|
|
|
foreach (Staff* staff, _staves)
|
|
|
|
staff->spatiumChanged(oldValue, newValue);
|
2013-11-11 15:11:28 +01:00
|
|
|
_noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, newValue / (MScore::DPI * SPATIUM20));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-05-27 15:59:43 +02:00
|
|
|
void Score::setSpatium(qreal v)
|
|
|
|
{
|
|
|
|
style()->setSpatium(v);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getCreateMeasure
|
|
|
|
// - return Measure for tick
|
|
|
|
// - create new Measure(s) if there is no measure for
|
|
|
|
// this tick
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::getCreateMeasure(int tick)
|
|
|
|
{
|
|
|
|
Measure* last = lastMeasure();
|
|
|
|
if (last == 0 || ((last->tick() + last->ticks()) <= tick)) {
|
|
|
|
int lastTick = last ? (last->tick()+last->ticks()) : 0;
|
|
|
|
while (tick >= lastTick) {
|
|
|
|
Measure* m = new Measure(this);
|
|
|
|
Fraction ts = _sigmap->timesig(lastTick).timesig();
|
2013-07-25 17:22:49 +02:00
|
|
|
// qDebug("getCreateMeasure %d %d/%d", tick, ts.numerator(), ts.denominator());
|
2012-05-26 14:26:10 +02:00
|
|
|
m->setTick(lastTick);
|
|
|
|
m->setTimesig(ts);
|
|
|
|
m->setLen(ts);
|
2014-07-03 15:02:36 +02:00
|
|
|
measures()->add(static_cast<MeasureBase*>(m));
|
2012-05-26 14:26:10 +02:00
|
|
|
lastTick += ts.ticks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tick2measure(tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
Add \a element to its parent.
|
|
|
|
|
|
|
|
Several elements (clef, keysig, timesig) need special handling, as they may cause
|
|
|
|
changes throughout the score.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Score::addElement(Element* element)
|
|
|
|
{
|
|
|
|
if (MScore::debugMode) {
|
|
|
|
qDebug(" Score(%p)::addElement %p(%s) parent %p(%s)",
|
|
|
|
this, element, element->name(), element->parent(),
|
|
|
|
element->parent() ? element->parent()->name() : "");
|
|
|
|
}
|
2013-09-16 18:32:59 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (element->parent() && element->parent()->type() == Element::Type::SEGMENT)
|
2012-08-08 20:46:29 +02:00
|
|
|
static_cast<Segment*>(element->parent())->measure()->setDirty();
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type et = element->type();
|
|
|
|
if (et == Element::Type::TREMOLO)
|
2013-06-05 15:47:34 +02:00
|
|
|
setLayoutAll(true);
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (et == Element::Type::MEASURE
|
|
|
|
|| (et == Element::Type::HBOX && element->parent()->type() != Element::Type::VBOX)
|
|
|
|
|| et == Element::Type::VBOX
|
|
|
|
|| et == Element::Type::TBOX
|
|
|
|
|| et == Element::Type::FBOX
|
2012-05-26 14:26:10 +02:00
|
|
|
) {
|
2014-02-28 14:25:47 +01:00
|
|
|
setLayoutAll(true);
|
2014-07-03 15:02:36 +02:00
|
|
|
measures()->add(static_cast<MeasureBase*>(element));
|
2014-05-30 10:16:38 +02:00
|
|
|
addLayoutFlags(LayoutFlag::FIX_TICKS);
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-03 15:02:36 +02:00
|
|
|
if (element->parent())
|
2012-05-26 14:26:10 +02:00
|
|
|
element->parent()->add(element);
|
|
|
|
|
|
|
|
switch(et) {
|
2014-07-03 15:02:36 +02:00
|
|
|
case Element::Type::BEAM:
|
|
|
|
{
|
|
|
|
Beam* b = static_cast<Beam*>(element);
|
|
|
|
int n = b->elements().size();
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
b->elements().at(i)->setBeam(b);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SLUR:
|
2014-05-30 10:16:38 +02:00
|
|
|
addLayoutFlags(LayoutFlag::PLAY_EVENTS);
|
2013-06-20 18:48:28 +02:00
|
|
|
// fall through
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::VOLTA:
|
|
|
|
case Element::Type::TRILL:
|
|
|
|
case Element::Type::PEDAL:
|
|
|
|
case Element::Type::TEXTLINE:
|
|
|
|
case Element::Type::HAIRPIN:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Spanner* spanner = static_cast<Spanner*>(element);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (et == Element::Type::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
|
2013-07-04 13:40:25 +02:00
|
|
|
break;
|
2014-07-09 18:05:58 +02:00
|
|
|
addSpanner(spanner);
|
|
|
|
for (SpannerSegment* ss : spanner->spannerSegments()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (ss->system())
|
|
|
|
ss->system()->add(ss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::OTTAVA:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Ottava* o = static_cast<Ottava*>(element);
|
2014-07-03 15:02:36 +02:00
|
|
|
addSpanner(o);
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(SpannerSegment* ss, o->spannerSegments()) {
|
|
|
|
if (ss->system())
|
|
|
|
ss->system()->add(ss);
|
|
|
|
}
|
2014-05-30 10:16:38 +02:00
|
|
|
layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
|
2014-07-15 12:49:51 +02:00
|
|
|
o->staff()->updateOttava();
|
2012-05-26 14:26:10 +02:00
|
|
|
_playlistDirty = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::DYNAMIC:
|
2014-05-30 10:16:38 +02:00
|
|
|
layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
|
2012-05-26 14:26:10 +02:00
|
|
|
_playlistDirty = true;
|
|
|
|
break;
|
2013-07-05 11:23:52 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEMPO_TEXT:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
TempoText* tt = static_cast<TempoText*>(element);
|
|
|
|
setTempo(tt->segment(), tt->tempo());
|
|
|
|
}
|
|
|
|
break;
|
2013-07-05 11:23:52 +02:00
|
|
|
|
2015-03-05 16:46:37 +01:00
|
|
|
case Element::Type::INSTRUMENT_CHANGE: {
|
|
|
|
InstrumentChange* ic = static_cast<InstrumentChange*>(element);
|
|
|
|
ic->part()->setInstrument(ic->instrument(), ic->segment()->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
rebuildMidiMapping();
|
|
|
|
_instrumentsChanged = true;
|
2015-03-05 16:46:37 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CHORD:
|
2015-01-30 17:03:51 +01:00
|
|
|
setPlaylistDirty();
|
2015-01-29 10:19:25 +01:00
|
|
|
// create playlist does not work here bc. tremolos may not be complete
|
|
|
|
// createPlayEvents(static_cast<Chord*>(element));
|
2012-11-21 15:57:35 +01:00
|
|
|
break;
|
|
|
|
|
2015-02-17 20:22:24 +01:00
|
|
|
case Element::Type::NOTE:
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TREMOLO:
|
|
|
|
case Element::Type::ARTICULATION:
|
|
|
|
case Element::Type::ARPEGGIO:
|
2013-01-03 16:56:56 +01:00
|
|
|
{
|
2012-12-08 17:24:20 +01:00
|
|
|
Element* cr = element->parent();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD)
|
2013-07-01 16:28:39 +02:00
|
|
|
createPlayEvents(static_cast<Chord*>(cr));
|
2012-12-08 17:24:20 +01:00
|
|
|
}
|
2012-11-20 20:51:18 +01:00
|
|
|
break;
|
2015-01-29 22:37:39 +01:00
|
|
|
case Element::Type::BREATH:
|
|
|
|
addLayoutFlags(LayoutFlag::FIX_TICKS);
|
|
|
|
break;
|
|
|
|
case Element::Type::LAYOUT_BREAK:
|
|
|
|
if (static_cast<LayoutBreak*>(element)->layoutBreakType() == LayoutBreak::Type::SECTION)
|
|
|
|
addLayoutFlags(LayoutFlag::FIX_TICKS);
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
setLayoutAll(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeElement
|
|
|
|
/// Remove \a element from its parent.
|
|
|
|
/// Several elements (clef, keysig, timesig) need special handling, as they may cause
|
|
|
|
/// changes throughout the score.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeElement(Element* element)
|
|
|
|
{
|
|
|
|
Element* parent = element->parent();
|
|
|
|
|
|
|
|
if (MScore::debugMode) {
|
|
|
|
qDebug(" Score(%p)::removeElement %p(%s) parent %p(%s)",
|
|
|
|
this, element, element->name(), parent, parent ? parent->name() : "");
|
|
|
|
}
|
2014-05-21 20:08:37 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (parent && parent->type() == Element::Type::SEGMENT)
|
2013-10-30 09:00:01 +01:00
|
|
|
static_cast<Segment*>(parent)->measure()->setDirty();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
// special for MEASURE, HBOX, VBOX
|
|
|
|
// their parent is not static
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type et = element->type();
|
|
|
|
if (et == Element::Type::TREMOLO)
|
2013-06-05 15:47:34 +02:00
|
|
|
setLayoutAll(true);
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (et == Element::Type::MEASURE
|
|
|
|
|| (et == Element::Type::HBOX && parent->type() != Element::Type::VBOX)
|
|
|
|
|| et == Element::Type::VBOX
|
|
|
|
|| et == Element::Type::TBOX
|
|
|
|
|| et == Element::Type::FBOX
|
2012-05-26 14:26:10 +02:00
|
|
|
) {
|
2014-07-03 15:02:36 +02:00
|
|
|
measures()->remove(static_cast<MeasureBase*>(element));
|
2014-05-30 10:16:38 +02:00
|
|
|
addLayoutFlags(LayoutFlag::FIX_TICKS);
|
2013-06-16 23:33:37 +02:00
|
|
|
setLayoutAll(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (et == Element::Type::BEAM) // beam parent does not survive layout
|
2012-05-26 14:26:10 +02:00
|
|
|
element->setParent(0);
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
parent->remove(element);
|
|
|
|
|
|
|
|
switch(et) {
|
2014-07-03 15:02:36 +02:00
|
|
|
case Element::Type::BEAM:
|
|
|
|
{
|
|
|
|
Beam* b = static_cast<Beam*>(element);
|
|
|
|
foreach(ChordRest* cr, b->elements())
|
|
|
|
cr->setBeam(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SLUR:
|
2014-05-30 10:16:38 +02:00
|
|
|
addLayoutFlags(LayoutFlag::PLAY_EVENTS);
|
2013-06-20 18:48:28 +02:00
|
|
|
// fall through
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::VOLTA:
|
|
|
|
case Element::Type::TRILL:
|
|
|
|
case Element::Type::PEDAL:
|
|
|
|
case Element::Type::TEXTLINE:
|
|
|
|
case Element::Type::HAIRPIN:
|
Lyrics multi-system melisma and dashes
Implements melisma and dash lines for lyrics spanning several systems.
The melisma and dash line is based on the `SLine` class and its segments on the `LineSegment` class. Both the whole line and its segments are not selectable, marked as generated and not saved in the score file, which is not changed in any way.
For very wide dash segments, several dashes are drawn; the distance between the dashes is not configurable.
Lyrics layout code in `Measure` class and in `layout.cpp` file has been commented out as the lyrics line layout is all contained in the lyrics.cpp file
The line is registered with the `Score` (to have its layout delayed until all elements are positioned) with a mechanism similar to other `Spanner`'s, but in a different container (`_unmanagedSpanner`), as the owning `Lyrics` should decide when create, register, unregister and delete its line.
The line segments are registered with the `System` they belong to (to have them drawn), in the same way as other `Spanner`'s.
There is code for using the dash metrics of the lyrics font, but it is turned off via a conditional directive, as there does not seem to be a reliable way to determine the dash metrics; conventional values (determined by trials and errors and based on my taste!) are used when the conditional directive is off.
2015-01-11 10:11:44 +01:00
|
|
|
// case Element::Type::LYRICSLINE:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Spanner* spanner = static_cast<Spanner*>(element);
|
2014-07-09 18:05:58 +02:00
|
|
|
if (et == Element::Type::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
|
|
|
|
break;
|
2014-07-03 15:02:36 +02:00
|
|
|
removeSpanner(spanner);
|
|
|
|
for (SpannerSegment* ss : spanner->spannerSegments()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (ss->system())
|
|
|
|
ss->system()->remove(ss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::OTTAVA:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Ottava* o = static_cast<Ottava*>(element);
|
2014-07-03 15:02:36 +02:00
|
|
|
removeSpanner(o);
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(SpannerSegment* ss, o->spannerSegments()) {
|
|
|
|
if (ss->system())
|
|
|
|
ss->system()->remove(ss);
|
|
|
|
}
|
2014-07-15 12:49:51 +02:00
|
|
|
o->staff()->updateOttava();
|
2014-05-30 10:16:38 +02:00
|
|
|
layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
|
2012-05-26 14:26:10 +02:00
|
|
|
_playlistDirty = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::DYNAMIC:
|
2014-05-30 10:16:38 +02:00
|
|
|
layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
|
2012-05-26 14:26:10 +02:00
|
|
|
_playlistDirty = true;
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CHORD:
|
|
|
|
case Element::Type::REST:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(element);
|
|
|
|
if (cr->beam())
|
|
|
|
cr->beam()->remove(cr);
|
Lyrics multi-system melisma and dashes
Implements melisma and dash lines for lyrics spanning several systems.
The melisma and dash line is based on the `SLine` class and its segments on the `LineSegment` class. Both the whole line and its segments are not selectable, marked as generated and not saved in the score file, which is not changed in any way.
For very wide dash segments, several dashes are drawn; the distance between the dashes is not configurable.
Lyrics layout code in `Measure` class and in `layout.cpp` file has been commented out as the lyrics line layout is all contained in the lyrics.cpp file
The line is registered with the `Score` (to have its layout delayed until all elements are positioned) with a mechanism similar to other `Spanner`'s, but in a different container (`_unmanagedSpanner`), as the owning `Lyrics` should decide when create, register, unregister and delete its line.
The line segments are registered with the `System` they belong to (to have them drawn), in the same way as other `Spanner`'s.
There is code for using the dash metrics of the lyrics font, but it is turned off via a conditional directive, as there does not seem to be a reliable way to determine the dash metrics; conventional values (determined by trials and errors and based on my taste!) are used when the conditional directive is off.
2015-01-11 10:11:44 +01:00
|
|
|
for (Lyrics* lyr : cr->lyricsList())
|
2015-01-16 11:51:45 +01:00
|
|
|
if (lyr) // lyrics list may be sparse
|
|
|
|
lyr->removeFromScore();
|
2014-05-21 20:08:37 +02:00
|
|
|
// TODO: check for tuplet?
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEMPO_TEXT:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
TempoText* tt = static_cast<TempoText*>(element);
|
|
|
|
int tick = tt->segment()->tick();
|
|
|
|
tempomap()->delTempo(tick);
|
|
|
|
}
|
|
|
|
break;
|
2015-03-05 16:46:37 +01:00
|
|
|
case Element::Type::INSTRUMENT_CHANGE: {
|
|
|
|
InstrumentChange* ic = static_cast<InstrumentChange*>(element);
|
|
|
|
ic->part()->removeInstrument(ic->segment()->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
rebuildMidiMapping();
|
|
|
|
_instrumentsChanged = true;
|
2015-03-05 16:46:37 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2012-11-20 20:51:18 +01:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TREMOLO:
|
|
|
|
case Element::Type::ARTICULATION:
|
|
|
|
case Element::Type::ARPEGGIO:
|
2013-01-29 18:11:51 +01:00
|
|
|
{
|
|
|
|
Element* cr = element->parent();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD)
|
2013-07-01 16:28:39 +02:00
|
|
|
createPlayEvents(static_cast<Chord*>(cr));
|
2013-01-29 18:11:51 +01:00
|
|
|
}
|
2012-11-20 20:51:18 +01:00
|
|
|
break;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
setLayoutAll(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::firstMeasure() const
|
|
|
|
{
|
|
|
|
MeasureBase* mb = _measures.first();
|
2014-06-24 18:36:02 +02:00
|
|
|
while (mb && mb->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
mb = mb->next();
|
2013-09-19 15:08:54 +02:00
|
|
|
|
|
|
|
return static_cast<Measure*>(mb);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstMeasureMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::firstMeasureMM() const
|
|
|
|
{
|
|
|
|
MeasureBase* mb = _measures.first();
|
2014-06-24 18:36:02 +02:00
|
|
|
while (mb && mb->type() != Element::Type::MEASURE)
|
2013-09-19 15:08:54 +02:00
|
|
|
mb = mb->next();
|
2013-09-20 12:59:31 +02:00
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
2014-05-26 15:31:36 +02:00
|
|
|
if (m && styleB(StyleIdx::createMultiMeasureRests) && m->hasMMRest())
|
2013-09-20 12:59:31 +02:00
|
|
|
return m->mmRest();
|
|
|
|
return m;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-09-19 15:08:54 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
MeasureBase* Score::firstMM() const
|
|
|
|
{
|
|
|
|
MeasureBase* m = _measures.first();
|
|
|
|
if (m
|
2014-06-24 18:36:02 +02:00
|
|
|
&& m->type() == Element::Type::MEASURE
|
2014-05-26 15:31:36 +02:00
|
|
|
&& styleB(StyleIdx::createMultiMeasureRests)
|
2013-09-19 15:08:54 +02:00
|
|
|
&& static_cast<Measure*>(m)->hasMMRest()) {
|
|
|
|
return static_cast<Measure*>(m)->mmRest();
|
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// measure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
MeasureBase* Score::measure(int idx) const
|
|
|
|
{
|
|
|
|
MeasureBase* mb = _measures.first();
|
|
|
|
for (int i = 0; i < idx; ++i) {
|
|
|
|
mb = mb->next();
|
|
|
|
if (mb == 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return mb;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// lastMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::lastMeasure() const
|
|
|
|
{
|
|
|
|
MeasureBase* mb = _measures.last();
|
2014-06-24 18:36:02 +02:00
|
|
|
while (mb && mb->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
mb = mb->prev();
|
|
|
|
return static_cast<Measure*>(mb);
|
|
|
|
}
|
|
|
|
|
2013-10-06 16:43:43 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// lastMeasureMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::lastMeasureMM() const
|
|
|
|
{
|
2014-08-17 12:41:44 +02:00
|
|
|
Measure* m = lastMeasure();
|
2015-01-28 04:56:28 +01:00
|
|
|
if (m && styleB(StyleIdx::createMultiMeasureRests)) {
|
|
|
|
Measure* m1 = const_cast<Measure*>(m->mmRest1());
|
|
|
|
if (m1)
|
|
|
|
return m1;
|
|
|
|
}
|
2014-08-17 12:41:44 +02:00
|
|
|
return m;
|
2013-10-06 16:43:43 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstSegment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* Score::firstSegment(Segment::Type segType) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-12-15 13:31:59 +01:00
|
|
|
Segment* seg;
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = firstMeasure();
|
2014-12-15 13:31:59 +01:00
|
|
|
if (!m)
|
|
|
|
seg = 0;
|
|
|
|
else {
|
|
|
|
seg = m->first();
|
|
|
|
if (seg && !(seg->segmentType() & segType))
|
|
|
|
seg = seg->next1(segType);
|
|
|
|
}
|
|
|
|
|
2014-07-06 01:56:30 +02:00
|
|
|
#ifdef SCRIPT_INTERFACE
|
|
|
|
// if called from QML/JS, tell QML engine not to garbage collect this object
|
|
|
|
if (seg)
|
|
|
|
QQmlEngine::setObjectOwnership(seg, QQmlEngine::CppOwnership);
|
|
|
|
#endif
|
2014-07-06 02:14:58 +02:00
|
|
|
return seg;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-10-02 10:26:09 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstSegmentMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* Score::firstSegmentMM(Segment::Type segType) const
|
2013-10-02 10:26:09 +02:00
|
|
|
{
|
|
|
|
Measure* m = firstMeasureMM();
|
|
|
|
return m ? m->first(segType) : 0;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// lastSegment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* Score::lastSegment() const
|
|
|
|
{
|
|
|
|
Measure* m = lastMeasure();
|
|
|
|
return m ? m->last() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// utick2utime
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal Score::utick2utime(int tick) const
|
|
|
|
{
|
|
|
|
return repeatList()->utick2utime(tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// utime2utick
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::utime2utick(qreal utime) const
|
|
|
|
{
|
|
|
|
return repeatList()->utime2utick(utime);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// inputPos
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::inputPos() const
|
|
|
|
{
|
|
|
|
return _is.tick();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// scanElements
|
|
|
|
// scan all elements
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::scanElements(void* data, void (*func)(void*, Element*), bool all)
|
|
|
|
{
|
2014-12-30 00:59:01 +01:00
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
|
|
|
mb->scanElements(data, func, all);
|
|
|
|
if (mb->type() == Element::Type::MEASURE) {
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
|
|
|
Measure* mmr = m->mmRest();
|
|
|
|
if (mmr)
|
|
|
|
mmr->scanElements(data, func, all);
|
|
|
|
}
|
|
|
|
}
|
2014-05-07 12:10:28 +02:00
|
|
|
for (Page* page : pages())
|
2012-05-26 14:26:10 +02:00
|
|
|
page->scanElements(data, func, all);
|
|
|
|
}
|
|
|
|
|
2014-12-04 14:40:26 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// scanElementsInRange
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-15 12:55:33 +02:00
|
|
|
void Score::scanElementsInRange(void* data, void (*func)(void*, Element*), bool all)
|
|
|
|
{
|
|
|
|
Segment* startSeg = _selection.startSegment();
|
2014-12-30 00:59:01 +01:00
|
|
|
for (Segment* s = startSeg; s && s !=_selection.endSegment(); s = s->next1()) {
|
|
|
|
s->scanElements(data, func, all);
|
|
|
|
Measure* m = s->measure();
|
|
|
|
if (m && s == m->first()) {
|
|
|
|
Measure* mmr = m->mmRest();
|
|
|
|
if (mmr)
|
|
|
|
mmr->scanElements(data, func, all);
|
|
|
|
}
|
2014-07-15 12:55:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setSelection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setSelection(const Selection& s)
|
|
|
|
{
|
|
|
|
deselectAll();
|
|
|
|
_selection = s;
|
2013-04-17 10:31:21 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(Element* e, _selection.elements())
|
|
|
|
e->setSelected(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getText
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-03 11:22:42 +02:00
|
|
|
Text* Score::getText(TextStyleType subtype)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2012-11-27 13:19:24 +01:00
|
|
|
MeasureBase* m = first();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (m && m->type() == Element::Type::VBOX) {
|
2015-02-17 20:22:24 +01:00
|
|
|
foreach(Element* e, m->el()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::TEXT && static_cast<Text*>(e)->textStyleType() == subtype)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Text*>(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// rootScore
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Score* Score::rootScore()
|
|
|
|
{
|
|
|
|
Score* score = this;
|
|
|
|
while (score->parentScore())
|
|
|
|
score = parentScore();
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Score* Score::rootScore() const
|
|
|
|
{
|
|
|
|
const Score* score = this;
|
|
|
|
while (score->parentScore())
|
|
|
|
score = parentScore();
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// undo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
UndoStack* Score::undo() const
|
|
|
|
{
|
|
|
|
return rootScore()->_undo;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// repeatList
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
RepeatList* Score::repeatList() const
|
|
|
|
{
|
|
|
|
return rootScore()->_repeatList;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// links
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-05-16 12:48:23 +02:00
|
|
|
QMap<int, LinkedElements*>& Score::links()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
return rootScore()->_elinks;
|
|
|
|
}
|
|
|
|
|
2013-10-07 12:09:11 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTempomap
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setTempomap(TempoMap* tm)
|
|
|
|
{
|
|
|
|
delete _tempomap;
|
|
|
|
_tempomap = tm;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tempomap
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
TempoMap* Score::tempomap() const
|
|
|
|
{
|
|
|
|
return rootScore()->_tempomap;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// sigmap
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
TimeSigMap* Score::sigmap() const
|
|
|
|
{
|
|
|
|
return rootScore()->_sigmap;
|
|
|
|
}
|
|
|
|
|
2013-07-19 10:39:32 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// metaTag
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-22 19:49:51 +02:00
|
|
|
QString Score::metaTag(const QString& s) const
|
2013-07-19 10:39:32 +02:00
|
|
|
{
|
2013-07-19 18:03:35 +02:00
|
|
|
if (_metaTags.contains(s))
|
|
|
|
return _metaTags.value(s);
|
2013-07-19 10:39:32 +02:00
|
|
|
return rootScore()->_metaTags.value(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setMetaTag
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-22 19:49:51 +02:00
|
|
|
void Score::setMetaTag(const QString& tag, const QString& val)
|
2013-07-19 10:39:32 +02:00
|
|
|
{
|
2013-07-19 18:03:35 +02:00
|
|
|
_metaTags.insert(tag, val);
|
2013-07-19 10:39:32 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addExcerpt
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addExcerpt(Score* score)
|
|
|
|
{
|
2014-11-27 17:22:18 +01:00
|
|
|
Excerpt* ex = new Excerpt(this);
|
|
|
|
ex->setPartScore(score);
|
2012-05-26 14:26:10 +02:00
|
|
|
excerpts().append(ex);
|
|
|
|
ex->setTitle(score->name());
|
|
|
|
foreach(Staff* s, score->staves()) {
|
|
|
|
LinkedStaves* ls = s->linkedStaves();
|
|
|
|
if (ls == 0)
|
|
|
|
continue;
|
|
|
|
foreach(Staff* ps, ls->staves()) {
|
|
|
|
if (ps->score() == this) {
|
|
|
|
ex->parts().append(ps->part());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setExcerptsChanged(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeExcerpt
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeExcerpt(Score* score)
|
|
|
|
{
|
|
|
|
foreach (Excerpt* ex, excerpts()) {
|
2014-11-27 14:50:02 +01:00
|
|
|
if (ex->partScore() == score) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (excerpts().removeOne(ex)) {
|
|
|
|
delete ex;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("removeExcerpt:: ex not found");
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("Score::removeExcerpt: excerpt not found");
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// clone
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Score* Score::clone()
|
|
|
|
{
|
|
|
|
QBuffer buffer;
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
Xml xml(&buffer);
|
|
|
|
xml.header();
|
|
|
|
|
|
|
|
xml.stag("museScore version=\"" MSC_VERSION "\"");
|
|
|
|
write(xml, false);
|
|
|
|
xml.etag();
|
|
|
|
|
|
|
|
buffer.close();
|
|
|
|
|
2013-01-11 18:10:18 +01:00
|
|
|
XmlReader r(buffer.buffer());
|
2012-05-26 14:26:10 +02:00
|
|
|
Score* score = new Score(style());
|
2013-01-11 18:10:18 +01:00
|
|
|
score->read1(r, true);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-30 10:16:38 +02:00
|
|
|
score->addLayoutFlags(LayoutFlag::FIX_TICKS | LayoutFlag::FIX_PITCH_VELO);
|
2012-05-26 14:26:10 +02:00
|
|
|
score->doLayout();
|
|
|
|
score->scanElements(0, elementAdjustReadPos); //??
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2013-04-02 20:46:07 +02:00
|
|
|
// setSynthesizerState
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-02 20:46:07 +02:00
|
|
|
void Score::setSynthesizerState(const SynthesizerState& s)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
// TODO: make undoable
|
|
|
|
_synthesizerState = s;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setLayoutAll
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setLayoutAll(bool val)
|
|
|
|
{
|
|
|
|
foreach(Score* score, scoreList())
|
|
|
|
score->_layoutAll = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeOmr
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeOmr()
|
|
|
|
{
|
|
|
|
_showOmr = false;
|
2012-07-06 17:42:20 +02:00
|
|
|
#ifdef OMR
|
2012-05-26 14:26:10 +02:00
|
|
|
delete _omr;
|
|
|
|
_omr = 0;
|
2012-07-06 17:42:20 +02:00
|
|
|
#endif
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeAudio
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeAudio()
|
|
|
|
{
|
|
|
|
delete _audio;
|
|
|
|
_audio = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendScore
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::appendScore(Score* score)
|
|
|
|
{
|
2014-12-30 08:31:07 +01:00
|
|
|
if (parts().size() < score->parts().size() || staves().size() < score->staves().size())
|
2014-11-28 14:49:46 +01:00
|
|
|
return false;
|
2012-05-26 14:26:10 +02:00
|
|
|
TieMap tieMap;
|
|
|
|
|
2014-11-28 14:49:46 +01:00
|
|
|
MeasureBase* lastMeasure = last();
|
2014-12-16 13:30:36 +01:00
|
|
|
if (!lastMeasure)
|
|
|
|
return false;
|
2014-11-28 14:49:46 +01:00
|
|
|
int tickLen = lastMeasure->endTick();
|
|
|
|
|
2014-11-29 17:45:51 +01:00
|
|
|
if (!lastMeasure->lineBreak() && !lastMeasure->pageBreak()) {
|
2014-11-28 14:49:46 +01:00
|
|
|
lastMeasure->undoSetBreak(true, LayoutBreak::Type::LINE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lastMeasure->sectionBreak()) {
|
|
|
|
lastMeasure->undoSetBreak(true, LayoutBreak::Type::SECTION);
|
|
|
|
}
|
|
|
|
|
2014-12-23 00:18:59 +01:00
|
|
|
// match concert pitch states
|
|
|
|
if (styleB(StyleIdx::concertPitch) != score->styleB(StyleIdx::concertPitch))
|
|
|
|
score->cmdConcertPitchChanged(styleB(StyleIdx::concertPitch), true);
|
|
|
|
|
2014-11-28 14:49:46 +01:00
|
|
|
// clone the measures
|
2012-05-26 14:26:10 +02:00
|
|
|
MeasureBaseList* ml = &score->_measures;
|
|
|
|
for (MeasureBase* mb = ml->first(); mb; mb = mb->next()) {
|
|
|
|
MeasureBase* nmb;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::MEASURE)
|
2013-07-05 11:23:52 +02:00
|
|
|
nmb = static_cast<Measure*>(mb)->cloneMeasure(this, &tieMap);
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
|
|
|
nmb = mb->clone();
|
|
|
|
nmb->setNext(0);
|
|
|
|
nmb->setPrev(0);
|
|
|
|
nmb->setScore(this);
|
|
|
|
_measures.add(nmb);
|
|
|
|
}
|
|
|
|
fixTicks();
|
2014-11-28 14:49:46 +01:00
|
|
|
|
2014-12-23 00:18:59 +01:00
|
|
|
// adjust key signatures
|
2014-11-28 14:49:46 +01:00
|
|
|
for (Staff* st : score->staves()) {
|
2014-12-23 00:18:59 +01:00
|
|
|
int staffIdx = score->staffIdx(st);
|
2014-12-27 23:25:39 +01:00
|
|
|
Staff* joinedStaff = staff(staffIdx);
|
2014-12-23 00:18:59 +01:00
|
|
|
// special case for initial "C" key signature - these have no explicit element
|
|
|
|
Measure* m = tick2measure(tickLen);
|
|
|
|
Segment* seg = m->getSegment(Segment::Type::KeySig, tickLen);
|
|
|
|
if (!seg->element(staffIdx * VOICES)) {
|
2014-12-27 23:25:39 +01:00
|
|
|
// no need to create new initial "C" key sig
|
|
|
|
// if staff already ends in that key
|
|
|
|
if (joinedStaff->key(tickLen - 1) == Key::C)
|
|
|
|
continue;
|
2014-12-23 00:18:59 +01:00
|
|
|
Key key = Key::C;
|
|
|
|
KeySig* ks = new KeySig(this);
|
|
|
|
ks->setTrack(staffIdx * VOICES);
|
|
|
|
ks->setKey(key);
|
|
|
|
ks->setParent(seg);
|
|
|
|
addElement(ks);
|
|
|
|
}
|
|
|
|
// other key signatures (initial other than "C", non-initial)
|
2015-03-09 18:31:28 +01:00
|
|
|
for (auto k : *(st->keyList())) {
|
2014-11-28 14:49:46 +01:00
|
|
|
int tick = k.first;
|
2014-12-08 18:02:17 +01:00
|
|
|
KeySigEvent key = k.second;
|
2014-12-27 23:25:39 +01:00
|
|
|
joinedStaff->setKey(tick + tickLen, key);
|
2014-11-28 14:49:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// clone the spanners
|
|
|
|
for (auto sp : score->spanner()) {
|
|
|
|
Spanner* spanner = sp.second;
|
|
|
|
Spanner* ns = static_cast<Spanner*>(spanner->clone());
|
|
|
|
ns->setScore(this);
|
|
|
|
ns->setParent(0);
|
|
|
|
ns->setTick(spanner->tick() + tickLen);
|
|
|
|
ns->setTick2(spanner->tick2() + tickLen);
|
|
|
|
if (ns->type() == Element::Type::SLUR) {
|
|
|
|
// set start/end element for slur
|
|
|
|
ns->setStartElement(0);
|
|
|
|
ns->setEndElement(0);
|
|
|
|
Measure* sm = tick2measure(ns->tick());
|
|
|
|
if (sm)
|
|
|
|
ns->setStartElement(sm->findChordRest(ns->tick(), ns->track()));
|
|
|
|
Measure * em = tick2measure(ns->tick2());
|
|
|
|
if (em)
|
|
|
|
ns->setEndElement(em->findChordRest(ns->tick2(), ns->track2()));
|
|
|
|
if (!ns->startElement())
|
|
|
|
qDebug("clone Slur: no start element");
|
|
|
|
if (!ns->endElement())
|
|
|
|
qDebug("clone Slur: no end element");
|
|
|
|
}
|
|
|
|
addElement(ns);
|
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
setLayoutAll(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// splitStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::splitStaff(int staffIdx, int splitPoint)
|
|
|
|
{
|
2013-01-22 20:37:52 +01:00
|
|
|
qDebug("split staff %d point %d", staffIdx, splitPoint);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// create second staff
|
|
|
|
//
|
|
|
|
Staff* s = staff(staffIdx);
|
|
|
|
Part* p = s->part();
|
2014-08-16 13:32:08 +02:00
|
|
|
Staff* ns = new Staff(this);
|
|
|
|
ns->setPart(p);
|
2015-01-26 23:37:10 +01:00
|
|
|
// convert staffIdx from score-relative to part-relative
|
|
|
|
int staffIdxPart = staffIdx - p->staff(0)->idx();
|
|
|
|
undoInsertStaff(ns, staffIdxPart + 1, false);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
Clef* clef = new Clef(this);
|
2013-09-05 16:37:49 +02:00
|
|
|
clef->setClefType(ClefType::F);
|
2012-05-26 14:26:10 +02:00
|
|
|
clef->setTrack((staffIdx+1) * VOICES);
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* seg = firstMeasure()->getSegment(Segment::Type::Clef, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
clef->setParent(seg);
|
|
|
|
undoAddElement(clef);
|
|
|
|
|
2014-12-08 18:02:17 +01:00
|
|
|
undoChangeKeySig(ns, 0, s->keySigEvent(0));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
rebuildMidiMapping();
|
|
|
|
_instrumentsChanged = true;
|
|
|
|
doLayout();
|
|
|
|
|
|
|
|
//
|
|
|
|
// move notes
|
|
|
|
//
|
2014-05-27 10:34:08 +02:00
|
|
|
select(0, SelectType::SINGLE, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
int strack = staffIdx * VOICES;
|
|
|
|
int dtrack = (staffIdx + 1) * VOICES;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = firstSegment(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
2014-08-16 16:10:47 +02:00
|
|
|
Chord* c = static_cast<Chord*>(s->element(strack + voice));
|
|
|
|
if (c == 0 || c->type() != Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
QList<Note*> removeNotes;
|
|
|
|
foreach(Note* note, c->notes()) {
|
|
|
|
if (note->pitch() >= splitPoint)
|
|
|
|
continue;
|
|
|
|
Chord* chord = static_cast<Chord*>(s->element(dtrack + voice));
|
2014-06-24 18:36:02 +02:00
|
|
|
Q_ASSERT(!chord || (chord->type() == Element::Type::CHORD));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (chord == 0) {
|
|
|
|
chord = new Chord(*c);
|
2014-08-16 16:10:47 +02:00
|
|
|
qDeleteAll(chord->notes());
|
2012-05-26 14:26:10 +02:00
|
|
|
chord->notes().clear();
|
|
|
|
chord->setTrack(dtrack + voice);
|
|
|
|
undoAddElement(chord);
|
|
|
|
}
|
|
|
|
Note* nnote = new Note(*note);
|
|
|
|
nnote->setTrack(dtrack + voice);
|
|
|
|
chord->add(nnote);
|
2013-03-21 16:27:10 +01:00
|
|
|
nnote->updateLine();
|
2012-05-26 14:26:10 +02:00
|
|
|
removeNotes.append(note);
|
|
|
|
}
|
2014-04-09 16:09:21 +02:00
|
|
|
c->sortNotes();
|
2015-04-10 11:51:15 +02:00
|
|
|
for (Note* note : removeNotes) {
|
2012-05-26 14:26:10 +02:00
|
|
|
undoRemoveElement(note);
|
2015-04-10 11:51:15 +02:00
|
|
|
Chord* chord = note->chord();
|
|
|
|
if (chord->notes().isEmpty()) {
|
|
|
|
undoRemoveElement(chord);
|
|
|
|
for (auto sp : spanner()) {
|
|
|
|
Slur* slur = static_cast<Slur*>(sp.second);
|
|
|
|
if (slur->type() != Element::Type::SLUR)
|
|
|
|
continue;
|
|
|
|
if (slur->startCR() == chord) {
|
|
|
|
slur->undoChangeProperty(P_ID::TRACK, slur->track()+VOICES);
|
2015-04-13 21:03:29 +02:00
|
|
|
for (ScoreElement* ee : slur->linkList()) {
|
|
|
|
Slur* lslur = static_cast<Slur*>(ee);
|
|
|
|
lslur->setStartElement(0);
|
|
|
|
}
|
2015-04-10 11:51:15 +02:00
|
|
|
}
|
|
|
|
if (slur->endCR() == chord) {
|
|
|
|
slur->undoChangeProperty(P_ID::SPANNER_TRACK2, slur->track2()+VOICES);
|
2015-04-13 21:03:29 +02:00
|
|
|
for (ScoreElement* ee : slur->linkList()) {
|
|
|
|
Slur* lslur = static_cast<Slur*>(ee);
|
|
|
|
lslur->setEndElement(0);
|
|
|
|
}
|
2015-04-10 11:51:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// make sure that the timeline for dtrack
|
|
|
|
// has no gaps
|
|
|
|
//
|
|
|
|
int ctick = 0;
|
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(dtrack));
|
|
|
|
if (cr == 0)
|
|
|
|
continue;
|
|
|
|
int rest = s->tick() - ctick;
|
|
|
|
if (rest) {
|
|
|
|
// insert Rest
|
|
|
|
Segment* s = tick2segment(ctick);
|
|
|
|
if (s == 0) {
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("no segment at %d", ctick);
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0);
|
|
|
|
}
|
|
|
|
ctick = s->tick() + cr->actualTicks();
|
|
|
|
}
|
|
|
|
int rest = m->tick() + m->ticks() - ctick;
|
|
|
|
if (rest) {
|
|
|
|
setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0);
|
|
|
|
ctick += rest;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// same for strack
|
|
|
|
//
|
|
|
|
ctick = 0;
|
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(strack));
|
|
|
|
if (cr == 0)
|
|
|
|
continue;
|
|
|
|
int rest = s->tick() - ctick;
|
|
|
|
if (rest) {
|
|
|
|
// insert Rest
|
|
|
|
Segment* s = tick2segment(ctick);
|
|
|
|
if (s == 0) {
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("no segment at %d", ctick);
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
setRest(ctick, strack, Fraction::fromTicks(rest), false, 0);
|
|
|
|
}
|
|
|
|
ctick = s->tick() + cr->actualTicks();
|
|
|
|
}
|
|
|
|
int rest = m->tick() + m->ticks() - ctick;
|
|
|
|
if (rest) {
|
|
|
|
setRest(ctick, strack, Fraction::fromTicks(rest), false, 0);
|
|
|
|
ctick += rest;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdRemovePart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdRemovePart(Part* part)
|
|
|
|
{
|
|
|
|
int sidx = staffIdx(part);
|
|
|
|
int n = part->nstaves();
|
2014-08-16 14:18:06 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
cmdRemoveStaff(sidx);
|
2013-09-20 12:59:31 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
undoRemovePart(part, sidx);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// insertPart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::insertPart(Part* part, int idx)
|
|
|
|
{
|
|
|
|
int staff = 0;
|
|
|
|
for (QList<Part*>::iterator i = _parts.begin(); i != _parts.end(); ++i) {
|
|
|
|
if (staff >= idx) {
|
|
|
|
_parts.insert(i, part);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
staff += (*i)->nstaves();
|
|
|
|
}
|
|
|
|
_parts.push_back(part);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removePart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removePart(Part* part)
|
|
|
|
{
|
|
|
|
_parts.removeAt(_parts.indexOf(part));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// insertStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-08-11 15:25:55 +02:00
|
|
|
void Score::insertStaff(Staff* staff, int ridx)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-08-11 15:25:55 +02:00
|
|
|
staff->part()->insertStaff(staff, ridx);
|
2014-08-16 13:32:08 +02:00
|
|
|
|
2014-08-16 14:18:06 +02:00
|
|
|
int idx = staffIdx(staff->part()) + ridx;
|
|
|
|
_staves.insert(idx, staff);
|
2014-08-16 13:32:08 +02:00
|
|
|
|
2013-10-11 13:58:18 +02:00
|
|
|
for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
|
|
|
|
Spanner* s = i->second;
|
2014-12-20 20:56:40 +01:00
|
|
|
if (s->systemFlag())
|
|
|
|
continue;
|
2013-10-11 13:58:18 +02:00
|
|
|
if (s->staffIdx() >= idx) {
|
|
|
|
int t = s->track() + VOICES;
|
|
|
|
s->setTrack(t < ntracks() ? t : ntracks() - 1);
|
|
|
|
if (s->track2() != -1) {
|
|
|
|
t = s->track2() + VOICES;
|
|
|
|
s->setTrack2(t < ntracks() ? t : s->track());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-08-16 14:18:06 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeStaff(Staff* staff)
|
|
|
|
{
|
|
|
|
int idx = staffIdx(staff);
|
|
|
|
for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
|
|
|
|
Spanner* s = i->second;
|
|
|
|
if (s->staffIdx() > idx) {
|
|
|
|
int t = s->track() - VOICES;
|
|
|
|
s->setTrack(t >=0 ? t : 0);
|
|
|
|
if (s->track2() != -1) {
|
|
|
|
t = s->track2() - VOICES;
|
|
|
|
s->setTrack2(t >=0 ? t : s->track());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_staves.removeAll(staff);
|
|
|
|
staff->part()->removeStaff(staff);
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// adjustBracketsDel
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::adjustBracketsDel(int sidx, int eidx)
|
|
|
|
{
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
Staff* staff = _staves[staffIdx];
|
|
|
|
for (int i = 0; i < staff->bracketLevels(); ++i) {
|
|
|
|
int span = staff->bracketSpan(i);
|
|
|
|
if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
|
|
|
|
continue;
|
|
|
|
if ((sidx >= staffIdx) && (eidx <= (staffIdx + span)))
|
|
|
|
undoChangeBracketSpan(staff, i, span - (eidx-sidx));
|
|
|
|
}
|
|
|
|
int span = staff->barLineSpan();
|
2012-10-14 00:35:11 +02:00
|
|
|
if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) {
|
2012-11-05 10:12:57 +01:00
|
|
|
int newSpan = span - (eidx-sidx) + 1;
|
2012-10-14 00:35:11 +02:00
|
|
|
int lastSpannedStaffIdx = staffIdx + newSpan - 1;
|
2012-11-05 10:12:57 +01:00
|
|
|
undoChangeBarLineSpan(staff, newSpan, 0, (_staves[lastSpannedStaffIdx]->lines()-1)*2);
|
2012-10-14 00:35:11 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// adjustBracketsIns
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::adjustBracketsIns(int sidx, int eidx)
|
|
|
|
{
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
Staff* staff = _staves[staffIdx];
|
|
|
|
int bl = staff->bracketLevels();
|
|
|
|
for (int i = 0; i < bl; ++i) {
|
|
|
|
int span = staff->bracketSpan(i);
|
|
|
|
if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
|
|
|
|
continue;
|
|
|
|
if ((sidx >= staffIdx) && (eidx < (staffIdx + span)))
|
|
|
|
undoChangeBracketSpan(staff, i, span + (eidx-sidx));
|
|
|
|
}
|
|
|
|
int span = staff->barLineSpan();
|
2014-08-16 17:54:10 +02:00
|
|
|
if ((sidx >= staffIdx) && (eidx < (staffIdx + span))) {
|
|
|
|
int idx = staffIdx + span - 1;
|
|
|
|
if (idx >= _staves.size())
|
|
|
|
idx = _staves.size() - 1;
|
|
|
|
undoChangeBarLineSpan(staff, span, 0, (_staves[idx]->lines()-1)*2);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-11 16:13:43 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// adjustKeySigs
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::adjustKeySigs(int sidx, int eidx, KeyList km)
|
|
|
|
{
|
|
|
|
for (int staffIdx = sidx; staffIdx < eidx; ++staffIdx) {
|
|
|
|
Staff* staff = _staves[staffIdx];
|
2014-06-05 11:37:21 +02:00
|
|
|
if (staff->isDrumStaff())
|
|
|
|
continue;
|
|
|
|
for (auto i = km.begin(); i != km.end(); ++i) {
|
|
|
|
int tick = i->first;
|
|
|
|
Measure* measure = tick2measure(tick);
|
2014-11-10 11:42:00 +01:00
|
|
|
if (!measure)
|
|
|
|
continue;
|
2014-12-08 18:02:17 +01:00
|
|
|
KeySigEvent oKey = i->second;
|
|
|
|
KeySigEvent nKey = oKey;
|
2015-03-13 11:16:43 +01:00
|
|
|
int diff = -staff->part()->instrument()->transpose().chromatic;
|
2014-12-08 18:02:17 +01:00
|
|
|
if (diff != 0 && !styleB(StyleIdx::concertPitch) && !oKey.custom())
|
|
|
|
nKey.setKey(transposeKey(nKey.key(), diff));
|
2014-06-05 11:37:21 +02:00
|
|
|
staff->setKey(tick, nKey);
|
|
|
|
KeySig* keysig = new KeySig(this);
|
|
|
|
keysig->setTrack(staffIdx * VOICES);
|
2014-12-08 18:02:17 +01:00
|
|
|
keysig->setKeySigEvent(nKey);
|
2014-06-05 11:37:21 +02:00
|
|
|
Segment* s = measure->getSegment(keysig, tick);
|
|
|
|
s->add(keysig);
|
2013-09-11 16:13:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdRemoveStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdRemoveStaff(int staffIdx)
|
|
|
|
{
|
|
|
|
Staff* s = staff(staffIdx);
|
2013-09-16 21:57:54 +02:00
|
|
|
adjustBracketsDel(staffIdx, staffIdx+1);
|
2013-09-19 18:24:58 +02:00
|
|
|
|
|
|
|
QList<Spanner*> sl;
|
|
|
|
for (auto i = _spanner.cbegin(); i != _spanner.cend(); ++i) {
|
|
|
|
Spanner* s = i->second;
|
2014-12-20 20:56:40 +01:00
|
|
|
if (s->staffIdx() == staffIdx && (staffIdx != 0 || !s->systemFlag()))
|
2013-09-19 18:24:58 +02:00
|
|
|
sl.append(s);
|
|
|
|
}
|
2014-08-07 11:33:05 +02:00
|
|
|
for (auto i : sl) {
|
|
|
|
i->undoUnlink();
|
|
|
|
undo(new RemoveElement(i));
|
|
|
|
}
|
2013-09-19 18:24:58 +02:00
|
|
|
|
2014-08-11 15:25:55 +02:00
|
|
|
undoRemoveStaff(s);
|
2013-09-16 21:57:54 +02:00
|
|
|
|
2013-09-18 16:35:08 +02:00
|
|
|
// remove linked staff and measures in linked staves in excerps
|
2013-09-16 21:57:54 +02:00
|
|
|
// should be done earlier for the main staff
|
2014-08-07 19:39:18 +02:00
|
|
|
Staff* s2 = 0;
|
2013-09-19 18:24:58 +02:00
|
|
|
if (s->linkedStaves()) {
|
2014-08-05 16:09:14 +02:00
|
|
|
for (Staff* staff : s->linkedStaves()->staves()) {
|
2014-08-07 19:39:18 +02:00
|
|
|
if (staff != s)
|
|
|
|
s2 = staff;
|
2013-09-17 13:11:23 +02:00
|
|
|
Score* lscore = staff->score();
|
2014-08-05 16:09:14 +02:00
|
|
|
if (lscore != this) {
|
2014-08-11 15:25:55 +02:00
|
|
|
undoRemoveStaff(staff);
|
2014-08-25 19:30:56 +02:00
|
|
|
if (staff->part()->nstaves() == 0) {
|
|
|
|
int pIndex = lscore->staffIdx(staff->part());
|
2013-09-18 16:35:08 +02:00
|
|
|
undoRemovePart(staff->part(), pIndex);
|
2014-08-25 19:30:56 +02:00
|
|
|
}
|
2013-09-17 13:11:23 +02:00
|
|
|
}
|
2013-09-16 21:57:54 +02:00
|
|
|
}
|
2014-08-07 19:39:18 +02:00
|
|
|
s->score()->undo(new UnlinkStaff(s2, s));
|
2013-09-16 21:57:54 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// sortStaves
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::sortStaves(QList<int>& dst)
|
|
|
|
{
|
|
|
|
systems()->clear(); //??
|
|
|
|
_parts.clear();
|
|
|
|
Part* curPart = 0;
|
|
|
|
QList<Staff*> dl;
|
2013-09-20 12:59:31 +02:00
|
|
|
foreach (int idx, dst) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Staff* staff = _staves[idx];
|
|
|
|
if (staff->part() != curPart) {
|
|
|
|
curPart = staff->part();
|
|
|
|
curPart->staves()->clear();
|
|
|
|
_parts.push_back(curPart);
|
|
|
|
}
|
|
|
|
curPart->staves()->push_back(staff);
|
|
|
|
dl.push_back(staff);
|
|
|
|
}
|
|
|
|
_staves = dl;
|
|
|
|
|
2013-09-20 12:59:31 +02:00
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
m->sortStaves(dst);
|
2013-09-20 12:59:31 +02:00
|
|
|
if (m->hasMMRest())
|
|
|
|
m->mmRest()->sortStaves(dst);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-11 10:46:48 +02:00
|
|
|
for (auto i : _spanner.map()) {
|
|
|
|
Spanner* sp = i.second;
|
2014-12-20 20:56:40 +01:00
|
|
|
if (sp->systemFlag())
|
|
|
|
continue;
|
2013-10-11 10:46:48 +02:00
|
|
|
int voice = sp->voice();
|
|
|
|
int staffIdx = sp->staffIdx();
|
|
|
|
int idx = dst.indexOf(staffIdx);
|
|
|
|
if (idx >=0) {
|
|
|
|
sp->setTrack(idx * VOICES + voice);
|
|
|
|
if (sp->track2() != -1)
|
|
|
|
sp->setTrack2(idx * VOICES +(sp->track2() % VOICES)); // at least keep the voice...
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdConcertPitchChanged
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-09 14:13:22 +02:00
|
|
|
void Score::cmdConcertPitchChanged(bool flag, bool /*useDoubleSharpsFlats*/)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-04-02 18:11:56 +02:00
|
|
|
undo(new ChangeConcertPitch(this, flag)); // change style flag
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-04-02 18:11:56 +02:00
|
|
|
for (Staff* staff : _staves) {
|
2014-05-27 13:30:23 +02:00
|
|
|
if (staff->staffType()->group() == StaffGroup::PERCUSSION)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = staff->part()->instrument()->transpose();
|
2012-05-26 14:26:10 +02:00
|
|
|
if (interval.isZero())
|
|
|
|
continue;
|
|
|
|
if (!flag)
|
|
|
|
interval.flip();
|
2014-04-02 18:11:56 +02:00
|
|
|
|
2014-04-09 09:40:25 +02:00
|
|
|
int staffIdx = staff->idx();
|
2014-04-02 18:11:56 +02:00
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
|
|
|
|
|
|
|
transposeKeys(staffIdx, staffIdx+1, 0, lastSegment()->tick(), interval);
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* segment = firstSegment(Segment::Type::ChordRest); segment; segment = segment->next1(Segment::Type::ChordRest)) {
|
2014-04-02 18:11:56 +02:00
|
|
|
for (Element* e : segment->annotations()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if ((e->type() != Element::Type::HARMONY) || (e->track() < startTrack) || (e->track() >= endTrack))
|
2014-04-02 18:11:56 +02:00
|
|
|
continue;
|
|
|
|
Harmony* h = static_cast<Harmony*>(e);
|
2014-12-20 00:27:06 +01:00
|
|
|
int rootTpc = transposeTpc(h->rootTpc(), interval, true);
|
|
|
|
int baseTpc = transposeTpc(h->baseTpc(), interval, true);
|
2015-04-08 05:28:28 +02:00
|
|
|
for (ScoreElement* e : h->linkList()) {
|
|
|
|
// don't transpose all links
|
|
|
|
// just ones resulting from mmrests
|
|
|
|
Harmony* he = static_cast<Harmony*>(e);
|
|
|
|
if (he->staff() == h->staff())
|
|
|
|
undoTransposeHarmony(he, rootTpc, baseTpc);
|
|
|
|
}
|
2014-04-02 18:11:56 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addAudioTrack
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addAudioTrack()
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// padToggle
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-30 10:16:58 +02:00
|
|
|
void Score::padToggle(Pad n)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
switch (n) {
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE00:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_LONG);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE0:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_BREVE);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE1:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_WHOLE);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE2:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_HALF);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE4:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_QUARTER);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE8:
|
2014-08-15 18:32:48 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_EIGHTH);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE16:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_16TH);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE32:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_32ND);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE64:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_64TH);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::NOTE128:
|
2014-05-21 15:43:19 +02:00
|
|
|
_is.setDuration(TDuration::DurationType::V_128TH);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::REST:
|
2013-10-24 12:09:00 +02:00
|
|
|
_is.setRest(!_is.rest());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::DOT:
|
2015-03-11 23:18:01 +01:00
|
|
|
if (_is.duration().dots() == 1)
|
2012-05-26 14:26:10 +02:00
|
|
|
_is.setDots(0);
|
|
|
|
else
|
|
|
|
_is.setDots(1);
|
|
|
|
break;
|
2014-05-30 10:16:58 +02:00
|
|
|
case Pad::DOTDOT:
|
2015-03-11 23:18:01 +01:00
|
|
|
if (_is.duration().dots() == 2)
|
2012-05-26 14:26:10 +02:00
|
|
|
_is.setDots(0);
|
|
|
|
else
|
|
|
|
_is.setDots(2);
|
|
|
|
break;
|
|
|
|
}
|
2014-05-30 10:16:58 +02:00
|
|
|
if (n >= Pad::NOTE00 && n <= Pad::NOTE128) {
|
2012-05-26 14:26:10 +02:00
|
|
|
_is.setDots(0);
|
|
|
|
//
|
|
|
|
// if in "note enter" mode, reset
|
|
|
|
// rest flag
|
|
|
|
//
|
|
|
|
if (noteEntryMode())
|
2013-10-24 12:09:00 +02:00
|
|
|
_is.setRest(false);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (noteEntryMode() || !selection().isSingle()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//do not allow to add a dot on a full measure rest
|
|
|
|
Element* e = selection().element();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && e->type() == Element::Type::REST) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Rest* r = static_cast<Rest*>(e);
|
|
|
|
TDuration d = r->durationType();
|
2014-05-21 15:43:19 +02:00
|
|
|
if (d.type() == TDuration::DurationType::V_MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
_is.setDots(0);
|
|
|
|
// return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-22 13:05:23 +01:00
|
|
|
ChordRest* cr = selection().cr();
|
|
|
|
if (!cr)
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD && (static_cast<Chord*>(cr)->noteType() != NoteType::NORMAL)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// handle appoggiatura and acciaccatura
|
|
|
|
//
|
2014-11-29 08:27:07 +01:00
|
|
|
undoChangeChordRestLen(cr, _is.duration());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
changeCRlen(cr, _is.duration());
|
2014-11-22 13:05:23 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// deselect
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::deselect(Element* el)
|
|
|
|
{
|
|
|
|
refresh |= el->abbox();
|
|
|
|
_selection.remove(el);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// select
|
|
|
|
// staffIdx is valid, if element is of type MEASURE
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::select(Element* e, SelectType type, int staffIdx)
|
|
|
|
{
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && (e->type() == Element::Type::NOTE || e->type() == Element::Type::REST)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Element* ee = e;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (ee->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
ee = ee->parent();
|
2014-11-17 16:44:05 +01:00
|
|
|
int tick = static_cast<ChordRest*>(ee)->segment()->tick();
|
|
|
|
if (playPos() != tick)
|
|
|
|
setPlayPos(tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
if (MScore::debugMode)
|
2014-06-17 14:20:24 +02:00
|
|
|
qDebug("select element <%s> type %hhd(state %hhd) staff %d",
|
2012-05-26 14:26:10 +02:00
|
|
|
e ? e->name() : "", type, selection().state(), e ? e->staffIdx() : -1);
|
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
switch (type) {
|
|
|
|
case SelectType::SINGLE: return selectSingle(e, staffIdx);
|
|
|
|
case SelectType::ADD: return selectAdd(e);
|
|
|
|
case SelectType::RANGE: return selectRange(e, staffIdx);
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectSingle
|
|
|
|
// staffIdx is valid, if element is of type MEASURE
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::selectSingle(Element* e, int staffIdx)
|
|
|
|
{
|
|
|
|
SelState selState = _selection.state();
|
|
|
|
deselectAll();
|
|
|
|
if (e == 0) {
|
|
|
|
selState = SelState::NONE;
|
|
|
|
_updateAll = true;
|
|
|
|
}
|
|
|
|
else {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::MEASURE) {
|
2014-05-24 22:24:48 +02:00
|
|
|
select(e, SelectType::RANGE, staffIdx);
|
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
refresh |= e->abbox();
|
|
|
|
_selection.add(e);
|
|
|
|
_is.setTrack(e->track());
|
|
|
|
selState = SelState::LIST;
|
2014-07-10 14:13:37 +02:00
|
|
|
if (e->type() == Element::Type::NOTE) {
|
2014-05-24 22:24:48 +02:00
|
|
|
e = e->parent();
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::REST || e->type() == Element::Type::CHORD) {
|
2014-05-24 22:24:48 +02:00
|
|
|
_is.setLastSegment(_is.segment());
|
|
|
|
_is.setSegment(static_cast<ChordRest*>(e)->segment());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
_selection.setActiveSegment(0);
|
|
|
|
_selection.setActiveTrack(0);
|
|
|
|
|
|
|
|
_selection.setState(selState);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectAdd
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::selectAdd(Element* e)
|
|
|
|
{
|
|
|
|
SelState selState = _selection.state();
|
|
|
|
|
|
|
|
if (_selection.isRange()) {
|
|
|
|
select(0, SelectType::SINGLE, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::MEASURE) {
|
2014-05-24 22:24:48 +02:00
|
|
|
Measure* m = static_cast<Measure*>(e);
|
|
|
|
int tick = m->tick();
|
|
|
|
if (_selection.isNone()) {
|
2014-05-31 19:30:17 +02:00
|
|
|
_selection.setRange(m->tick2segment(tick),
|
|
|
|
m == lastMeasure() ? 0 : m->last(),
|
|
|
|
0,
|
|
|
|
nstaves());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-05-24 22:24:48 +02:00
|
|
|
select(0, SelectType::SINGLE, 0);
|
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
_updateAll = true;
|
|
|
|
selState = SelState::RANGE;
|
|
|
|
_selection.updateSelectedElements();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
else { // None or List
|
|
|
|
refresh |= e->abbox();
|
|
|
|
if (_selection.elements().contains(e))
|
|
|
|
_selection.remove(e);
|
|
|
|
else {
|
|
|
|
_selection.add(e);
|
|
|
|
selState = SelState::LIST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_selection.setState(selState);
|
|
|
|
}
|
2014-05-24 12:53:50 +02:00
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectRange
|
|
|
|
// staffIdx is valid, if element is of type MEASURE
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::selectRange(Element* e, int staffIdx)
|
|
|
|
{
|
|
|
|
int activeTrack = e->track();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::MEASURE) {
|
2014-05-24 22:24:48 +02:00
|
|
|
Measure* m = static_cast<Measure*>(e);
|
|
|
|
int tick = m->tick();
|
|
|
|
int etick = tick + m->ticks();
|
|
|
|
activeTrack = staffIdx * VOICES;
|
|
|
|
if (_selection.isNone()
|
2014-05-24 12:53:50 +02:00
|
|
|
|| (_selection.isList() && !_selection.isSingle())) {
|
|
|
|
if (_selection.isList())
|
2014-05-24 20:28:01 +02:00
|
|
|
deselectAll();
|
2014-05-24 22:24:48 +02:00
|
|
|
_selection.setRange(m->tick2segment(tick),
|
|
|
|
m == lastMeasure() ? 0 : m->last(),
|
|
|
|
staffIdx,
|
|
|
|
staffIdx + 1);
|
|
|
|
}
|
|
|
|
else if (_selection.isRange()) {
|
2014-05-31 22:23:23 +02:00
|
|
|
_selection.extendRangeSelection(m->tick2segment(tick),
|
|
|
|
m == lastMeasure() ? 0 : m->last(),
|
|
|
|
staffIdx,
|
|
|
|
tick,
|
|
|
|
etick);
|
2014-05-24 22:24:48 +02:00
|
|
|
}
|
|
|
|
else if (_selection.isSingle()) {
|
2014-06-30 13:05:28 +02:00
|
|
|
Element* oe = selection().element();
|
|
|
|
if (oe->type() == Element::Element::Type::NOTE || oe->isChordRest()) {
|
|
|
|
if (oe->type() == Element::Element::Type::NOTE)
|
|
|
|
oe = oe->parent();
|
|
|
|
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(oe);
|
|
|
|
int oetick = cr->segment()->tick();
|
|
|
|
if (tick < oetick) {
|
|
|
|
_selection.setStartSegment(m->tick2segment(tick));
|
|
|
|
if (etick >= oetick)
|
|
|
|
_selection.setEndSegment(m->last());
|
|
|
|
else
|
2014-08-10 02:09:04 +02:00
|
|
|
_selection.setEndSegment(cr->nextSegmentAfterCR(Segment::Type::ChordRest
|
|
|
|
| Segment::Type::EndBarLine
|
|
|
|
| Segment::Type::Clef));
|
2014-08-11 15:25:55 +02:00
|
|
|
|
2014-06-30 13:05:28 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
_selection.setStartSegment(cr->segment());
|
|
|
|
_selection.setEndSegment(m->last());
|
|
|
|
}
|
|
|
|
|
|
|
|
_selection.setStaffStart(staffIdx);
|
|
|
|
_selection.setStaffEnd(staffIdx + 1);
|
2014-08-15 17:20:20 +02:00
|
|
|
if (_selection.staffStart() > cr->staffIdx())
|
2014-06-30 13:05:28 +02:00
|
|
|
_selection.setStaffStart(cr->staffIdx());
|
|
|
|
else if (cr->staffIdx() >= _selection.staffEnd())
|
|
|
|
_selection.setStaffEnd(cr->staffIdx() + 1);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-06-30 13:05:28 +02:00
|
|
|
else {
|
|
|
|
deselectAll();
|
|
|
|
_selection.setRange(m->tick2segment(tick),
|
|
|
|
m == lastMeasure() ? 0 : m->last(),
|
|
|
|
staffIdx,
|
|
|
|
staffIdx + 1);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-06-17 14:20:24 +02:00
|
|
|
qDebug("SELECT_RANGE: measure: sel state %hhd", _selection.state());
|
2014-05-24 22:24:48 +02:00
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (e->type() == Element::Type::NOTE || e->isChordRest()) {
|
|
|
|
if (e->type() == Element::Type::NOTE)
|
2014-05-24 22:24:48 +02:00
|
|
|
e = e->parent();
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-24 12:53:50 +02:00
|
|
|
|
2014-05-31 21:14:25 +02:00
|
|
|
if (_selection.isNone()
|
|
|
|
|| (_selection.isList() && !_selection.isSingle())) {
|
|
|
|
if (_selection.isList())
|
|
|
|
deselectAll();
|
2014-05-31 19:30:17 +02:00
|
|
|
_selection.setRange(cr->segment(),
|
2014-06-30 11:26:58 +02:00
|
|
|
cr->nextSegmentAfterCR(Segment::Type::ChordRest
|
|
|
|
| Segment::Type::EndBarLine
|
|
|
|
| Segment::Type::Clef),
|
2014-05-31 19:30:17 +02:00
|
|
|
e->staffIdx(),
|
2014-06-18 10:17:00 +02:00
|
|
|
e->staffIdx() + 1);
|
2014-05-24 22:24:48 +02:00
|
|
|
activeTrack = cr->track();
|
|
|
|
}
|
|
|
|
else if (_selection.isSingle()) {
|
|
|
|
Element* oe = _selection.element();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (oe && (oe->type() == Element::Type::NOTE || oe->type() == Element::Type::REST)) {
|
|
|
|
if (oe->type() == Element::Type::NOTE)
|
2014-05-24 22:24:48 +02:00
|
|
|
oe = oe->parent();
|
|
|
|
ChordRest* ocr = static_cast<ChordRest*>(oe);
|
2014-05-31 21:14:25 +02:00
|
|
|
|
|
|
|
Segment* endSeg = tick2segment(ocr->segment()->tick() + ocr->actualTicks());
|
|
|
|
if (!endSeg)
|
|
|
|
endSeg = ocr->segment()->next();
|
|
|
|
|
2014-05-31 19:30:17 +02:00
|
|
|
_selection.setRange(ocr->segment(),
|
2014-05-31 21:14:25 +02:00
|
|
|
endSeg,
|
2014-05-31 19:30:17 +02:00
|
|
|
oe->staffIdx(),
|
2014-06-18 10:17:00 +02:00
|
|
|
oe->staffIdx() + 1);
|
2014-05-31 21:14:25 +02:00
|
|
|
|
|
|
|
_selection.extendRangeSelection(cr);
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-05-24 22:24:48 +02:00
|
|
|
select(e, SelectType::SINGLE, 0);
|
2014-05-24 20:28:01 +02:00
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
}
|
|
|
|
else if (_selection.isRange()) {
|
2014-05-31 21:14:25 +02:00
|
|
|
_selection.extendRangeSelection(cr);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-06-17 14:20:24 +02:00
|
|
|
qDebug("sel state %hhd", _selection.state());
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-05-24 22:24:48 +02:00
|
|
|
if (!_selection.endSegment())
|
|
|
|
_selection.setEndSegment(cr->segment()->nextCR());
|
|
|
|
if (!_selection.startSegment())
|
|
|
|
_selection.setStartSegment(cr->segment());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
select(e, SelectType::SINGLE, staffIdx);
|
|
|
|
return;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
_selection.setActiveTrack(activeTrack);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-02-09 18:21:37 +01:00
|
|
|
// doing this in note entry mode can clear selection
|
|
|
|
if (_selection.startSegment() && !noteEntryMode())
|
2014-11-15 23:12:09 +01:00
|
|
|
setPlayPos(_selection.startSegment()->tick());
|
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
_selection.updateSelectedElements();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2014-07-15 00:28:31 +02:00
|
|
|
// collectMatch
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::collectMatch(void* data, Element* e)
|
|
|
|
{
|
|
|
|
ElementPattern* p = static_cast<ElementPattern*>(data);
|
|
|
|
if (p->type != int(e->type()))
|
|
|
|
return;
|
2014-09-23 18:07:32 +02:00
|
|
|
|
|
|
|
if (p->subtypeValid) {
|
|
|
|
// HACK: grace note is different from normal note
|
|
|
|
// TODO: this disables the ability to distinguish note heads in subtype
|
|
|
|
|
|
|
|
if (p->type == int(Element::Type::NOTE)) {
|
|
|
|
if (p->subtype != static_cast<Note*>(e)->chord()->isGrace())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (p->subtype != e->subtype())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2014-07-16 15:36:20 +02:00
|
|
|
if ((p->staffStart != -1)
|
|
|
|
&& ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx())))
|
2014-07-15 00:28:31 +02:00
|
|
|
return;
|
2014-11-22 12:03:22 +01:00
|
|
|
if (e->type() == Element::Type::CHORD || e->type() == Element::Type::REST
|
|
|
|
|| e->type() == Element::Type::NOTE || e->type() == Element::Type::LYRICS
|
|
|
|
|| e->type() == Element::Type::BEAM
|
|
|
|
|| e->type() == Element::Type::STEM) {
|
2014-07-15 00:28:31 +02:00
|
|
|
if (p->voice != -1 && p->voice != e->voice())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (p->system) {
|
|
|
|
Element* ee = e;
|
|
|
|
do {
|
|
|
|
if (ee->type() == Element::Type::SYSTEM) {
|
|
|
|
if (p->system != ee)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ee = ee->parent();
|
|
|
|
} while (ee);
|
|
|
|
}
|
|
|
|
p->el.append(e);
|
|
|
|
}
|
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectSimilar
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-15 00:28:31 +02:00
|
|
|
void Score::selectSimilar(Element* e, bool sameStaff)
|
|
|
|
{
|
|
|
|
Element::Type type = e->type();
|
2014-08-15 17:20:20 +02:00
|
|
|
Score* score = e->score();
|
2014-07-15 00:28:31 +02:00
|
|
|
|
|
|
|
ElementPattern pattern;
|
2014-09-23 18:07:32 +02:00
|
|
|
pattern.type = int(type);
|
|
|
|
if (type == Element::Type::NOTE) {
|
|
|
|
pattern.subtype = static_cast<Note*>(e)->chord()->isGrace();
|
|
|
|
pattern.subtypeValid = true;
|
|
|
|
}
|
2014-10-08 04:21:02 +02:00
|
|
|
else if (type == Element::Type::SLUR_SEGMENT) {
|
|
|
|
pattern.subtype = static_cast<int>(static_cast<SlurSegment*>(e)->spanner()->type());
|
|
|
|
pattern.subtypeValid = true;
|
|
|
|
}
|
2014-09-23 18:07:32 +02:00
|
|
|
else {
|
|
|
|
pattern.subtype = 0;
|
|
|
|
pattern.subtypeValid = false;
|
|
|
|
}
|
2014-07-16 15:36:20 +02:00
|
|
|
pattern.staffStart = sameStaff ? e->staffIdx() : -1;
|
|
|
|
pattern.staffEnd = sameStaff ? e->staffIdx()+1 : -1;
|
2014-07-15 00:28:31 +02:00
|
|
|
pattern.voice = -1;
|
|
|
|
pattern.system = 0;
|
|
|
|
|
|
|
|
score->scanElements(&pattern, collectMatch);
|
|
|
|
|
|
|
|
score->select(0, SelectType::SINGLE, 0);
|
2014-08-15 17:20:20 +02:00
|
|
|
foreach (Element* e, pattern.el) {
|
2014-07-15 00:28:31 +02:00
|
|
|
score->select(e, SelectType::ADD, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectSimilarInRange
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-15 00:28:31 +02:00
|
|
|
void Score::selectSimilarInRange(Element* e)
|
|
|
|
{
|
|
|
|
Element::Type type = e->type();
|
|
|
|
ElementPattern pattern;
|
|
|
|
|
|
|
|
Score* score = e->score();
|
|
|
|
pattern.type = int(type);
|
|
|
|
pattern.subtype = 0;
|
2014-07-16 15:36:20 +02:00
|
|
|
pattern.staffStart = selection().staffStart();
|
|
|
|
pattern.staffEnd = selection().staffEnd();
|
2014-07-15 00:28:31 +02:00
|
|
|
pattern.voice = -1;
|
|
|
|
pattern.system = 0;
|
|
|
|
pattern.subtypeValid = false;
|
|
|
|
|
|
|
|
score->scanElementsInRange(&pattern, collectMatch);
|
|
|
|
|
|
|
|
score->select(0, SelectType::SINGLE, 0);
|
2014-08-15 17:20:20 +02:00
|
|
|
foreach (Element* e, pattern.el) {
|
2014-07-15 00:28:31 +02:00
|
|
|
score->select(e, SelectType::ADD, 0);
|
|
|
|
}
|
|
|
|
}
|
2014-08-15 17:20:20 +02:00
|
|
|
|
2014-07-15 00:28:31 +02:00
|
|
|
//---------------------------------------------------------
|
2012-05-26 14:26:10 +02:00
|
|
|
// lassoSelect
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::lassoSelect(const QRectF& bbox)
|
|
|
|
{
|
2014-05-27 10:34:08 +02:00
|
|
|
select(0, SelectType::SINGLE, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
QRectF fr(bbox.normalized());
|
|
|
|
foreach(Page* page, _pages) {
|
|
|
|
QRectF pr(page->bbox());
|
|
|
|
QRectF frr(fr.translated(-page->pos()));
|
|
|
|
if (pr.right() < frr.left())
|
|
|
|
continue;
|
|
|
|
if (pr.left() > frr.right())
|
|
|
|
break;
|
|
|
|
|
2013-04-29 15:31:22 +02:00
|
|
|
QList<Element*> el = page->items(frr);
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int i = 0; i < el.size(); ++i) {
|
2013-04-29 15:31:22 +02:00
|
|
|
Element* e = el.at(i);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (frr.contains(e->abbox())) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() != Element::Type::MEASURE && e->selectable())
|
2014-05-27 10:34:08 +02:00
|
|
|
select(e, SelectType::ADD, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// lassoSelectEnd
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::lassoSelectEnd()
|
|
|
|
{
|
|
|
|
int noteRestCount = 0;
|
|
|
|
Segment* startSegment = 0;
|
|
|
|
Segment* endSegment = 0;
|
|
|
|
int startStaff = 0x7fffffff;
|
|
|
|
int endStaff = 0;
|
2014-12-18 20:41:15 +01:00
|
|
|
const ChordRest* endCR = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (_selection.elements().isEmpty()) {
|
2014-05-26 13:21:04 +02:00
|
|
|
_selection.setState(SelState::NONE);
|
2013-03-04 20:10:58 +01:00
|
|
|
_updateAll = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-05-26 13:21:04 +02:00
|
|
|
_selection.setState(SelState::LIST);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
foreach(const Element* e, _selection.elements()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() != Element::Type::NOTE && e->type() != Element::Type::REST)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
++noteRestCount;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
e = e->parent();
|
|
|
|
Segment* seg = static_cast<const ChordRest*>(e)->segment();
|
2012-08-09 12:12:43 +02:00
|
|
|
if ((startSegment == 0) || (*seg < *startSegment))
|
2012-05-26 14:26:10 +02:00
|
|
|
startSegment = seg;
|
2012-08-09 12:12:43 +02:00
|
|
|
if ((endSegment == 0) || (*seg > *endSegment)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
endSegment = seg;
|
2014-05-31 17:22:45 +02:00
|
|
|
endCR = static_cast<const ChordRest*>(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
int idx = e->staffIdx();
|
|
|
|
if (idx < startStaff)
|
|
|
|
startStaff = idx;
|
|
|
|
if (idx > endStaff)
|
|
|
|
endStaff = idx;
|
|
|
|
}
|
|
|
|
if (noteRestCount > 0) {
|
2014-05-31 17:22:45 +02:00
|
|
|
endSegment = endCR->nextSegmentAfterCR(Segment::Type::ChordRest
|
|
|
|
| Segment::Type::EndBarLine
|
|
|
|
| Segment::Type::Clef);
|
2012-05-26 14:26:10 +02:00
|
|
|
_selection.setRange(startSegment, endSegment, startStaff, endStaff+1);
|
2014-05-24 12:53:50 +02:00
|
|
|
if (!_selection.isRange())
|
2014-05-26 13:21:04 +02:00
|
|
|
_selection.setState(SelState::RANGE);
|
2014-05-31 17:22:45 +02:00
|
|
|
_selection.updateSelectedElements();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
_updateAll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addLyrics
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addLyrics(int tick, int staffIdx, const QString& txt)
|
|
|
|
{
|
|
|
|
if (txt.trimmed().isEmpty())
|
|
|
|
return;
|
|
|
|
Measure* measure = tick2measure(tick);
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* seg = measure->findSegment(Segment::Type::ChordRest, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (seg == 0) {
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("no segment found for lyrics<%s> at tick %d",
|
2012-05-26 14:26:10 +02:00
|
|
|
qPrintable(txt), tick);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(seg->element(track));
|
|
|
|
if (cr) {
|
|
|
|
Lyrics* l = new Lyrics(this);
|
|
|
|
l->setText(txt);
|
2013-09-06 16:59:20 +02:00
|
|
|
l->setTrack(track);
|
2012-05-26 14:26:10 +02:00
|
|
|
cr->add(l);
|
|
|
|
}
|
|
|
|
else {
|
2013-07-25 17:22:49 +02:00
|
|
|
qDebug("no chord/rest for lyrics<%s> at tick %d, staff %d",
|
2012-05-26 14:26:10 +02:00
|
|
|
qPrintable(txt), tick, staffIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTempo
|
|
|
|
// convenience function to access TempoMap
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setTempo(Segment* segment, qreal tempo)
|
|
|
|
{
|
|
|
|
setTempo(segment->tick(), tempo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Score::setTempo(int tick, qreal tempo)
|
|
|
|
{
|
|
|
|
tempomap()->setTempo(tick, tempo);
|
|
|
|
_playlistDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeTempo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeTempo(int tick)
|
|
|
|
{
|
|
|
|
tempomap()->delTempo(tick);
|
|
|
|
_playlistDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setPause
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setPause(int tick, qreal seconds)
|
|
|
|
{
|
|
|
|
tempomap()->setPause(tick, seconds);
|
|
|
|
_playlistDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tempo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal Score::tempo(int tick) const
|
|
|
|
{
|
|
|
|
return tempomap()->tempo(tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// loWidth
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal Score::loWidth() const
|
|
|
|
{
|
|
|
|
return pageFormat()->size().width() * MScore::DPI;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// loHeight
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal Score::loHeight() const
|
|
|
|
{
|
|
|
|
return pageFormat()->size().height() * MScore::DPI;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdSelectAll
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdSelectAll()
|
|
|
|
{
|
2013-10-06 16:43:43 +02:00
|
|
|
if (_measures.size() == 0)
|
|
|
|
return;
|
2014-08-20 19:18:27 +02:00
|
|
|
deselectAll();
|
2014-11-11 06:16:30 +01:00
|
|
|
Measure* first = firstMeasureMM();
|
|
|
|
if (!first)
|
|
|
|
return;
|
|
|
|
Measure* last = lastMeasureMM();
|
|
|
|
selectRange(first, 0);
|
|
|
|
selectRange(last, nstaves() - 1);
|
2013-10-06 16:43:43 +02:00
|
|
|
setUpdateAll(true);
|
|
|
|
end();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdSelectSection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdSelectSection()
|
|
|
|
{
|
|
|
|
Segment* s = _selection.startSegment();
|
|
|
|
if (s == 0)
|
|
|
|
return;
|
|
|
|
MeasureBase* sm = s->measure();
|
|
|
|
MeasureBase* em = sm;
|
|
|
|
while (sm->prev()) {
|
|
|
|
if (sm->prev()->sectionBreak())
|
|
|
|
break;
|
|
|
|
sm = sm->prev();
|
|
|
|
}
|
|
|
|
while (em->next()) {
|
|
|
|
if (em->sectionBreak())
|
|
|
|
break;
|
|
|
|
em = em->next();
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
while (sm && sm->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
sm = sm->next();
|
2014-06-24 18:36:02 +02:00
|
|
|
while (em && em->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
em = em->next();
|
|
|
|
if (sm == 0 || em == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_selection.setRange(static_cast<Measure*>(sm)->first(),
|
|
|
|
static_cast<Measure*>(em)->last(), 0, nstaves());
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// undo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::undo(UndoCommand* cmd) const
|
|
|
|
{
|
|
|
|
undo()->push(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setLayoutMode
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setLayoutMode(LayoutMode lm)
|
|
|
|
{
|
|
|
|
_layoutMode = lm;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// linkId
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::linkId()
|
|
|
|
{
|
2012-09-22 19:40:45 +02:00
|
|
|
return (rootScore()->_linkId)++;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-22 19:40:45 +02:00
|
|
|
// val is a used link id
|
2012-05-26 14:26:10 +02:00
|
|
|
void Score::linkId(int val)
|
|
|
|
{
|
2012-09-10 16:10:25 +02:00
|
|
|
Score* s = rootScore();
|
2012-09-22 19:40:45 +02:00
|
|
|
if (val >= s->_linkId)
|
|
|
|
s->_linkId = val + 1; // update unused link id
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// scoreList
|
|
|
|
// return a list of scores containing the root score
|
|
|
|
// and all part scores (if there are any)
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<Score*> Score::scoreList()
|
|
|
|
{
|
|
|
|
QList<Score*> scores;
|
|
|
|
Score* root = rootScore();
|
|
|
|
scores.append(root);
|
2014-11-27 14:50:02 +01:00
|
|
|
for (const Excerpt* ex : root->excerpts()) {
|
|
|
|
if (ex->partScore())
|
|
|
|
scores.append(ex->partScore());
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return scores;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// switchLayer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::switchLayer(const QString& s)
|
|
|
|
{
|
|
|
|
int layerIdx = 0;
|
2014-05-31 12:03:21 +02:00
|
|
|
for (const Layer& l : layer()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (s == l.name) {
|
|
|
|
setCurrentLayer(layerIdx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
++layerIdx;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-07 09:24:05 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendPart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::appendPart(const QString& name)
|
|
|
|
{
|
|
|
|
static InstrumentTemplate defaultInstrument;
|
|
|
|
InstrumentTemplate* t;
|
|
|
|
|
|
|
|
t = searchTemplate(name);
|
2012-06-08 19:06:52 +02:00
|
|
|
if (t == 0) {
|
|
|
|
qDebug("appendPart: <%s> not found", qPrintable(name));
|
2012-06-07 09:24:05 +02:00
|
|
|
t = &defaultInstrument;
|
2012-06-08 19:06:52 +02:00
|
|
|
}
|
2012-06-07 09:24:05 +02:00
|
|
|
|
|
|
|
if (t->channel.isEmpty()) {
|
|
|
|
Channel a;
|
|
|
|
a.chorus = 0;
|
|
|
|
a.reverb = 0;
|
|
|
|
a.name = "normal";
|
|
|
|
a.bank = 0;
|
|
|
|
a.volume = 100;
|
2014-12-11 11:35:10 +01:00
|
|
|
a.pan = 64; // actually 63.5 for center
|
2012-06-07 09:24:05 +02:00
|
|
|
t->channel.append(a);
|
|
|
|
}
|
|
|
|
Part* part = new Part(this);
|
|
|
|
part->initFromInstrTemplate(t);
|
|
|
|
int n = nstaves();
|
2012-07-26 14:35:13 +02:00
|
|
|
for (int i = 0; i < t->nstaves(); ++i) {
|
2014-08-16 13:32:08 +02:00
|
|
|
Staff* staff = new Staff(this);
|
|
|
|
staff->setPart(part);
|
2012-06-07 09:24:05 +02:00
|
|
|
staff->setLines(t->staffLines[i]);
|
|
|
|
staff->setSmall(t->smallStaff[i]);
|
|
|
|
if (i == 0) {
|
|
|
|
staff->setBracket(0, t->bracket[0]);
|
2012-07-26 14:35:13 +02:00
|
|
|
staff->setBracketSpan(0, t->nstaves());
|
2012-06-07 09:24:05 +02:00
|
|
|
}
|
|
|
|
undoInsertStaff(staff, n + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
part->staves()->front()->setBarLineSpan(part->nstaves());
|
2014-08-16 14:18:06 +02:00
|
|
|
undoInsertPart(part, n);
|
2012-06-07 09:24:05 +02:00
|
|
|
fixTicks();
|
|
|
|
rebuildMidiMapping();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendMeasures
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::appendMeasures(int n)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < n; ++i)
|
2014-06-24 18:36:02 +02:00
|
|
|
insertMeasure(Element::Type::MEASURE, 0, false);
|
2012-06-07 09:24:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addText
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addText(const QString& type, const QString& txt)
|
|
|
|
{
|
|
|
|
MeasureBase* measure = first();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (measure == 0 || measure->type() != Element::Type::VBOX) {
|
|
|
|
insertMeasure(Element::Type::VBOX, measure);
|
2012-06-07 09:24:05 +02:00
|
|
|
measure = first();
|
|
|
|
}
|
|
|
|
Text* text = new Text(this);
|
|
|
|
if (type == "title")
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::TITLE);
|
2012-06-07 09:24:05 +02:00
|
|
|
else if (type == "subtitle")
|
2014-05-30 10:13:29 +02:00
|
|
|
text->setTextStyleType(TextStyleType::SUBTITLE);
|
2012-06-07 09:24:05 +02:00
|
|
|
text->setParent(measure);
|
|
|
|
text->setText(txt);
|
|
|
|
undoAddElement(text);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-06-28 10:03:19 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// newCursor
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Cursor* Score::newCursor()
|
|
|
|
{
|
|
|
|
return new Cursor(this);
|
|
|
|
}
|
|
|
|
|
2013-06-20 13:57:15 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addSpanner
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addSpanner(Spanner* s)
|
|
|
|
{
|
2013-07-05 11:23:52 +02:00
|
|
|
_spanner.addSpanner(s);
|
2013-06-20 13:57:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeSpanner
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeSpanner(Spanner* s)
|
|
|
|
{
|
2013-07-05 11:23:52 +02:00
|
|
|
_spanner.removeSpanner(s);
|
2013-06-20 13:57:15 +02:00
|
|
|
}
|
|
|
|
|
2013-06-10 11:03:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// isSpannerStartEnd
|
|
|
|
// does is spanner start or end at tick position tick
|
|
|
|
// for track ?
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::isSpannerStartEnd(int tick, int track) const
|
|
|
|
{
|
2013-07-05 11:23:52 +02:00
|
|
|
for (auto i : _spanner.map()) {
|
2013-06-20 13:57:15 +02:00
|
|
|
if (i.second->track() != track)
|
2013-06-10 11:03:34 +02:00
|
|
|
continue;
|
2013-06-20 13:57:15 +02:00
|
|
|
if (i.second->tick() == tick || i.second->tick2() == tick)
|
2013-06-10 11:03:34 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-29 17:33:45 +01:00
|
|
|
void Score::insertTime(int tick, int len)
|
|
|
|
{
|
2015-02-17 09:36:42 +01:00
|
|
|
for (Staff* staff : staves())
|
|
|
|
staff->insertTime(tick, len);
|
2015-03-13 12:39:38 +01:00
|
|
|
for (Part* part : parts())
|
|
|
|
part->insertTime(tick, len);
|
2013-06-19 16:25:29 +02:00
|
|
|
}
|
2012-06-28 10:03:19 +02:00
|
|
|
|
Lyrics multi-system melisma and dashes
Implements melisma and dash lines for lyrics spanning several systems.
The melisma and dash line is based on the `SLine` class and its segments on the `LineSegment` class. Both the whole line and its segments are not selectable, marked as generated and not saved in the score file, which is not changed in any way.
For very wide dash segments, several dashes are drawn; the distance between the dashes is not configurable.
Lyrics layout code in `Measure` class and in `layout.cpp` file has been commented out as the lyrics line layout is all contained in the lyrics.cpp file
The line is registered with the `Score` (to have its layout delayed until all elements are positioned) with a mechanism similar to other `Spanner`'s, but in a different container (`_unmanagedSpanner`), as the owning `Lyrics` should decide when create, register, unregister and delete its line.
The line segments are registered with the `System` they belong to (to have them drawn), in the same way as other `Spanner`'s.
There is code for using the dash metrics of the lyrics font, but it is turned off via a conditional directive, as there does not seem to be a reliable way to determine the dash metrics; conventional values (determined by trials and errors and based on my taste!) are used when the conditional directive is off.
2015-01-11 10:11:44 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addUnmanagedSpanner
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::addUnmanagedSpanner(Spanner* s)
|
|
|
|
{
|
|
|
|
_unmanagedSpanner.insert(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeSpanner
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeUnmanagedSpanner(Spanner* s)
|
|
|
|
{
|
|
|
|
_unmanagedSpanner.erase(s);
|
|
|
|
}
|
|
|
|
|
2013-08-22 18:29:25 +02:00
|
|
|
//---------------------------------------------------------
|
2013-10-18 12:21:01 +02:00
|
|
|
// setPos
|
2013-08-22 18:29:25 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-10-18 12:21:01 +02:00
|
|
|
void Score::setPos(POS pos, int tick)
|
2013-08-22 18:29:25 +02:00
|
|
|
{
|
2013-10-18 12:21:01 +02:00
|
|
|
if (tick < 0)
|
2013-08-22 18:29:25 +02:00
|
|
|
tick = 0;
|
2014-11-13 08:42:16 +01:00
|
|
|
if (tick != _pos[int(pos)])
|
2013-10-18 12:21:01 +02:00
|
|
|
_pos[int(pos)] = tick;
|
2014-11-13 08:42:16 +01:00
|
|
|
// even though tick position might not have changed, layout might have
|
2014-11-17 16:44:05 +01:00
|
|
|
// so we should update cursor here
|
|
|
|
// however, we must be careful not to call setPos() again while handling posChanged, or recursion results
|
2014-11-13 08:42:16 +01:00
|
|
|
emit posChanged(pos, unsigned(tick));
|
2013-08-22 18:29:25 +02:00
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
|
2014-05-22 16:18:35 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// uniqueStaves
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<int> Score::uniqueStaves() const
|
|
|
|
{
|
|
|
|
QList<int> sl;
|
|
|
|
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
Staff* s = staff(staffIdx);
|
|
|
|
if (s->linkedStaves()) {
|
|
|
|
bool alreadyInList = false;
|
|
|
|
for (int idx : sl) {
|
|
|
|
if (s->linkedStaves()->staves().contains(staff(idx))) {
|
|
|
|
alreadyInList = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (alreadyInList)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sl.append(staffIdx);
|
|
|
|
}
|
|
|
|
return sl;
|
|
|
|
}
|
|
|
|
|
2014-05-29 12:13:49 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// findCR
|
|
|
|
// find chord/rest <= tick in track
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Score::findCR(int tick, int track) const
|
|
|
|
{
|
|
|
|
Measure* m = tick2measureMM(tick);
|
2014-07-27 16:48:05 +02:00
|
|
|
if (!m) {
|
|
|
|
qDebug("findCR: no measure for tick %d", tick);
|
2014-07-27 16:11:59 +02:00
|
|
|
return nullptr;
|
2014-07-27 16:48:05 +02:00
|
|
|
}
|
2014-05-29 12:13:49 +02:00
|
|
|
// attach to first rest all spanner when mmRest
|
|
|
|
if (m->isMMRest())
|
|
|
|
tick = m->tick();
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* s = m->first(Segment::Type::ChordRest);
|
|
|
|
for (Segment* ns = s; ; ns = ns->next(Segment::Type::ChordRest)) {
|
2014-05-29 12:13:49 +02:00
|
|
|
if (ns == 0 || ns->tick() > tick)
|
|
|
|
break;
|
|
|
|
if (ns->element(track))
|
|
|
|
s = ns;
|
|
|
|
}
|
|
|
|
if (s)
|
|
|
|
return static_cast<ChordRest*>(s->element(track));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-08-18 11:33:17 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// findCRinStaff
|
|
|
|
// find chord/rest <= tick in staff
|
2015-02-11 08:28:31 +01:00
|
|
|
// prefer shortest duration (so it does not overlap next chordrest segment)
|
2014-08-18 11:33:17 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Score::findCRinStaff(int tick, int track) const
|
|
|
|
{
|
|
|
|
Measure* m = tick2measureMM(tick);
|
|
|
|
if (!m) {
|
2014-08-19 13:34:14 +02:00
|
|
|
qDebug("findCRinStaff: no measure for tick %d", tick);
|
2014-08-18 11:33:17 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
// attach to first rest all spanner when mmRest
|
|
|
|
if (m->isMMRest())
|
|
|
|
tick = m->tick();
|
|
|
|
Segment* s = m->first(Segment::Type::ChordRest);
|
|
|
|
int strack = (track / VOICES) * VOICES;
|
|
|
|
int etrack = strack + VOICES;
|
|
|
|
int actualTrack = strack;
|
|
|
|
|
|
|
|
for (Segment* ns = s; ; ns = ns->next(Segment::Type::ChordRest)) {
|
|
|
|
if (ns == 0 || ns->tick() > tick)
|
|
|
|
break;
|
2015-02-11 08:28:31 +01:00
|
|
|
// found a segment; now find shortest chordrest on this staff (if any)
|
|
|
|
ChordRest* shortestCR = 0;
|
2014-08-18 11:33:17 +02:00
|
|
|
for (int t = strack; t < etrack; ++t) {
|
2015-02-11 08:28:31 +01:00
|
|
|
ChordRest* cr = static_cast<ChordRest*>(ns->element(t));
|
|
|
|
if (cr && (!shortestCR || cr->actualTicks() < shortestCR->actualTicks())) {
|
|
|
|
shortestCR = cr;
|
2014-08-18 11:33:17 +02:00
|
|
|
s = ns;
|
|
|
|
actualTrack = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (s)
|
|
|
|
return static_cast<ChordRest*>(s->element(actualTrack));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-08-16 17:01:41 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setSoloMute
|
|
|
|
// called once at opening file, adds soloMute marks
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setSoloMute()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < _midiMapping.size(); i++) {
|
|
|
|
Channel* b = _midiMapping[i].articulation;
|
|
|
|
if (b->solo) {
|
|
|
|
b->soloMute = false;
|
|
|
|
for (int j = 0; j < _midiMapping.size(); j++) {
|
|
|
|
Channel* a = _midiMapping[j].articulation;
|
|
|
|
a->soloMute = (i != j && !a->solo);
|
|
|
|
a->solo = (i == j || a->solo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 14:04:45 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setName(QString s)
|
|
|
|
{
|
|
|
|
s.replace('/', '_'); // for sanity
|
|
|
|
if (!(s.endsWith(".mscz") || s.endsWith(".mscx")))
|
|
|
|
s += ".mscz";
|
|
|
|
info.setFile(s);
|
|
|
|
}
|
|
|
|
|
2014-11-01 10:52:35 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setImportedFilePath
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::setImportedFilePath(const QString& filePath)
|
|
|
|
{
|
|
|
|
_importedFilePath = filePath;
|
|
|
|
}
|
|
|
|
|
2014-11-14 21:00:36 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// title
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Score::title()
|
|
|
|
{
|
2014-12-28 18:20:12 +01:00
|
|
|
QString fn;
|
2014-11-14 21:00:36 +01:00
|
|
|
Text* t = getText(TextStyleType::TITLE);
|
|
|
|
if (t)
|
|
|
|
fn = QTextDocumentFragment::fromHtml(t->text()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\"");
|
2014-12-28 18:20:12 +01:00
|
|
|
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = metaTag("workTitle");
|
|
|
|
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = fileInfo()->baseName();
|
|
|
|
|
2014-11-14 21:00:36 +01:00
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = "Untitled";
|
2014-12-28 18:20:12 +01:00
|
|
|
|
2014-11-14 21:00:36 +01:00
|
|
|
return fn.simplified();
|
|
|
|
}
|
|
|
|
|
2014-12-28 18:20:12 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// subtitle
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Score::subtitle()
|
|
|
|
{
|
|
|
|
QString fn;
|
|
|
|
Text* t = getText(TextStyleType::SUBTITLE);
|
|
|
|
if (t)
|
|
|
|
fn = QTextDocumentFragment::fromHtml(t->text()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\"");
|
|
|
|
|
|
|
|
return fn.simplified();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// composer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Score::composer()
|
|
|
|
{
|
|
|
|
QString fn;
|
|
|
|
Text* t = getText(TextStyleType::COMPOSER);
|
|
|
|
if (t)
|
|
|
|
fn = QTextDocumentFragment::fromHtml(t->text()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\"");
|
|
|
|
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = metaTag("composer");
|
|
|
|
|
|
|
|
return fn.simplified();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// poet
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Score::poet()
|
|
|
|
{
|
|
|
|
QString fn;
|
|
|
|
Text* t = getText(TextStyleType::POET);
|
|
|
|
if (t)
|
|
|
|
fn = QTextDocumentFragment::fromHtml(t->text()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\"");
|
|
|
|
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = metaTag("lyricist");
|
|
|
|
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = "";
|
|
|
|
|
|
|
|
return fn.simplified();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nmeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::nmeasures()
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure())
|
|
|
|
n++;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// hasLyrics
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::hasLyrics()
|
|
|
|
{
|
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
|
|
|
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
|
|
|
|
for (int i = 0; i < ntracks() ; ++i) {
|
|
|
|
if (seg->lyricsList(i) && seg->lyricsList(i)->size() > 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// hasHarmonies
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::hasHarmonies()
|
|
|
|
{
|
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
|
|
|
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
|
|
|
|
for (Element* e : seg->annotations()) {
|
|
|
|
if (e->type() == Element::Type::HARMONY)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// keysig
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::keysig()
|
|
|
|
{
|
|
|
|
Key result = Key::C;
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
Staff* st = staff(staffIdx);
|
|
|
|
Key key = st->key(0);
|
|
|
|
if (st->staffType()->group() == StaffGroup::PERCUSSION || st->keySigEvent(0).custom()) // ignore percussion and custom key
|
|
|
|
continue;
|
|
|
|
result = key;
|
2015-03-13 11:16:43 +01:00
|
|
|
int diff = st->part()->instrument()->transpose().chromatic;
|
2014-12-28 18:20:12 +01:00
|
|
|
if (!styleB(StyleIdx::concertPitch) && diff)
|
|
|
|
result = transposeKey(key, diff);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return int(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// duration
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::duration()
|
|
|
|
{
|
|
|
|
updateRepeatList(true);
|
|
|
|
RepeatSegment* rs = repeatList()->last();
|
|
|
|
return lrint(utick2utime(rs->utick + rs->len));
|
|
|
|
}
|
|
|
|
|
2014-11-15 16:29:48 +01:00
|
|
|
//---------------------------------------------------------
|
2014-12-09 08:45:30 +01:00
|
|
|
// createRehearsalMarkText
|
2014-11-15 16:29:48 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-12-09 08:45:30 +01:00
|
|
|
QString Score::createRehearsalMarkText(RehearsalMark* current) const
|
2014-11-15 16:29:48 +01:00
|
|
|
{
|
2014-12-09 08:45:30 +01:00
|
|
|
int tick = current->segment()->tick();
|
2014-11-15 16:29:48 +01:00
|
|
|
RehearsalMark* before = 0;
|
|
|
|
RehearsalMark* after = 0;
|
|
|
|
for (Segment* s = firstSegment(); s; s = s->next1()) {
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
if (e && e->type() == Element::Type::REHEARSAL_MARK) {
|
|
|
|
if (s->tick() < tick)
|
|
|
|
before = static_cast<RehearsalMark*>(e);
|
|
|
|
else if (s->tick() > tick) {
|
|
|
|
after = static_cast<RehearsalMark*>(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (after)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QString s = "A";
|
|
|
|
QString s1 = before ? before->text() : "";
|
|
|
|
QString s2 = after ? after->text() : "";
|
|
|
|
// qDebug("createRehearsalMark <%s> xx <%s>", qPrintable(s1), qPrintable(s2));
|
|
|
|
if (s1.isEmpty())
|
|
|
|
return s;
|
2014-12-09 08:45:30 +01:00
|
|
|
s = nextRehearsalMarkText(before, current); // try to sequence
|
2014-11-15 16:29:48 +01:00
|
|
|
if (!s2.isEmpty()) {
|
2014-12-09 08:45:30 +01:00
|
|
|
if (s != s2 && s != current->text())
|
|
|
|
return s; // found something between before & after
|
|
|
|
else if (s1.size() == 2)
|
|
|
|
s = s1[0] + QChar::fromLatin1(s1[1].toLatin1() + 1); // B1 -> B2, BB -> BC, etc
|
|
|
|
else if (s1[0].isLetter())
|
|
|
|
s = s1 + QChar::fromLatin1('1'); // B -> B1, Bridge -> Bridge1, etc
|
|
|
|
}
|
|
|
|
else if (s == current->text()) {
|
|
|
|
s = s1 + QChar::fromLatin1('1'); // B -> B1, Bridge -> Bridge1, etc
|
2014-11-15 16:29:48 +01:00
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2014-12-09 08:45:30 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextRehearsalMarkText
|
|
|
|
// finds next rehearsal in sequence established by previous
|
|
|
|
// Alphabetic sequences:
|
|
|
|
// A, B, ..., Y, Z, AA, BB, ..., YY, ZZ
|
|
|
|
// a, b, ..., y, z, aa, bb, ..., yy, zz
|
|
|
|
// Numeric sequences:
|
|
|
|
// 1, 2, 3, ...
|
|
|
|
// If number of previous rehearsal mark matches measure number, assume use of measure numbers throughout
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Score::nextRehearsalMarkText(RehearsalMark* previous, RehearsalMark* current) const
|
|
|
|
{
|
|
|
|
QString previousText = previous->text();
|
|
|
|
QString fallback = current ? current->text() : previousText + "'";
|
|
|
|
|
|
|
|
if (previousText.length() == 1 && previousText[0].isLetter()) {
|
|
|
|
// single letter sequence
|
|
|
|
if (previousText == "Z")
|
|
|
|
return "AA";
|
|
|
|
else if (previousText == "z")
|
|
|
|
return "aa";
|
|
|
|
else
|
|
|
|
return QChar::fromLatin1(previousText[0].toLatin1() + 1);
|
|
|
|
}
|
|
|
|
else if (previousText.length() == 2 && previousText[0].isLetter() && previousText[1].isLetter()) {
|
|
|
|
// double letter sequence
|
|
|
|
if (previousText[0] == previousText[1]) {
|
|
|
|
// repeated letter sequence
|
|
|
|
if (previousText.toUpper() != "ZZ") {
|
|
|
|
QString c = QChar::fromLatin1(previousText[0].toLatin1() + 1);
|
|
|
|
return c + c;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return fallback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return fallback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// try to interpret as number
|
|
|
|
bool ok;
|
|
|
|
int n = previousText.toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
return fallback;
|
|
|
|
}
|
|
|
|
else if (current && n == previous->segment()->measure()->no() + 1) {
|
|
|
|
// use measure number
|
|
|
|
n = current->segment()->measure()->no() + 1;
|
|
|
|
return QString("%1").arg(n);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// use number sequence
|
|
|
|
n = previousText.toInt() + 1;
|
|
|
|
return QString("%1").arg(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-13 06:57:37 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// changeVoice
|
|
|
|
// moves selected notes into specified voice if possible
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::changeVoice(int voice)
|
|
|
|
{
|
|
|
|
startCmd();
|
|
|
|
QList<Element*> el;
|
|
|
|
foreach(Element* e, selection().elements()) {
|
|
|
|
if (e->type() == Element::Type::NOTE) {
|
|
|
|
Note* note = static_cast<Note*>(e);
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
|
|
|
|
// TODO - handle ties; for now we skip tied notes
|
|
|
|
if (note->tieFor() || note->tieBack())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (chord->voice() != voice) {
|
|
|
|
Segment* s = chord->segment();
|
|
|
|
Measure* m = s->measure();
|
|
|
|
int notes = chord->notes().size();
|
|
|
|
int dstTrack = chord->staffIdx() * VOICES + voice;
|
|
|
|
ChordRest* dstCR = static_cast<ChordRest*>(s->element(dstTrack));
|
|
|
|
Chord* dstChord = nullptr;
|
|
|
|
|
|
|
|
// set up destination chord
|
|
|
|
|
|
|
|
if (dstCR && dstCR->type() == Element::Type::CHORD && dstCR->globalDuration() == chord->globalDuration()) {
|
|
|
|
// existing chord in destination with correct duration;
|
|
|
|
// can simply move note in
|
|
|
|
dstChord = static_cast<Chord*>(dstCR);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (dstCR && dstCR->type() == Element::Type::REST && dstCR->globalDuration() == chord->globalDuration()) {
|
|
|
|
// existing rest in destination with correct duration;
|
|
|
|
// replace with chord, then move note in
|
|
|
|
// this case allows for tuplets, unlike the more general case below
|
|
|
|
dstChord = new Chord(this);
|
|
|
|
dstChord->setTrack(dstTrack);
|
|
|
|
dstChord->setDurationType(chord->durationType());
|
|
|
|
dstChord->setDuration(chord->duration());
|
|
|
|
dstChord->setTuplet(dstCR->tuplet());
|
|
|
|
dstChord->setParent(s);
|
|
|
|
undoRemoveElement(dstCR);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (!chord->tuplet()) {
|
|
|
|
// rests or gap in destination
|
|
|
|
// insert new chord if the rests / gap are long enough
|
|
|
|
// then move note in
|
|
|
|
ChordRest* pcr = nullptr;
|
|
|
|
ChordRest* ncr = nullptr;
|
|
|
|
for (Segment* s2 = m->first(Segment::Type::ChordRest); s2; s2 = s2->next()) {
|
|
|
|
ChordRest* cr2 = static_cast<ChordRest*>(s2->element(dstTrack));
|
|
|
|
if (!cr2 || cr2->type() == Element::Type::REST)
|
|
|
|
continue;
|
|
|
|
if (s2->tick() < s->tick()) {
|
|
|
|
pcr = cr2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (s2->tick() >= s->tick()) {
|
|
|
|
ncr = cr2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int gapStart = pcr ? pcr->tick() + pcr->actualTicks() : m->tick();
|
|
|
|
int gapEnd = ncr ? ncr->tick() : m->tick() + m->ticks();
|
|
|
|
if (gapStart <= s->tick() && gapEnd >= s->tick() + chord->actualTicks()) {
|
|
|
|
// big enough gap found
|
|
|
|
dstChord = new Chord(this);
|
|
|
|
dstChord->setTrack(dstTrack);
|
|
|
|
dstChord->setDurationType(chord->durationType());
|
|
|
|
dstChord->setDuration(chord->duration());
|
|
|
|
dstChord->setParent(s);
|
|
|
|
// makeGapVoice will not back-fill an empty voice
|
|
|
|
if (voice && !dstCR)
|
|
|
|
expandVoice(s, /*m->first(Segment::Type::ChordRest,*/ dstTrack);
|
|
|
|
makeGapVoice(s, dstTrack, chord->actualFraction(), s->tick());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// move note to destination chord
|
|
|
|
if (dstChord) {
|
|
|
|
// create & add new note
|
|
|
|
Note* newNote = new Note(*note);
|
|
|
|
newNote->setSelected(false);
|
|
|
|
newNote->setParent(dstChord);
|
|
|
|
undoAddElement(newNote);
|
|
|
|
el.append(newNote);
|
|
|
|
// add new chord if one was created
|
|
|
|
if (dstChord != dstCR)
|
|
|
|
undoAddCR(dstChord, m, s->tick());
|
|
|
|
// remove original note
|
|
|
|
if (notes > 1) {
|
|
|
|
undoRemoveElement(note);
|
|
|
|
}
|
|
|
|
else if (notes == 1) {
|
|
|
|
// create rest to leave behind
|
|
|
|
Rest* r = new Rest(this);
|
|
|
|
r->setTrack(chord->track());
|
|
|
|
r->setDurationType(chord->durationType());
|
|
|
|
r->setDuration(chord->duration());
|
|
|
|
r->setTuplet(chord->tuplet());
|
|
|
|
r->setParent(s);
|
|
|
|
// remove chord, replace with rest
|
|
|
|
undoRemoveElement(chord);
|
|
|
|
undoAddCR(r, m, s->tick());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!el.isEmpty())
|
|
|
|
selection().clear();
|
|
|
|
foreach(Element* e, el)
|
|
|
|
select(e, SelectType::ADD, -1);
|
|
|
|
setLayoutAll(true);
|
|
|
|
endCmd();
|
|
|
|
}
|
|
|
|
|
2015-02-20 22:30:10 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cropPage - crop a single page score to the content
|
|
|
|
/// margins will be applied on the 4 sides
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cropPage(qreal margins)
|
|
|
|
{
|
|
|
|
if (npages() == 1) {
|
|
|
|
Page* page = pages()[0];
|
|
|
|
if (page) {
|
|
|
|
QRectF ttbox = page->tbbox();
|
|
|
|
|
|
|
|
PageFormat* curFormat = pageFormat();
|
|
|
|
PageFormat f;
|
|
|
|
f.copy(*curFormat);
|
|
|
|
|
|
|
|
qreal margin = margins / INCH;
|
|
|
|
f.setSize(QSizeF((ttbox.width() / MScore::DPI) + 2 * margin, (ttbox.height()/ MScore::DPI) + 2 * margin));
|
|
|
|
|
|
|
|
qreal offset = curFormat->oddLeftMargin() - ttbox.x() / MScore::DPI;
|
|
|
|
if (offset < 0)
|
|
|
|
offset = 0.0;
|
|
|
|
f.setOddLeftMargin(margin + offset);
|
|
|
|
f.setEvenLeftMargin(margin + offset);
|
|
|
|
f.setOddBottomMargin(margin);
|
|
|
|
f.setOddTopMargin(margin);
|
|
|
|
f.setEvenBottomMargin(margin);
|
|
|
|
f.setEvenTopMargin(margin);
|
|
|
|
|
|
|
|
undoChangePageFormat(&f, spatium(), pageNumberOffset());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|