MuseScore/src/libmscore/input.cpp
2021-04-19 17:08:37 +02:00

322 lines
9.2 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "input.h"
#include "segment.h"
#include "part.h"
#include "staff.h"
#include "stem.h"
#include "hook.h"
#include "score.h"
#include "chord.h"
#include "rest.h"
#include "measure.h"
#include "accidental.h"
#include "durationtype.h"
#include "select.h"
#include "articulation.h"
namespace Ms {
class DrumSet;
//---------------------------------------------------------
// drumset
//---------------------------------------------------------
const Drumset* InputState::drumset() const
{
if (_segment == 0 || _track == -1) {
return 0;
}
return _segment->score()->staff(_track / VOICES)->part()->instrument(_segment->tick())->drumset();
}
//---------------------------------------------------------
// staffGroup
//---------------------------------------------------------
StaffGroup InputState::staffGroup() const
{
if (_segment == 0 || _track == -1) {
return StaffGroup::STANDARD;
}
StaffGroup staffGroup = _segment->score()->staff(_track / VOICES)->staffType(_segment->tick())->group();
Instrument* instrument = _segment->score()->staff(_track / VOICES)->part()->instrument(_segment->tick());
// if not tab, pitched/unpitched input depends on instrument, not staff (override StaffGroup)
if (staffGroup != StaffGroup::TAB) {
staffGroup = instrument->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
}
return staffGroup;
}
//---------------------------------------------------------
// tick
//---------------------------------------------------------
Fraction InputState::tick() const
{
return _segment ? _segment->tick() : Fraction(0, 1);
}
//---------------------------------------------------------
// cr
//---------------------------------------------------------
ChordRest* InputState::cr() const
{
// _track could potentially be -1, for instance after navigation through a frame
return _segment && _track >= 0 ? toChordRest(_segment->element(_track)) : 0;
}
//---------------------------------------------------------
// setDots
//---------------------------------------------------------
void InputState::setDots(int n)
{
if (n && (!_duration.isValid() || _duration.isZero() || _duration.isMeasure())) {
_duration = TDuration::DurationType::V_QUARTER;
}
_duration.setDots(n);
}
//---------------------------------------------------------
// note
//---------------------------------------------------------
Note* InputState::note(Element* e)
{
return e && e->isNote() ? toNote(e) : nullptr;
}
//---------------------------------------------------------
// chordRest
//---------------------------------------------------------
ChordRest* InputState::chordRest(Element* e)
{
if (!e) {
return nullptr;
}
if (e->isChordRest()) {
return toChordRest(e);
}
if (e->isNote()) {
return toNote(e)->chord();
}
if (e->isStem()) {
return toStem(e)->chord();
}
if (e->isHook()) {
return toHook(e)->chord();
}
return nullptr;
}
//---------------------------------------------------------
// update
//---------------------------------------------------------
void InputState::update(Selection& selection)
{
setDuration(TDuration::DurationType::V_INVALID);
setRest(false);
setAccidentalType(AccidentalType::NONE);
Note* n1 = nullptr;
ChordRest* cr1 = nullptr;
bool differentAccidentals = false;
bool differentDurations = false;
bool chordsAndRests = false;
std::set<SymId> articulationSymbolIds;
for (Element* e : selection.elements()) {
if (Note* n = note(e)) {
if (n1) {
if (n->accidentalType() != n1->accidentalType()) {
setAccidentalType(AccidentalType::NONE);
differentAccidentals = true;
}
std::set<SymId> articulationsIds;
for (Articulation* articulation: n->chord()->articulations()) {
articulationsIds.insert(articulation->symId());
}
articulationsIds = Ms::splitArticulations(articulationsIds);
articulationsIds = Ms::flipArticulations(articulationsIds, Ms::Placement::ABOVE);
for (const SymId& articulationSymbolId: articulationsIds) {
if (std::find(articulationSymbolIds.begin(), articulationSymbolIds.end(),
articulationSymbolId) == articulationSymbolIds.end()) {
articulationSymbolIds.erase(articulationSymbolId);
}
}
} else {
setAccidentalType(n->accidentalType());
std::set<SymId> articulationsIds;
for (Articulation* articulation: n->chord()->articulations()) {
articulationsIds.insert(articulation->symId());
}
articulationSymbolIds = Ms::flipArticulations(articulationsIds, Ms::Placement::ABOVE);
n1 = n;
}
}
if (ChordRest* cr = chordRest(e)) {
if (cr1) {
if (cr->durationType() != cr1->durationType()) {
setDuration(TDuration::DurationType::V_INVALID);
differentDurations = true;
}
if ((cr->isRest() && !cr1->isRest()) || (!cr->isRest() && cr1->isRest())) {
setRest(false);
chordsAndRests = true;
}
} else {
setDuration(cr->durationType());
setRest(cr->isRest());
cr1 = cr;
}
}
if (differentAccidentals && differentDurations && chordsAndRests) {
break;
}
}
setArticulationIds(joinArticulations(articulationSymbolIds));
Element* e = selection.element();
if (e == 0) {
return;
}
ChordRest* cr = chordRest(e);
Note* n = note(e);
if (!n && cr && cr->isChord()) {
n = toChord(cr)->upNote();
}
if (cr) {
setTrack(cr->track());
setNoteType(n ? n->noteType() : NoteType::NORMAL);
setBeamMode(cr->beamMode());
}
setDrumNote(-1);
if (n) {
const Instrument* instr = n->part()->instrument(n->tick());
if (instr->useDrumset()) {
setDrumNote(n->pitch());
}
}
}
//---------------------------------------------------------
// moveInputPos
//---------------------------------------------------------
void InputState::moveInputPos(Element* e)
{
if (e == 0) {
return;
}
Segment* s;
if (e->isChordRest()) {
s = toChordRest(e)->segment();
} else {
s = toSegment(e);
}
if (s->isSegment()) {
if (s->measure()->isMMRest()) {
Measure* m = s->measure()->mmRestFirst();
s = m->findSegment(SegmentType::ChordRest, m->tick());
}
_lastSegment = _segment;
_segment = s;
}
}
//---------------------------------------------------------
// setSegment
//---------------------------------------------------------
void InputState::setSegment(Segment* s)
{
if (s && s->measure()->isMMRest()) {
Measure* m = s->measure()->mmRestFirst();
s = m->findSegment(SegmentType::ChordRest, m->tick());
}
_segment = s;
_lastSegment = s;
}
//---------------------------------------------------------
// nextInputPos
//---------------------------------------------------------
Segment* InputState::nextInputPos() const
{
Measure* m = _segment->measure();
Segment* s = _segment->next1(SegmentType::ChordRest);
for (; s; s = s->next1(SegmentType::ChordRest)) {
if (s->element(_track)) {
if (s->element(_track)->isRest() && toRest(s->element(_track))->isGap()) {
m = s->measure();
} else {
return s;
}
} else if (s->measure() != m) {
return s;
}
}
return 0;
}
//---------------------------------------------------------
// moveToNextInputPos
// TODO: special case: note is first note of tie: goto to last note of tie
//---------------------------------------------------------
void InputState::moveToNextInputPos()
{
Segment* s = nextInputPos();
_lastSegment = _segment;
if (s) {
_segment = s;
}
}
//---------------------------------------------------------
// endOfScore
//---------------------------------------------------------
bool InputState::endOfScore() const
{
return (_lastSegment == _segment) && !nextInputPos();
}
}