MuseScore/libmscore/dynamichairpingroup.cpp
Dmitri Ovodok 55230ca86d Add a way to handle dragging elements collectively
Also draw anchor lines for multiple selected elements on dragging
2020-04-17 00:10:39 +03:00

190 lines
6.2 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA
//
// 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
//=============================================================================
#include "dynamichairpingroup.h"
#include "dynamic.h"
#include "hairpin.h"
#include "score.h"
#include "segment.h"
namespace Ms {
static std::pair<Hairpin*, Hairpin*> findAdjacentHairpins(Dynamic* d)
{
Score* score = d->score();
const Segment* dSeg = d->segment();
Hairpin* leftHairpin = nullptr;
Hairpin* rightHairpin = nullptr;
const Fraction tick = dSeg->tick();
const int intTick = tick.ticks();
const auto& nearSpanners = score->spannerMap().findOverlapping(intTick - 1, intTick + 1);
for (auto i : nearSpanners) {
Spanner* s = i.value;
if (s->track() == d->track() && s->isHairpin()) {
Hairpin* h = toHairpin(s);
if (h->tick() == tick)
rightHairpin = h;
else if (h->tick2() == tick)
leftHairpin = h;
}
}
return { leftHairpin, rightHairpin };
}
std::unique_ptr<ElementGroup> HairpinWithDynamicsDragGroup::detectFor(HairpinSegment* hs, std::function<bool(const Element*)> isDragged)
{
if (!hs->isSingleType())
return nullptr;
Hairpin* hairpin = hs->hairpin();
Segment* startSegment = hairpin->startSegment();
Segment* endSegment = hairpin->endSegment();
const int track = hs->track();
Dynamic* startDynamic = toDynamic(startSegment->findAnnotation(ElementType::DYNAMIC, track, track));
Dynamic* endDynamic = toDynamic(endSegment->findAnnotation(ElementType::DYNAMIC, track, track));
// Include only dragged dynamics to this group
if (!isDragged(startDynamic))
startDynamic = nullptr;
if (!isDragged(endDynamic))
endDynamic = nullptr;
if (startDynamic || endDynamic)
return std::unique_ptr<ElementGroup>(new HairpinWithDynamicsDragGroup(startDynamic, hs, endDynamic));
return nullptr;
}
std::unique_ptr<ElementGroup> HairpinWithDynamicsDragGroup::detectFor(Dynamic* d, std::function<bool(const Element*)> isDragged)
{
Hairpin* leftHairpin = nullptr;
Hairpin* rightHairpin = nullptr;
std::tie(leftHairpin, rightHairpin) = findAdjacentHairpins(d);
// Dynamic will be governed bt HairpinWithDynamicsDragGroup if any of adjacent
// hairpins is dragged, disable separate drag logic for dynamic in this case.
if (isDragged(leftHairpin))
return std::unique_ptr<ElementGroup>(new DisabledElementGroup());
if (isDragged(rightHairpin))
return std::unique_ptr<ElementGroup>(new DisabledElementGroup());
return nullptr;
}
void HairpinWithDynamicsDragGroup::startDrag(EditData& ed)
{
if (startDynamic)
startDynamic->startDrag(ed);
static_cast<Element*>(hairpinSegment)->startDrag(ed);
if (endDynamic)
endDynamic->startDrag(ed);
}
QRectF HairpinWithDynamicsDragGroup::drag(EditData& ed)
{
QRectF r;
if (startDynamic)
r |= static_cast<Element*>(startDynamic)->drag(ed);
r |= hairpinSegment->drag(ed);
if (endDynamic)
r |= static_cast<Element*>(endDynamic)->drag(ed);
Hairpin* h = hairpinSegment->hairpin();
const Fraction startTick = startDynamic ? startDynamic->segment()->tick() : h->tick();
const Fraction endTick = endDynamic ? endDynamic->segment()->tick() : h->tick2();
if (endTick > startTick) {
if (h->tick() != startTick)
h->undoChangeProperty(Pid::SPANNER_TICK, startTick);
if (h->tick2() != endTick)
h->undoChangeProperty(Pid::SPANNER_TICKS, endTick - startTick);
}
return r;
}
void HairpinWithDynamicsDragGroup::endDrag(EditData& ed)
{
if (startDynamic) {
startDynamic->endDrag(ed);
startDynamic->triggerLayout();
}
hairpinSegment->endDrag(ed);
hairpinSegment->triggerLayout();
if (endDynamic) {
endDynamic->endDrag(ed);
endDynamic->triggerLayout();
}
}
std::unique_ptr<ElementGroup> DynamicNearHairpinsDragGroup::detectFor(Dynamic* d, std::function<bool(const Element*)> isDragged)
{
Hairpin* leftHairpin = nullptr;
Hairpin* rightHairpin = nullptr;
std::tie(leftHairpin, rightHairpin) = findAdjacentHairpins(d);
// Drag hairpins according to this rule only if they are not being dragged themselves
if (isDragged(leftHairpin))
leftHairpin = nullptr;
if (isDragged(rightHairpin))
rightHairpin = nullptr;
if (leftHairpin || rightHairpin)
return std::unique_ptr<ElementGroup>(new DynamicNearHairpinsDragGroup(leftHairpin, d, rightHairpin));
return nullptr;
}
void DynamicNearHairpinsDragGroup::startDrag(EditData& ed)
{
dynamic->startDrag(ed);
}
QRectF DynamicNearHairpinsDragGroup::drag(EditData& ed)
{
QRectF r(static_cast<Element*>(dynamic)->drag(ed));
const Fraction tick = dynamic->segment()->tick();
if (leftHairpin && leftHairpin->tick2() != tick && tick > leftHairpin->tick())
leftHairpin->undoChangeProperty(Pid::SPANNER_TICKS, tick - leftHairpin->tick());
if (rightHairpin && rightHairpin->tick() != tick) {
const Fraction tick2 = rightHairpin->tick2();
if (tick < tick2) {
rightHairpin->undoChangeProperty(Pid::SPANNER_TICK, tick);
rightHairpin->undoChangeProperty(Pid::SPANNER_TICKS, tick2 - tick);
}
}
if (leftHairpin || rightHairpin)
dynamic->triggerLayout();
return r;
}
void DynamicNearHairpinsDragGroup::endDrag(EditData& ed)
{
dynamic->endDrag(ed);
dynamic->triggerLayout();
}
} // namespace Ms