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 Selection plus other selection related functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "mscore.h"
|
2014-06-29 16:48:39 +02:00
|
|
|
#include "arpeggio.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "barline.h"
|
|
|
|
#include "beam.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "chord.h"
|
2014-06-15 13:37:45 +02:00
|
|
|
#include "element.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "figuredbass.h"
|
2014-06-29 19:53:04 +02:00
|
|
|
#include "glissando.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "harmony.h"
|
2014-07-01 16:38:53 +02:00
|
|
|
#include "hook.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "input.h"
|
|
|
|
#include "limits.h"
|
|
|
|
#include "lyrics.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "note.h"
|
2014-05-30 21:44:51 +02:00
|
|
|
#include "notedot.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "page.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "rest.h"
|
|
|
|
#include "score.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "segment.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "select.h"
|
|
|
|
#include "sig.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "slur.h"
|
2014-05-30 21:44:51 +02:00
|
|
|
#include "stem.h"
|
2014-07-01 16:38:53 +02:00
|
|
|
#include "stemslash.h"
|
2014-05-26 12:10:59 +02:00
|
|
|
#include "tie.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "system.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "text.h"
|
|
|
|
#include "textline.h"
|
2014-07-08 20:22:07 +02:00
|
|
|
#include "tremolo.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "tuplet.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "xml.h"
|
2014-06-18 12:25:56 +02:00
|
|
|
#include "staff.h"
|
|
|
|
#include "part.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
|
|
|
//---------------------------------------------------------
|
|
|
|
// Selection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Selection::Selection(Score* s)
|
|
|
|
{
|
|
|
|
_score = s;
|
2014-05-26 13:21:04 +02:00
|
|
|
_state = SelState::NONE;
|
2012-05-26 14:26:10 +02:00
|
|
|
_startSegment = 0;
|
|
|
|
_endSegment = 0;
|
|
|
|
_activeSegment = 0;
|
2014-08-15 17:20:20 +02:00
|
|
|
_staffStart = 0;
|
|
|
|
_staffEnd = 0;
|
|
|
|
_activeTrack = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tickStart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Selection::tickStart() const
|
|
|
|
{
|
2014-08-02 15:08:43 +02:00
|
|
|
switch (_state) {
|
|
|
|
case SelState::RANGE:
|
|
|
|
return _startSegment->tick();
|
|
|
|
case SelState::LIST: {
|
|
|
|
ChordRest* cr = firstChordRest();
|
|
|
|
return (cr) ? cr->tick() : -1;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tickEnd
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Selection::tickEnd() const
|
|
|
|
{
|
2014-08-02 15:08:43 +02:00
|
|
|
switch (_state) {
|
|
|
|
case SelState::RANGE: {
|
|
|
|
if (_endSegment) {
|
|
|
|
return _endSegment->tick();
|
|
|
|
}
|
|
|
|
else { // endsegment == 0 if end of score
|
|
|
|
Measure* m = _score->lastMeasure();
|
|
|
|
return m->tick() + m->ticks();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SelState::LIST: {
|
|
|
|
ChordRest* cr = lastChordRest();
|
|
|
|
return (cr) ? cr->tick() : -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return -1;
|
2013-08-01 10:53:14 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// isStartActive
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Selection::isStartActive() const
|
|
|
|
{
|
|
|
|
return activeSegment() && activeSegment()->tick() == tickStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// isEndActive
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Selection::isEndActive() const {
|
|
|
|
return activeSegment() && activeSegment()->tick() == tickEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// element
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Element* Selection::element() const
|
|
|
|
{
|
|
|
|
return _el.size() == 1 ? _el[0] : 0;
|
|
|
|
}
|
2014-11-22 13:05:23 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cr
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Selection::cr() const
|
|
|
|
{
|
|
|
|
Element* e = element();
|
2015-01-20 03:09:00 +01:00
|
|
|
if (!e)
|
|
|
|
return 0;
|
2014-11-22 13:05:23 +01:00
|
|
|
if (e->type() == Element::Type::NOTE)
|
|
|
|
e = e->parent();
|
|
|
|
if (e->isChordRest())
|
|
|
|
return static_cast<ChordRest*>(e);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// activeCR
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Selection::activeCR() const
|
|
|
|
{
|
2014-05-26 13:21:04 +02:00
|
|
|
if ((_state != SelState::RANGE) || !_activeSegment)
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
if (_activeSegment == _startSegment)
|
|
|
|
return firstChordRest(_activeTrack);
|
|
|
|
else
|
|
|
|
return lastChordRest(_activeTrack);
|
|
|
|
}
|
|
|
|
|
2014-06-13 22:02:26 +02:00
|
|
|
Segment* Selection::firstChordRestSegment() const
|
|
|
|
{
|
|
|
|
if (!isRange()) return 0;
|
|
|
|
|
|
|
|
for (Segment* s = _startSegment; s && (s != _endSegment); s = s->next1MM()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (s->segmentType() == Segment::Type::ChordRest)
|
2014-06-13 22:02:26 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstChordRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Selection::firstChordRest(int track) const
|
|
|
|
{
|
2012-11-23 15:22:16 +01:00
|
|
|
if (_el.size() == 1) {
|
|
|
|
Element* el = _el[0];
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2012-11-23 15:22:16 +01:00
|
|
|
return static_cast<ChordRest*>(el->parent());
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (el->type() == Element::Type::REST)
|
2012-11-23 15:22:16 +01:00
|
|
|
return static_cast<ChordRest*>(el);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* cr = 0;
|
|
|
|
foreach (Element* el, _el) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
el = el->parent();
|
|
|
|
if (el->isChordRest()) {
|
|
|
|
if (track != -1 && el->track() != track)
|
|
|
|
continue;
|
|
|
|
if (cr) {
|
|
|
|
if (static_cast<ChordRest*>(el)->tick() < cr->tick())
|
|
|
|
cr = static_cast<ChordRest*>(el);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cr = static_cast<ChordRest*>(el);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// lastChordRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Selection::lastChordRest(int track) const
|
|
|
|
{
|
2012-11-23 15:22:16 +01:00
|
|
|
if (_el.size() == 1) {
|
|
|
|
Element* el = _el[0];
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el && el->type() == Element::Type::NOTE)
|
2012-11-23 15:22:16 +01:00
|
|
|
return static_cast<ChordRest*>(el->parent());
|
2014-11-27 11:32:35 +01:00
|
|
|
else if (el->type() == Element::Type::CHORD || el->type() == Element::Type::REST || el->type() == Element::Type::REPEAT_MEASURE)
|
2012-11-23 15:22:16 +01:00
|
|
|
return static_cast<ChordRest*>(el);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* cr = 0;
|
2014-11-22 13:05:23 +01:00
|
|
|
for (auto el : _el) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
el = ((Note*)el)->chord();
|
2014-06-25 11:46:10 +02:00
|
|
|
if (el->isChordRest() && static_cast<ChordRest*>(el)->segment()->segmentType() == Segment::Type::ChordRest) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (track != -1 && el->track() != track)
|
|
|
|
continue;
|
|
|
|
if (cr) {
|
|
|
|
if (((ChordRest*)el)->tick() >= cr->tick())
|
|
|
|
cr = (ChordRest*)el;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cr = (ChordRest*)el;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2013-04-13 23:53:44 +02:00
|
|
|
// findMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Selection::findMeasure() const
|
|
|
|
{
|
|
|
|
Measure *m = 0;
|
2013-04-16 16:06:32 +02:00
|
|
|
if (_el.size() > 0) {
|
2013-04-13 23:53:44 +02:00
|
|
|
Element* el = _el[0];
|
|
|
|
m = static_cast<Measure*>(el->findMeasure());
|
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2012-05-26 14:26:10 +02:00
|
|
|
// deselectAll
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::deselectAll()
|
|
|
|
{
|
2014-05-26 13:21:04 +02:00
|
|
|
if (_state == SelState::RANGE)
|
2012-05-26 14:26:10 +02:00
|
|
|
_score->setUpdateAll();
|
|
|
|
clear();
|
|
|
|
updateState();
|
|
|
|
}
|
|
|
|
|
2014-11-22 13:05:23 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// changeSelection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static QRectF changeSelection(Element* e, bool b)
|
|
|
|
{
|
|
|
|
QRectF r = e->canvasBoundingRect();
|
|
|
|
e->setSelected(b);
|
|
|
|
r |= e->canvasBoundingRect();
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// clear
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::clear()
|
|
|
|
{
|
2014-11-22 13:05:23 +01:00
|
|
|
for (Element* e : _el) {
|
|
|
|
if (e->isSpanner()) { // TODO: only visible elements should be selectable?
|
|
|
|
Spanner* sp = static_cast<Spanner*>(e);
|
|
|
|
for (auto s : sp->spannerSegments())
|
|
|
|
e->score()->addRefresh(changeSelection(s, false));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
e->score()->addRefresh(changeSelection(e, false));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
_el.clear();
|
2013-02-08 00:28:15 +01:00
|
|
|
_startSegment = 0;
|
|
|
|
_endSegment = 0;
|
|
|
|
_activeSegment = 0;
|
2014-08-15 17:20:20 +02:00
|
|
|
_staffStart = 0;
|
|
|
|
_staffEnd = 0;
|
|
|
|
_activeTrack = 0;
|
2014-05-26 13:21:04 +02:00
|
|
|
setState(SelState::NONE);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// remove
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::remove(Element* el)
|
|
|
|
{
|
|
|
|
_el.removeOne(el);
|
|
|
|
el->setSelected(false);
|
|
|
|
updateState();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// add
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::add(Element* el)
|
|
|
|
{
|
|
|
|
_el.append(el);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2014-08-07 00:30:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// canSelect
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-24 12:06:12 +02:00
|
|
|
bool SelectionFilter::canSelect(const Element* e) const
|
2014-06-03 21:56:18 +02:00
|
|
|
{
|
2014-07-02 23:53:18 +02:00
|
|
|
if (e->type() == Element::Type::DYNAMIC || e->type() == Element::Type::HAIRPIN)
|
|
|
|
return isFiltered(SelectionFilterType::DYNAMIC);
|
|
|
|
if (e->type() == Element::Type::ARTICULATION || e->type() == Element::Type::TRILL)
|
|
|
|
return isFiltered(SelectionFilterType::ARTICULATION);
|
|
|
|
if (e->type() == Element::Type::LYRICS)
|
|
|
|
return isFiltered(SelectionFilterType::LYRICS);
|
|
|
|
if (e->type() == Element::Type::FINGERING)
|
|
|
|
return isFiltered(SelectionFilterType::FINGERING);
|
|
|
|
if (e->type() == Element::Type::HARMONY)
|
|
|
|
return isFiltered(SelectionFilterType::CHORD_SYMBOL);
|
|
|
|
if (e->type() == Element::Type::SLUR)
|
|
|
|
return isFiltered(SelectionFilterType::SLUR);
|
|
|
|
if (e->type() == Element::Type::FIGURED_BASS)
|
|
|
|
return isFiltered(SelectionFilterType::FIGURED_BASS);
|
|
|
|
if (e->type() == Element::Type::OTTAVA)
|
|
|
|
return isFiltered(SelectionFilterType::OTTAVA);
|
|
|
|
if (e->type() == Element::Type::PEDAL)
|
|
|
|
return isFiltered(SelectionFilterType::PEDAL_LINE);
|
|
|
|
if (e->type() == Element::Type::ARPEGGIO)
|
|
|
|
return isFiltered(SelectionFilterType::ARPEGGIO);
|
|
|
|
if (e->type() == Element::Type::GLISSANDO)
|
|
|
|
return isFiltered(SelectionFilterType::GLISSANDO);
|
|
|
|
if (e->type() == Element::Type::FRET_DIAGRAM)
|
|
|
|
return isFiltered(SelectionFilterType::FRET_DIAGRAM);
|
|
|
|
if (e->type() == Element::Type::BREATH)
|
|
|
|
return isFiltered(SelectionFilterType::BREATH);
|
2014-08-07 00:30:50 +02:00
|
|
|
if (e->isText()) // only TEXT, INSTRCHANGE and STAFFTEXT are caught here, rest are system thus not in selection
|
2014-07-02 23:53:18 +02:00
|
|
|
return isFiltered(SelectionFilterType::OTHER_TEXT);
|
2014-07-03 23:57:08 +02:00
|
|
|
if (e->isSLine()) // NoteLine, Volta
|
|
|
|
return isFiltered(SelectionFilterType::OTHER_LINE);
|
2015-01-29 11:20:03 +01:00
|
|
|
if (e->type() == Element::Type::TREMOLO && !static_cast<const Tremolo*>(e)->twoNotes())
|
2014-07-04 18:19:52 +02:00
|
|
|
return isFiltered(SelectionFilterType::TREMOLO);
|
2014-07-12 16:26:25 +02:00
|
|
|
if (e->type() == Element::Type::CHORD && static_cast<const Chord*>(e)->isGrace())
|
|
|
|
return isFiltered(SelectionFilterType::GRACE_NOTE);
|
2014-06-03 21:56:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-07 00:30:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// canSelectVoice
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SelectionFilter::canSelectVoice(int track) const
|
|
|
|
{
|
|
|
|
int voice = track % VOICES;
|
|
|
|
switch (voice) {
|
|
|
|
case 0: return isFiltered(SelectionFilterType::FIRST_VOICE);
|
|
|
|
case 1: return isFiltered(SelectionFilterType::SECOND_VOICE);
|
|
|
|
case 2: return isFiltered(SelectionFilterType::THIRD_VOICE);
|
|
|
|
case 3: return isFiltered(SelectionFilterType::FOURTH_VOICE);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendFiltered
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-15 12:23:45 +02:00
|
|
|
void Selection::appendFiltered(Element* e)
|
|
|
|
{
|
|
|
|
if (selectionFilter().canSelect(e))
|
|
|
|
_el.append(e);
|
|
|
|
}
|
|
|
|
|
2014-08-07 00:30:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// appendChord
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-01 16:38:53 +02:00
|
|
|
void Selection::appendChord(Chord* chord)
|
|
|
|
{
|
2015-02-24 09:16:56 +01:00
|
|
|
if (chord->beam() && !_el.contains(chord->beam())) _el.append(chord->beam());
|
2014-07-01 16:38:53 +02:00
|
|
|
if (chord->stem()) _el.append(chord->stem());
|
|
|
|
if (chord->hook()) _el.append(chord->hook());
|
|
|
|
if (chord->arpeggio()) appendFiltered(chord->arpeggio());
|
2014-12-13 17:40:39 +01:00
|
|
|
// if (chord->glissando()) appendFiltered(chord->glissando());
|
2014-07-01 16:38:53 +02:00
|
|
|
if (chord->stemSlash()) _el.append(chord->stemSlash());
|
2014-07-04 18:19:52 +02:00
|
|
|
if (chord->tremolo()) appendFiltered(chord->tremolo());
|
2014-07-01 16:38:53 +02:00
|
|
|
foreach(Note* note, chord->notes()) {
|
|
|
|
_el.append(note);
|
|
|
|
if (note->accidental()) _el.append(note->accidental());
|
|
|
|
foreach(Element* el, note->el())
|
|
|
|
appendFiltered(el);
|
2014-08-12 14:48:29 +02:00
|
|
|
for (int x = 0; x < MAX_DOTS; x++)
|
2014-07-01 16:38:53 +02:00
|
|
|
if (note->dot(x) != 0) _el.append(note->dot(x));
|
|
|
|
|
|
|
|
if (note->tieFor() && (note->tieFor()->endElement() != 0)) {
|
|
|
|
if (note->tieFor()->endElement()->type() == Element::Type::NOTE) {
|
|
|
|
Note* endNote = static_cast<Note*>(note->tieFor()->endElement());
|
|
|
|
Segment* s = endNote->chord()->segment();
|
|
|
|
if (_endSegment && (s->tick() < _endSegment->tick()))
|
|
|
|
_el.append(note->tieFor());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateSelectedElements
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::updateSelectedElements()
|
|
|
|
{
|
|
|
|
foreach(Element* e, _el)
|
|
|
|
e->setSelected(false);
|
|
|
|
_el.clear();
|
|
|
|
|
|
|
|
// assert:
|
|
|
|
int staves = _score->nstaves();
|
|
|
|
if (_staffStart < 0 || _staffStart >= staves || _staffEnd < 0 || _staffEnd > staves
|
|
|
|
|| _staffStart >= _staffEnd) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("updateSelectedElements: bad staff selection %d - %d, staves %d", _staffStart, _staffEnd, staves);
|
2012-05-26 14:26:10 +02:00
|
|
|
_staffStart = 0;
|
|
|
|
_staffEnd = 0;
|
|
|
|
}
|
|
|
|
int startTrack = _staffStart * VOICES;
|
|
|
|
int endTrack = _staffEnd * VOICES;
|
2014-06-01 20:24:29 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int st = startTrack; st < endTrack; ++st) {
|
2014-08-07 00:30:50 +02:00
|
|
|
if (!canSelectVoice(st))
|
|
|
|
continue;
|
2013-09-27 18:43:25 +02:00
|
|
|
for (Segment* s = _startSegment; s && (s != _endSegment); s = s->next1MM()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (s->segmentType() == Segment::Type::EndBarLine) // do not select end bar line
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2014-11-23 17:30:23 +01:00
|
|
|
foreach(Element* e, s->annotations()) {
|
|
|
|
if (e->track() != st)
|
|
|
|
continue;
|
|
|
|
if (e->systemFlag()) //exclude system text
|
|
|
|
continue;
|
|
|
|
appendFiltered(e);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
Element* e = s->element(st);
|
|
|
|
if (!e)
|
|
|
|
continue;
|
2014-08-20 21:05:05 +02:00
|
|
|
if (e->generated())
|
|
|
|
continue;
|
|
|
|
if (e->type() == Element::Type::TIMESIG)
|
|
|
|
continue;
|
2015-03-19 15:07:50 +01:00
|
|
|
if (e->type() == Element::Type::KEYSIG)
|
|
|
|
continue;
|
2014-06-06 22:09:21 +02:00
|
|
|
if (e->isChordRest()) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
2014-07-09 18:16:16 +02:00
|
|
|
for (Element* e : cr->lyricsList()) {
|
2014-06-15 12:23:45 +02:00
|
|
|
if (e)
|
|
|
|
appendFiltered(e);
|
2014-07-09 18:16:16 +02:00
|
|
|
}
|
2014-06-15 12:23:45 +02:00
|
|
|
foreach (Articulation* art, cr->articulations())
|
|
|
|
appendFiltered(art);
|
2014-06-06 22:09:21 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::CHORD) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Chord* chord = static_cast<Chord*>(e);
|
2014-07-01 16:38:53 +02:00
|
|
|
for (Chord* graceNote : chord->graceNotes())
|
2014-08-12 14:48:29 +02:00
|
|
|
if (canSelect(graceNote)) appendChord(graceNote);
|
2014-07-01 16:38:53 +02:00
|
|
|
appendChord(chord);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-06-15 12:23:45 +02:00
|
|
|
appendFiltered(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2014-05-30 21:44:51 +02:00
|
|
|
}
|
2014-06-26 13:54:02 +02:00
|
|
|
int stick = startSegment()->tick();
|
|
|
|
int etick = tickEnd();
|
|
|
|
|
|
|
|
for (auto i = score()->spanner().begin(); i != score()->spanner().end(); ++i) {
|
|
|
|
Spanner* sp = (*i).second;
|
2014-05-30 21:44:51 +02:00
|
|
|
// ignore spanners belonging to other tracks
|
|
|
|
if (sp->track() < startTrack || sp->track() >= endTrack)
|
|
|
|
continue;
|
2014-12-29 21:13:11 +01:00
|
|
|
// ignore voltas
|
|
|
|
if (sp->type() == Element::Type::VOLTA)
|
|
|
|
continue;
|
2014-07-12 16:26:25 +02:00
|
|
|
if (sp->type() == Element::Type::SLUR) {
|
|
|
|
if ((sp->tick() >= stick && sp->tick() < etick) || (sp->tick2() >= stick && sp->tick2() < etick))
|
2014-12-08 10:20:42 +01:00
|
|
|
if (canSelect(sp->startCR()) && canSelect(sp->endCR()))
|
2014-07-12 16:26:25 +02:00
|
|
|
appendFiltered(sp); // slur with start or end in range selection
|
|
|
|
}
|
2014-08-20 16:53:26 +02:00
|
|
|
else if ((sp->tick() >= stick && sp->tick() < etick) && (sp->tick2() >= stick && sp->tick2() <= etick))
|
2014-06-17 12:14:21 +02:00
|
|
|
appendFiltered(sp); // spanner with start and end in range selection
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setRange
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
void Selection::setRange(Segment* startSegment, Segment* endSegment, int staffStart, int staffEnd)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-05-24 22:24:48 +02:00
|
|
|
Q_ASSERT(staffEnd > staffStart && staffStart >= 0 && staffEnd >= 0 && staffEnd <= _score->nstaves());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-24 22:24:48 +02:00
|
|
|
_startSegment = startSegment;
|
|
|
|
_endSegment = endSegment;
|
|
|
|
_activeSegment = endSegment;
|
|
|
|
_staffStart = staffStart;
|
|
|
|
_staffEnd = staffEnd;
|
2014-05-26 13:21:04 +02:00
|
|
|
setState(SelState::RANGE);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// update
|
2012-07-31 09:48:37 +02:00
|
|
|
/// Set select flag for all Elements in select list.
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::update()
|
|
|
|
{
|
2014-11-22 13:05:23 +01:00
|
|
|
for (Element* e : _el)
|
2012-05-26 14:26:10 +02:00
|
|
|
e->setSelected(true);
|
|
|
|
updateState();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// dump
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::dump()
|
|
|
|
{
|
|
|
|
qDebug("Selection dump: ");
|
|
|
|
switch(_state) {
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::NONE: qDebug("NONE"); return;
|
|
|
|
case SelState::RANGE: qDebug("RANGE"); break;
|
|
|
|
case SelState::LIST: qDebug("LIST"); break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
foreach(const Element* e, _el)
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug(" %p %s", e, e->name());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateState
|
|
|
|
/// update selection and input state
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::updateState()
|
|
|
|
{
|
|
|
|
int n = _el.size();
|
|
|
|
Element* e = element();
|
|
|
|
if (n == 0)
|
2014-05-26 13:21:04 +02:00
|
|
|
setState(SelState::NONE);
|
|
|
|
else if (_state == SelState::NONE)
|
|
|
|
setState(SelState::LIST);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!_score->noteEntryMode())
|
2013-10-24 12:09:00 +02:00
|
|
|
_score->inputState().update(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setState
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::setState(SelState s)
|
|
|
|
{
|
2014-08-15 17:20:20 +02:00
|
|
|
_state = s;
|
|
|
|
_score->setSelectionChanged(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// mimeType
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Selection::mimeType() const
|
|
|
|
{
|
|
|
|
switch (_state) {
|
|
|
|
default:
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::NONE:
|
2012-05-26 14:26:10 +02:00
|
|
|
return QString();
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::LIST:
|
2012-05-26 14:26:10 +02:00
|
|
|
return isSingle() ? mimeSymbolFormat : mimeSymbolListFormat;
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::RANGE:
|
2012-05-26 14:26:10 +02:00
|
|
|
return mimeStaffListFormat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// mimeData
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QByteArray Selection::mimeData() const
|
|
|
|
{
|
|
|
|
QByteArray a;
|
|
|
|
switch (_state) {
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::LIST:
|
2012-05-26 14:26:10 +02:00
|
|
|
if (isSingle()) {
|
|
|
|
Element* e = element();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::TEXTLINE_SEGMENT)
|
2012-05-26 14:26:10 +02:00
|
|
|
e = static_cast<TextLineSegment*>(e)->textLine();
|
|
|
|
a = e->mimeData(QPointF());
|
|
|
|
}
|
2013-10-21 15:55:56 +02:00
|
|
|
else
|
|
|
|
a = symbolListMimeData();
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::NONE:
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-26 13:21:04 +02:00
|
|
|
case SelState::RANGE:
|
2012-05-26 14:26:10 +02:00
|
|
|
a = staffMimeData();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// hasElementInTrack
|
|
|
|
//---------------------------------------------------------
|
2014-07-28 18:40:02 +02:00
|
|
|
|
|
|
|
bool hasElementInTrack(Segment* startSeg, Segment* endSeg, int track)
|
|
|
|
{
|
|
|
|
for (Segment* seg = startSeg; seg != endSeg; seg = seg->next1MM()) {
|
|
|
|
if (seg->element(track))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstElementInTrack
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-31 08:37:58 +02:00
|
|
|
int firstElementInTrack(Segment* startSeg, Segment* endSeg, int track)
|
|
|
|
{
|
|
|
|
for (Segment* seg = startSeg; seg != endSeg; seg = seg->next1MM()) {
|
|
|
|
if (seg->element(track))
|
|
|
|
return seg->tick();
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// staffMimeData
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QByteArray Selection::staffMimeData() const
|
|
|
|
{
|
|
|
|
QBuffer buffer;
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
Xml xml(&buffer);
|
|
|
|
xml.header();
|
|
|
|
xml.clipboardmode = true;
|
2014-07-23 16:09:38 +02:00
|
|
|
xml.setFilter(selectionFilter());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
int ticks = tickEnd() - tickStart();
|
|
|
|
int staves = staffEnd() - staffStart();
|
2014-11-06 10:44:57 +01:00
|
|
|
if (!MScore::testMode) {
|
|
|
|
xml.stag(QString("StaffList version=\"" MSC_VERSION "\" tick=\"%1\" len=\"%2\" staff=\"%3\" staves=\"%4\"").arg(tickStart()).arg(ticks).arg(staffStart()).arg(staves));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
xml.stag(QString("StaffList version=\"2.00\" tick=\"%1\" len=\"%2\" staff=\"%3\" staves=\"%4\"").arg(tickStart()).arg(ticks).arg(staffStart()).arg(staves));
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment* seg1 = _startSegment;
|
|
|
|
Segment* seg2 = _endSegment;
|
|
|
|
|
|
|
|
for (int staffIdx = staffStart(); staffIdx < staffEnd(); ++staffIdx) {
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2014-07-28 18:40:02 +02:00
|
|
|
|
2014-07-31 08:37:58 +02:00
|
|
|
xml.stag(QString("Staff id=\"%1\"").arg(staffIdx));
|
2014-07-28 18:40:02 +02:00
|
|
|
|
2014-06-18 12:25:56 +02:00
|
|
|
Staff* staff = score()->staff(staffIdx);
|
|
|
|
Part* part = staff->part();
|
|
|
|
Interval interval = part->instr(seg1->tick())->transpose();
|
|
|
|
if (interval.chromatic)
|
|
|
|
xml.tag("transposeChromatic", interval.chromatic);
|
|
|
|
if (interval.diatonic)
|
|
|
|
xml.tag("transposeDiatonic", interval.diatonic);
|
2014-08-12 14:48:29 +02:00
|
|
|
for (int voice = 0; voice < VOICES; voice++) {
|
2014-08-07 00:30:50 +02:00
|
|
|
if (hasElementInTrack(seg1, seg2, startTrack + voice)
|
|
|
|
&& xml.canWriteVoice(voice)) {
|
2014-07-31 08:37:58 +02:00
|
|
|
int offset = firstElementInTrack(seg1, seg2, startTrack+voice) - tickStart();
|
|
|
|
xml.tag(QString("voice id=\"%1\"").arg(voice), offset);
|
|
|
|
}
|
|
|
|
}
|
2014-07-21 11:43:50 +02:00
|
|
|
score()->writeSegments(xml, startTrack, endTrack, seg1, seg2, false, true, true);
|
2012-05-26 14:26:10 +02:00
|
|
|
xml.etag();
|
|
|
|
}
|
|
|
|
|
|
|
|
xml.etag();
|
|
|
|
buffer.close();
|
|
|
|
return buffer.buffer();
|
|
|
|
}
|
|
|
|
|
2013-10-21 15:55:56 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// symbolListMimeData
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QByteArray Selection::symbolListMimeData() const
|
|
|
|
{
|
|
|
|
|
|
|
|
struct MAPDATA {
|
|
|
|
Element* e;
|
|
|
|
Segment* s;
|
|
|
|
};
|
|
|
|
|
|
|
|
QBuffer buffer;
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
Xml xml(&buffer);
|
|
|
|
xml.header();
|
|
|
|
xml.clipboardmode = true;
|
|
|
|
|
|
|
|
int topTrack = 1000000;
|
|
|
|
int bottomTrack = 0;
|
2014-05-31 18:42:48 +02:00
|
|
|
Segment* firstSeg = nullptr;
|
2013-10-21 15:55:56 +02:00
|
|
|
int firstTick = 0x7FFFFFFF;
|
|
|
|
MAPDATA mapData;
|
2014-05-31 18:42:48 +02:00
|
|
|
Segment* seg = nullptr;
|
2013-10-21 15:55:56 +02:00
|
|
|
int track;
|
2013-11-06 00:43:05 +01:00
|
|
|
std::multimap<qint64, MAPDATA> map;
|
2013-10-21 15:55:56 +02:00
|
|
|
|
|
|
|
// scan selection element list, inserting relevant elements in a tick-sorted map
|
|
|
|
foreach (Element* e, _el) {
|
|
|
|
switch (e->type()) {
|
|
|
|
/* All these element types are ignored:
|
|
|
|
|
|
|
|
Enabling copying of more element types requires enabling pasting in Score::pasteSymbols() in libmscore/paste.cpp
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SYMBOL:
|
|
|
|
case Element::Type::TEXT:
|
|
|
|
case Element::Type::INSTRUMENT_NAME:
|
|
|
|
case Element::Type::SLUR_SEGMENT:
|
|
|
|
case Element::Type::STAFF_LINES:
|
|
|
|
case Element::Type::BAR_LINE:
|
|
|
|
case Element::Type::STEM_SLASH:
|
|
|
|
case Element::Type::LINE:
|
|
|
|
case Element::Type::BRACKET:
|
|
|
|
case Element::Type::ARPEGGIO:
|
|
|
|
case Element::Type::ACCIDENTAL:
|
|
|
|
case Element::Type::STEM:
|
|
|
|
case Element::Type::NOTE:
|
|
|
|
case Element::Type::CLEF:
|
|
|
|
case Element::Type::KEYSIG:
|
|
|
|
case Element::Type::TIMESIG:
|
|
|
|
case Element::Type::REST:
|
|
|
|
case Element::Type::BREATH:
|
|
|
|
case Element::Type::GLISSANDO:
|
|
|
|
case Element::Type::REPEAT_MEASURE:
|
|
|
|
case Element::Type::IMAGE:
|
|
|
|
case Element::Type::TIE:
|
|
|
|
case Element::Type::CHORDLINE:
|
|
|
|
case Element::Type::DYNAMIC:
|
|
|
|
case Element::Type::BEAM:
|
|
|
|
case Element::Type::HOOK:
|
|
|
|
case Element::Type::MARKER:
|
|
|
|
case Element::Type::JUMP:
|
|
|
|
case Element::Type::FINGERING:
|
|
|
|
case Element::Type::TUPLET:
|
|
|
|
case Element::Type::TEMPO_TEXT:
|
|
|
|
case Element::Type::STAFF_TEXT:
|
|
|
|
case Element::Type::REHEARSAL_MARK:
|
|
|
|
case Element::Type::INSTRUMENT_CHANGE:
|
|
|
|
case Element::Type::FRET_DIAGRAM:
|
|
|
|
case Element::Type::BEND:
|
|
|
|
case Element::Type::TREMOLOBAR:
|
|
|
|
case Element::Type::VOLTA:
|
|
|
|
case Element::Type::HAIRPIN_SEGMENT:
|
|
|
|
case Element::Type::OTTAVA_SEGMENT:
|
|
|
|
case Element::Type::TRILL_SEGMENT:
|
|
|
|
case Element::Type::TEXTLINE_SEGMENT:
|
|
|
|
case Element::Type::VOLTA_SEGMENT:
|
|
|
|
case Element::Type::PEDAL_SEGMENT:
|
|
|
|
case Element::Type::LAYOUT_BREAK:
|
|
|
|
case Element::Type::SPACER:
|
|
|
|
case Element::Type::STAFF_STATE:
|
|
|
|
case Element::Type::LEDGER_LINE:
|
|
|
|
case Element::Type::NOTEHEAD:
|
|
|
|
case Element::Type::NOTEDOT:
|
|
|
|
case Element::Type::TREMOLO:
|
|
|
|
case Element::Type::MEASURE:
|
|
|
|
case Element::Type::SELECTION:
|
|
|
|
case Element::Type::LASSO:
|
|
|
|
case Element::Type::SHADOW_NOTE:
|
|
|
|
case Element::Type::RUBBERBAND:
|
|
|
|
case Element::Type::TAB_DURATION_SYMBOL:
|
|
|
|
case Element::Type::FSYMBOL:
|
|
|
|
case Element::Type::PAGE:
|
|
|
|
case Element::Type::HAIRPIN:
|
|
|
|
case Element::Type::OTTAVA:
|
|
|
|
case Element::Type::PEDAL:
|
|
|
|
case Element::Type::TRILL:
|
|
|
|
case Element::Type::TEXTLINE:
|
|
|
|
case Element::Type::NOTELINE:
|
|
|
|
case Element::Type::SEGMENT:
|
|
|
|
case Element::Type::SYSTEM:
|
|
|
|
case Element::Type::COMPOUND:
|
|
|
|
case Element::Type::CHORD:
|
|
|
|
case Element::Type::SLUR:
|
|
|
|
case Element::Type::ELEMENT:
|
|
|
|
case Element::Type::ELEMENT_LIST:
|
|
|
|
case Element::Type::STAFF_LIST:
|
|
|
|
case Element::Type::MEASURE_LIST:
|
|
|
|
case Element::Type::LAYOUT:
|
|
|
|
case Element::Type::HBOX:
|
|
|
|
case Element::Type::VBOX:
|
|
|
|
case Element::Type::TBOX:
|
|
|
|
case Element::Type::FBOX:
|
|
|
|
case Element::Type::ICON:
|
|
|
|
case Element::Type::OSSIA:
|
|
|
|
case Element::Type::BAGPIPE_EMBELLISHMENT:
|
2013-10-21 15:55:56 +02:00
|
|
|
continue;
|
|
|
|
*/
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ARTICULATION:
|
2013-10-21 15:55:56 +02:00
|
|
|
// ignore articulations not attached to chords/rest
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->parent()->type() == Element::Type::CHORD) {
|
2013-10-21 15:55:56 +02:00
|
|
|
Chord* par = static_cast<Chord*>( (static_cast<Articulation*>(e))->parent() );
|
|
|
|
seg = par->segment();
|
|
|
|
break;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (e->parent()->type() == Element::Type::REST) {
|
2013-10-21 15:55:56 +02:00
|
|
|
Rest* par = static_cast<Rest*>( (static_cast<Articulation*>(e))->parent() );
|
|
|
|
seg = par->segment();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::FIGURED_BASS:
|
2013-10-21 15:55:56 +02:00
|
|
|
seg = (static_cast<FiguredBass*>(e))->segment();
|
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::HARMONY:
|
2013-11-03 16:49:02 +01:00
|
|
|
// ignore chord sybols not attached to segment
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->parent()->type() == Element::Type::SEGMENT) {
|
2013-10-21 15:55:56 +02:00
|
|
|
seg = static_cast<Segment*>( (static_cast<Harmony*>(e))->parent() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::LYRICS:
|
2013-10-21 15:55:56 +02:00
|
|
|
seg = (static_cast<Lyrics*>(e))->segment();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
track = e->track();
|
|
|
|
if (track < topTrack)
|
|
|
|
topTrack = track;
|
|
|
|
if (track > bottomTrack)
|
|
|
|
bottomTrack = track;
|
|
|
|
if (seg->tick() < firstTick) {
|
|
|
|
firstSeg = seg;
|
|
|
|
firstTick = seg->tick();
|
|
|
|
}
|
|
|
|
mapData.e = e;
|
|
|
|
mapData.s = seg;
|
2013-11-06 00:43:05 +01:00
|
|
|
map.insert(std::pair<qint64,MAPDATA>( ((qint64)track << 32) + seg->tick(), mapData));
|
2013-10-21 15:55:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
xml.stag(QString("SymbolList version=\"" MSC_VERSION "\" fromtrack=\"%1\" totrack=\"%2\"")
|
|
|
|
.arg(topTrack).arg(bottomTrack));
|
|
|
|
// scan the map, outputting elements each with a relative <track> tag on track change,
|
2013-11-11 11:14:37 +01:00
|
|
|
// a relative tick and the number of CR segments to skip
|
2013-10-21 15:55:56 +02:00
|
|
|
int currTrack = -1;
|
|
|
|
for (auto iter = map.cbegin(); iter != map.cend(); ++iter) {
|
|
|
|
int numSegs;
|
|
|
|
int track = (int)(iter->first >> 32);
|
|
|
|
if (currTrack != track) {
|
|
|
|
xml.tag("trackOffset", track - topTrack);
|
|
|
|
currTrack = track;
|
|
|
|
seg = firstSeg;
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
xml.tag("tickOffset", (int)(iter->first & 0xFFFFFFFF) - firstTick);
|
2013-10-21 15:55:56 +02:00
|
|
|
numSegs = 0;
|
|
|
|
// with figured bass, we need to look for the proper segment
|
|
|
|
// not only according to ChordRest elements, but also annotations
|
2014-06-24 18:36:02 +02:00
|
|
|
if (iter->second.e->type() == Element::Type::FIGURED_BASS) {
|
2013-10-21 15:55:56 +02:00
|
|
|
bool done = false;
|
|
|
|
for ( ; seg; seg = seg->next1()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (seg->segmentType() == Segment::Type::ChordRest) {
|
2013-10-21 15:55:56 +02:00
|
|
|
// if no ChordRest in right track, look in anotations
|
|
|
|
if (seg->element(currTrack) == nullptr) {
|
|
|
|
foreach (Element* el, seg->annotations()) {
|
|
|
|
// do annotations include our element?
|
|
|
|
if (el == iter->second.e) {
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// do annotations include any f.b.?
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::FIGURED_BASS && el->track() == track) {
|
2013-10-21 15:55:56 +02:00
|
|
|
numSegs++; //yes: it counts as a step
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
continue; // segment is not relevant: no ChordRest nor f.b.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (iter->second.s == seg)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
numSegs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2013-12-26 23:29:50 +01:00
|
|
|
while (seg && iter->second.s != seg) {
|
2013-10-21 15:55:56 +02:00
|
|
|
seg = seg->nextCR(currTrack);
|
|
|
|
numSegs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xml.tag("segDelta", numSegs);
|
|
|
|
iter->second.e->write(xml);
|
|
|
|
}
|
|
|
|
|
|
|
|
xml.etag();
|
|
|
|
buffer.close();
|
|
|
|
return buffer.buffer();
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// noteList
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<Note*> Selection::noteList(int selTrack) const
|
|
|
|
{
|
|
|
|
QList<Note*>nl;
|
|
|
|
|
2014-05-26 13:21:04 +02:00
|
|
|
if (_state == SelState::LIST) {
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(Element* e, _el) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
nl.append(static_cast<Note*>(e));
|
|
|
|
}
|
|
|
|
}
|
2014-05-26 13:21:04 +02:00
|
|
|
else if (_state == SelState::RANGE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int staffIdx = staffStart(); staffIdx < staffEnd(); ++staffIdx) {
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
|
|
|
for (Segment* seg = _startSegment; seg && seg != _endSegment; seg = seg->next1()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (!(seg->segmentType() & (Segment::Type::ChordRest)))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
Element* e = seg->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e == 0 || e->type() != Element::Type::CHORD
|
2012-05-26 14:26:10 +02:00
|
|
|
|| (selTrack != -1 && selTrack != track))
|
|
|
|
continue;
|
|
|
|
Chord* c = static_cast<Chord*>(e);
|
|
|
|
nl.append(c->notes());
|
2013-08-31 17:45:28 +02:00
|
|
|
for (Chord* g : c->graceNotes()) {
|
|
|
|
nl.append(g->notes());
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nl;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// checkStart
|
2015-01-29 11:20:03 +01:00
|
|
|
// return false if element is NOT a tuplet or is start of a tuplet/tremolo
|
|
|
|
// return true if element is part of a tuplet/tremolo, but not the start
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool checkStart(Element* e)
|
|
|
|
{
|
|
|
|
if (e == 0 || !e->isChordRest())
|
|
|
|
return false;
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
2015-02-02 11:30:15 +01:00
|
|
|
bool rv = false;
|
2015-01-29 11:20:03 +01:00
|
|
|
if (cr->tuplet()) {
|
2015-03-15 15:24:00 +01:00
|
|
|
// check that complete tuplet is selected, all the way up to top level
|
2015-01-29 11:20:03 +01:00
|
|
|
Tuplet* tuplet = cr->tuplet();
|
|
|
|
while (tuplet) {
|
2015-03-14 18:28:07 +01:00
|
|
|
if (tuplet->elements().front() != e)
|
|
|
|
return true;
|
|
|
|
e = tuplet;
|
2015-01-29 11:20:03 +01:00
|
|
|
tuplet = tuplet->tuplet();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2015-03-14 22:47:11 +01:00
|
|
|
else if (cr->type() == Element::Type::CHORD) {
|
2015-01-29 11:20:03 +01:00
|
|
|
rv = false;
|
2015-03-14 22:47:11 +01:00
|
|
|
Chord* chord = static_cast<Chord*>(cr);
|
2015-01-29 11:20:03 +01:00
|
|
|
if (chord->tremolo() && chord->tremolo()->twoNotes())
|
|
|
|
rv = chord->tremolo()->chord2() == chord;
|
|
|
|
}
|
|
|
|
return rv;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// checkEnd
|
|
|
|
// return false if element is NOT a tuplet or is end of a tuplet
|
|
|
|
// return true if element is part of a tuplet, but not the end
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-03-14 22:47:11 +01:00
|
|
|
static bool checkEnd(Element* e, int endTick)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
if (e == 0 || !e->isChordRest())
|
|
|
|
return false;
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
2015-02-02 11:30:15 +01:00
|
|
|
bool rv = false;
|
2015-01-29 11:20:03 +01:00
|
|
|
if (cr->tuplet()) {
|
2015-03-15 15:24:00 +01:00
|
|
|
// check that complete tuplet is selected, all the way up to top level
|
2015-01-29 11:20:03 +01:00
|
|
|
Tuplet* tuplet = cr->tuplet();
|
|
|
|
while (tuplet) {
|
2015-03-14 18:28:07 +01:00
|
|
|
if (tuplet->elements().back() != e)
|
|
|
|
return true;
|
|
|
|
e = tuplet;
|
2015-01-29 11:20:03 +01:00
|
|
|
tuplet = tuplet->tuplet();
|
|
|
|
}
|
2015-03-14 22:47:11 +01:00
|
|
|
// also check that the selection extends to the end of the top-level tuplet
|
|
|
|
tuplet = static_cast<Tuplet*>(e);
|
|
|
|
if (tuplet->elements().first()->tick() + tuplet->actualTicks() > endTick)
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2015-03-14 22:47:11 +01:00
|
|
|
else if (cr->type() == Element::Type::CHORD) {
|
2015-01-29 11:20:03 +01:00
|
|
|
rv = false;
|
2015-03-14 22:47:11 +01:00
|
|
|
Chord* chord = static_cast<Chord*>(cr);
|
2015-01-29 11:20:03 +01:00
|
|
|
if (chord->tremolo() && chord->tremolo()->twoNotes())
|
|
|
|
rv = chord->tremolo()->chord1() == chord;
|
|
|
|
}
|
|
|
|
return rv;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// canCopy
|
|
|
|
// return false if range selection intersects a tuplet
|
2015-01-29 11:20:03 +01:00
|
|
|
// or a tremolo
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Selection::canCopy() const
|
|
|
|
{
|
2014-05-26 13:21:04 +02:00
|
|
|
if (_state != SelState::RANGE)
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
|
2015-03-14 22:47:11 +01:00
|
|
|
int endTick = _endSegment ? _endSegment->tick() : score()->lastSegment()->tick();
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int staffIdx = _staffStart; staffIdx != _staffEnd; ++staffIdx)
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
int track = staffIdx * VOICES + voice;
|
2015-03-14 22:47:11 +01:00
|
|
|
if (!canSelectVoice(track))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// check first cr in track within selection
|
|
|
|
ChordRest* check = _startSegment->nextChordRest(track);
|
|
|
|
if (check && check->tick() < endTick && checkStart(check))
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (! _endSegment)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// find last segment in the selection.
|
|
|
|
// Note that _endSegment is the first segment after the selection
|
2015-01-29 11:20:03 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment *endSegmentSelection = _startSegment;
|
|
|
|
while (endSegmentSelection->nextCR(track) &&
|
|
|
|
(endSegmentSelection->nextCR(track)->tick() < _endSegment->tick()))
|
|
|
|
endSegmentSelection = endSegmentSelection->nextCR(track);
|
|
|
|
|
2015-03-14 22:47:11 +01:00
|
|
|
if (checkEnd(endSegmentSelection->element(track), endTick))
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// measureRange
|
|
|
|
// return false if no measure range selected
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Selection::measureRange(Measure** m1, Measure** m2) const
|
|
|
|
{
|
2014-05-24 12:53:50 +02:00
|
|
|
if (!isRange())
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
*m1 = startSegment()->measure();
|
2015-01-19 20:49:52 +01:00
|
|
|
Segment* s2 = endSegment();
|
|
|
|
*m2 = s2 ? s2->measure() : score()->lastMeasure();
|
|
|
|
if (*m1 == *m2)
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
2015-01-19 20:49:52 +01:00
|
|
|
// if selection extends to last segment of a measure,
|
|
|
|
// then endSegment() will point to next measure
|
|
|
|
// this won't normally happen because end barlines are excluded from range selection
|
|
|
|
// but just in case, detect this and back up one measure
|
|
|
|
if (*m2 && s2 && (*m2)->tick() == s2->tick())
|
2012-05-26 14:26:10 +02:00
|
|
|
*m2 = (*m2)->prevMeasure();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-26 12:10:59 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// uniqueElements
|
|
|
|
// Return list of selected elements.
|
|
|
|
// If some elements are linked, only one of the linked
|
|
|
|
// elements show up in the list.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
const QList<Element*> Selection::uniqueElements() const
|
|
|
|
{
|
|
|
|
QList<Element*> l;
|
|
|
|
|
|
|
|
for (Element* e : elements()) {
|
|
|
|
bool alreadyThere = false;
|
|
|
|
for (Element* ee : l) {
|
|
|
|
if ((ee->links() && ee->links()->contains(e)) || e == ee) {
|
|
|
|
alreadyThere = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!alreadyThere)
|
|
|
|
l.append(e);
|
|
|
|
}
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// uniqueNotes
|
|
|
|
// Return list of selected notes.
|
|
|
|
// If some notes are linked, only one of the linked
|
|
|
|
// elements show up in the list.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<Note*> Selection::uniqueNotes(int track) const
|
|
|
|
{
|
|
|
|
QList<Note*> l;
|
|
|
|
|
2015-01-28 15:20:33 +01:00
|
|
|
for (Note* nn : noteList(track)) {
|
|
|
|
for (Note* note : nn->tiedNotes()) {
|
2014-05-26 12:10:59 +02:00
|
|
|
bool alreadyThere = false;
|
|
|
|
for (Note* n : l) {
|
|
|
|
if ((n->links() && n->links()->contains(note)) || n == note) {
|
|
|
|
alreadyThere = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!alreadyThere)
|
|
|
|
l.append(note);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2014-05-31 21:14:25 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// extendRangeSelection
|
|
|
|
// Extends the range selection to contain the given
|
|
|
|
// chord rest.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::extendRangeSelection(ChordRest* cr)
|
2014-05-31 22:23:23 +02:00
|
|
|
{
|
2014-06-30 11:26:58 +02:00
|
|
|
extendRangeSelection(cr->segment(),
|
|
|
|
cr->nextSegmentAfterCR(Segment::Type::ChordRest
|
|
|
|
| Segment::Type::EndBarLine
|
|
|
|
| Segment::Type::Clef),
|
|
|
|
cr->staffIdx(),
|
|
|
|
cr->tick(),
|
|
|
|
cr->tick());
|
2014-05-31 22:23:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// extendRangeSelection
|
|
|
|
// Extends the range selection to contain the given
|
|
|
|
// segment. SegAfter should represent the segment
|
|
|
|
// that is after seg. Tick and etick represent
|
|
|
|
// the start and end tick of an element. Useful when
|
|
|
|
// extending by a chord rest.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Selection::extendRangeSelection(Segment* seg, Segment* segAfter, int staffIdx, int tick, int etick)
|
2014-05-31 21:14:25 +02:00
|
|
|
{
|
2014-06-17 15:01:08 +02:00
|
|
|
bool activeIsFirst = false;
|
|
|
|
|
2014-05-31 21:14:25 +02:00
|
|
|
if (staffIdx < _staffStart)
|
|
|
|
_staffStart = staffIdx;
|
|
|
|
else if (staffIdx >= _staffEnd)
|
|
|
|
_staffEnd = staffIdx + 1;
|
|
|
|
|
|
|
|
if (tick < tickStart()) {
|
2014-05-31 22:23:23 +02:00
|
|
|
_startSegment = seg;
|
2014-05-31 21:14:25 +02:00
|
|
|
activeIsFirst = true;
|
|
|
|
}
|
2014-06-18 10:17:00 +02:00
|
|
|
else if (etick >= tickEnd()) {
|
2014-05-31 22:23:23 +02:00
|
|
|
_endSegment = segAfter;
|
2014-05-31 21:14:25 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (_activeSegment == _startSegment) {
|
2014-05-31 22:23:23 +02:00
|
|
|
_startSegment = seg;
|
2014-05-31 21:14:25 +02:00
|
|
|
activeIsFirst = true;
|
|
|
|
}
|
|
|
|
else {
|
2014-05-31 22:23:23 +02:00
|
|
|
_endSegment = segAfter;
|
2014-05-31 21:14:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
activeIsFirst ? _activeSegment = _startSegment : _activeSegment = _endSegment;
|
2014-11-03 20:09:45 +01:00
|
|
|
_score->setSelectionChanged(true);
|
2014-05-31 21:14:25 +02:00
|
|
|
}
|
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectionFilter
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-23 16:09:38 +02:00
|
|
|
SelectionFilter Selection::selectionFilter() const
|
2014-06-03 21:56:18 +02:00
|
|
|
{
|
|
|
|
return _score->selectionFilter();
|
|
|
|
}
|
2014-06-04 22:39:07 +02:00
|
|
|
|
2014-08-15 17:20:20 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setFiltered
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-04 22:39:07 +02:00
|
|
|
void SelectionFilter::setFiltered(SelectionFilterType type, bool set)
|
|
|
|
{
|
|
|
|
if (set)
|
|
|
|
_filtered = _filtered | (int)type;
|
|
|
|
else
|
|
|
|
_filtered = _filtered & ~(int)type;
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|