diff options
Diffstat (limited to 'src/gui/editors/notation/NotationSelector.cpp')
-rw-r--r-- | src/gui/editors/notation/NotationSelector.cpp | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/src/gui/editors/notation/NotationSelector.cpp b/src/gui/editors/notation/NotationSelector.cpp new file mode 100644 index 0000000..221fbe3 --- /dev/null +++ b/src/gui/editors/notation/NotationSelector.cpp @@ -0,0 +1,957 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "NotationSelector.h" +#include "misc/Debug.h" + +#include <klocale.h> +#include "base/Event.h" +#include "base/NotationTypes.h" +#include "base/PropertyName.h" +#include "base/Selection.h" +#include "base/ViewElement.h" +#include "base/BaseProperties.h" +#include "commands/edit/MoveAcrossSegmentsCommand.h" +#include "commands/edit/MoveCommand.h" +#include "commands/edit/TransposeCommand.h" +#include "commands/notation/IncrementDisplacementsCommand.h" +#include "gui/general/EditTool.h" +#include "gui/general/GUIPalette.h" +#include "gui/general/LinedStaff.h" +#include "gui/general/RosegardenCanvasView.h" +#include "gui/kdeext/QCanvasSimpleSprite.h" +#include "NotationElement.h" +#include "NotationProperties.h" +#include "NotationStaff.h" +#include "NotationTool.h" +#include "NotationView.h" +#include "NotePixmapFactory.h" +#include <kaction.h> +#include <qapplication.h> +#include <qiconset.h> +#include <qrect.h> +#include <qstring.h> +#include <qtimer.h> + + +namespace Rosegarden +{ + +using namespace BaseProperties; + +NotationSelector::NotationSelector(NotationView* view) + : NotationTool("NotationSelector", view), + m_selectionRect(0), + m_updateRect(false), + m_selectedStaff(0), + m_clickedElement(0), + m_selectionToMerge(0), + m_justSelectedBar(false), + m_wholeStaffSelectionComplete(false) +{ + connect(m_parentView, SIGNAL(usedSelection()), + this, SLOT(slotHideSelection())); + + connect(this, SIGNAL(editElement(NotationStaff *, NotationElement *, bool)), + m_parentView, SLOT(slotEditElement(NotationStaff *, NotationElement *, bool))); + + QIconSet icon + (NotePixmapFactory::toQPixmap(NotePixmapFactory:: + makeToolbarPixmap("crotchet"))); + new KToggleAction(i18n("Switch to Insert Tool"), icon, 0, this, + SLOT(slotInsertSelected()), actionCollection(), + "insert"); + + new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this, + SLOT(slotEraseSelected()), actionCollection(), + "erase"); + + // (this crashed, and it might be superfluous with ^N anyway, so I'm + // commenting it out, but leaving it here in case I change my mind about + // fooling with it.) (DMM) + // new KAction(i18n("Normalize Rests"), 0, 0, this, + // SLOT(slotCollapseRests()), actionCollection(), + // "collapse_rests"); + + new KAction(i18n("Collapse Rests"), 0, 0, this, + SLOT(slotCollapseRestsHard()), actionCollection(), + "collapse_rests_aggressively"); + + new KAction(i18n("Respell as Flat"), 0, 0, this, + SLOT(slotRespellFlat()), actionCollection(), + "respell_flat"); + + new KAction(i18n("Respell as Sharp"), 0, 0, this, + SLOT(slotRespellSharp()), actionCollection(), + "respell_sharp"); + + new KAction(i18n("Respell as Natural"), 0, 0, this, + SLOT(slotRespellNatural()), actionCollection(), + "respell_natural"); + + new KAction(i18n("Collapse Notes"), 0, 0, this, + SLOT(slotCollapseNotes()), actionCollection(), + "collapse_notes"); + + new KAction(i18n("Interpret"), 0, 0, this, + SLOT(slotInterpret()), actionCollection(), + "interpret"); + + new KAction(i18n("Move to Staff Above"), 0, 0, this, + SLOT(slotStaffAbove()), actionCollection(), + "move_events_up_staff"); + + new KAction(i18n("Move to Staff Below"), 0, 0, this, + SLOT(slotStaffBelow()), actionCollection(), + "move_events_down_staff"); + + new KAction(i18n("Make Invisible"), 0, 0, this, + SLOT(slotMakeInvisible()), actionCollection(), + "make_invisible"); + + new KAction(i18n("Make Visible"), 0, 0, this, + SLOT(slotMakeVisible()), actionCollection(), + "make_visible"); + + createMenu("notationselector.rc"); +} + +NotationSelector::~NotationSelector() +{ + delete m_selectionToMerge; +} + +void NotationSelector::handleLeftButtonPress(timeT t, + int height, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + std::cerr << "NotationSelector::handleMousePress: time is " << t << ", staffNo is " + << staffNo << ", e and element are " << e << " and " << element << std::endl; + + if (m_justSelectedBar) { + handleMouseTripleClick(t, height, staffNo, e, element); + m_justSelectedBar = false; + return ; + } + + m_wholeStaffSelectionComplete = false; + + delete m_selectionToMerge; + const EventSelection *selectionToMerge = 0; + if (e->state() & ShiftButton) { + m_clickedShift = true; + selectionToMerge = m_nParentView->getCurrentSelection(); + } else { + m_clickedShift = false; + } + m_selectionToMerge = + (selectionToMerge ? new EventSelection(*selectionToMerge) : 0); + + m_clickedElement = dynamic_cast<NotationElement*>(element); + if (m_clickedElement) { + m_selectedStaff = getStaffForElement(m_clickedElement); + m_lastDragPitch = -400; + m_lastDragTime = m_clickedElement->event()->getNotationAbsoluteTime(); + } else { + m_selectedStaff = 0; // don't know yet; wait until we have an element + } + + m_selectionRect->setX(e->x()); + m_selectionRect->setY(e->y()); + m_selectionRect->setSize(0, 0); + + m_selectionRect->show(); + m_updateRect = true; + m_startedFineDrag = false; + + //m_parentView->setCursorPosition(p.x()); +} + +void NotationSelector::handleRightButtonPress(timeT t, + int height, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + std::cerr << "NotationSelector::handleRightButtonPress" << std::endl; + + const EventSelection *sel = m_nParentView->getCurrentSelection(); + + if (!sel || sel->getSegmentEvents().empty()) { + + // if nothing selected, permit the possibility of selecting + // something before showing the menu + + if (element) { + m_clickedElement = dynamic_cast<NotationElement*>(element); + m_selectedStaff = getStaffForElement(m_clickedElement); + m_nParentView->setSingleSelectedEvent + (m_selectedStaff->getId(), m_clickedElement->event(), + true, true); + } + + handleLeftButtonPress(t, height, staffNo, e, element); + } + + EditTool::handleRightButtonPress(t, height, staffNo, e, element); +} + +void NotationSelector::slotClickTimeout() +{ + m_justSelectedBar = false; +} + +void NotationSelector::handleMouseDoubleClick(timeT, + int, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + NOTATION_DEBUG << "NotationSelector::handleMouseDoubleClick" << endl; + m_clickedElement = dynamic_cast<NotationElement*>(element); + + NotationStaff *staff = m_nParentView->getNotationStaff(staffNo); + if (!staff) + return ; + m_selectedStaff = staff; + + bool advanced = (e->state() & ShiftButton); + + if (m_clickedElement) { + + emit editElement(staff, m_clickedElement, advanced); + + } else { + + QRect rect = staff->getBarExtents(e->x(), e->y()); + + m_selectionRect->setX(rect.x() + 1); + m_selectionRect->setY(rect.y()); + m_selectionRect->setSize(rect.width() - 1, rect.height()); + + m_selectionRect->show(); + m_updateRect = false; + + m_justSelectedBar = true; + QTimer::singleShot(QApplication::doubleClickInterval(), this, + SLOT(slotClickTimeout())); + } + + return ; +} + +void NotationSelector::handleMouseTripleClick(timeT t, + int height, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + if (!m_justSelectedBar) + return ; + m_justSelectedBar = false; + + NOTATION_DEBUG << "NotationSelector::handleMouseTripleClick" << endl; + m_clickedElement = dynamic_cast<NotationElement*>(element); + + NotationStaff *staff = m_nParentView->getNotationStaff(staffNo); + if (!staff) + return ; + m_selectedStaff = staff; + + if (m_clickedElement) { + + // should be safe, as we've already set m_justSelectedBar false + handleLeftButtonPress(t, height, staffNo, e, element); + return ; + + } else { + + m_selectionRect->setX(staff->getX()); + m_selectionRect->setY(staff->getY()); + m_selectionRect->setSize(int(staff->getTotalWidth()) - 1, + staff->getTotalHeight() - 1); + + m_selectionRect->show(); + m_updateRect = false; + } + + m_wholeStaffSelectionComplete = true; + + return ; +} + +int NotationSelector::handleMouseMove(timeT, int, + QMouseEvent* e) +{ + if (!m_updateRect) + return RosegardenCanvasView::NoFollow; + + int w = int(e->x() - m_selectionRect->x()); + int h = int(e->y() - m_selectionRect->y()); + + if (m_clickedElement /* && !m_clickedElement->isRest() */) { + + if (m_startedFineDrag) { + dragFine(e->x(), e->y(), false); + } else if (m_clickedShift) { + if (w > 2 || w < -2 || h > 2 || h < -2) { + dragFine(e->x(), e->y(), false); + } + } else if (w > 3 || w < -3 || h > 3 || h < -3) { + drag(e->x(), e->y(), false); + } + + } else { + + // Qt rectangle dimensions appear to be 1-based + if (w > 0) + ++w; + else + --w; + if (h > 0) + ++h; + else + --h; + + m_selectionRect->setSize(w, h); + setViewCurrentSelection(true); + m_nParentView->canvas()->update(); + } + + return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical; +} + +void NotationSelector::handleMouseRelease(timeT, int, QMouseEvent *e) +{ + NOTATION_DEBUG << "NotationSelector::handleMouseRelease" << endl; + m_updateRect = false; + + NOTATION_DEBUG << "selectionRect width, height: " << m_selectionRect->width() + << ", " << m_selectionRect->height() << endl; + + // Test how far we've moved from the original click position -- not + // how big the rectangle is (if we were dragging an event, the + // rectangle size will still be zero). + + if (((e->x() - m_selectionRect->x()) > -3 && + (e->x() - m_selectionRect->x()) < 3 && + (e->y() - m_selectionRect->y()) > -3 && + (e->y() - m_selectionRect->y()) < 3) && + !m_startedFineDrag) { + + if (m_clickedElement != 0 && m_selectedStaff) { + + // If we didn't drag out a meaningful area, but _did_ + // click on an individual event, then select just that + // event + + if (m_selectionToMerge && + m_selectionToMerge->getSegment() == + m_selectedStaff->getSegment()) { + + // if the event was already part of the selection, we want to + // remove it + if (m_selectionToMerge->contains(m_clickedElement->event())) { + m_selectionToMerge->removeEvent(m_clickedElement->event()); + } else { + m_selectionToMerge->addEvent(m_clickedElement->event()); + } + + m_nParentView->setCurrentSelection(m_selectionToMerge, + true, true); + m_selectionToMerge = 0; + + } else { + + m_nParentView->setSingleSelectedEvent + (m_selectedStaff->getId(), m_clickedElement->event(), + true, true); + } + /* + } else if (m_selectedStaff) { + + // If we clicked on no event but on a staff, move the + // insertion cursor to the point where we clicked. + // Actually we only really want this to happen if + // we aren't double-clicking -- consider using a timer + // to establish whether a double-click is going to happen + + m_nParentView->slotSetInsertCursorPosition(e->x(), (int)e->y()); + */ + } else { + setViewCurrentSelection(false); + } + + } else { + + if (m_startedFineDrag) { + dragFine(e->x(), e->y(), true); + } else if (m_clickedElement /* && !m_clickedElement->isRest() */) { + drag(e->x(), e->y(), true); + } else { + setViewCurrentSelection(false); + } + } + + m_clickedElement = 0; + m_selectionRect->hide(); + m_wholeStaffSelectionComplete = false; + m_nParentView->canvas()->update(); +} + +void NotationSelector::drag(int x, int y, bool final) +{ + NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl; + + if (!m_clickedElement || !m_selectedStaff) + return ; + + EventSelection *selection = m_nParentView->getCurrentSelection(); + if (!selection || !selection->contains(m_clickedElement->event())) { + selection = new EventSelection(m_selectedStaff->getSegment()); + selection->addEvent(m_clickedElement->event()); + } + m_nParentView->setCurrentSelection(selection); + + LinedStaff *targetStaff = m_nParentView->getStaffForCanvasCoords(x, y); + if (!targetStaff) + targetStaff = m_selectedStaff; + + // Calculate time and height + + timeT clickedTime = m_clickedElement->event()->getNotationAbsoluteTime(); + + Accidental clickedAccidental = Accidentals::NoAccidental; + (void)m_clickedElement->event()->get<String>(ACCIDENTAL, clickedAccidental); + + long clickedPitch = 0; + (void)m_clickedElement->event()->get<Int>(PITCH, clickedPitch); + + long clickedHeight = 0; + (void)m_clickedElement->event()->get<Int> + (NotationProperties::HEIGHT_ON_STAFF, clickedHeight); + + Event *clefEvt = 0, *keyEvt = 0; + Clef clef; + ::Rosegarden::Key key; + + timeT dragTime = clickedTime; + double layoutX = m_clickedElement->getLayoutX(); + timeT duration = m_clickedElement->getViewDuration(); + + NotationElementList::iterator itr = + targetStaff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt); + + if (itr != targetStaff->getViewElementList()->end()) { + + NotationElement *elt = dynamic_cast<NotationElement *>(*itr); + dragTime = elt->getViewAbsoluteTime(); + layoutX = elt->getLayoutX(); + + if (elt->isRest() && duration > 0 && elt->getCanvasItem()) { + + double restX = 0, restWidth = 0; + elt->getCanvasAirspace(restX, restWidth); + + timeT restDuration = elt->getViewDuration(); + + if (restWidth > 0 && + restDuration >= duration * 2) { + + int parts = restDuration / duration; + double encroachment = x - restX; + NOTATION_DEBUG << "encroachment is " << encroachment << ", restWidth is " << restWidth << endl; + int part = (int)((encroachment / restWidth) * parts); + if (part >= parts) + part = parts - 1; + + dragTime += part * restDuration / parts; + layoutX += part * restWidth / parts + + (restX - elt->getCanvasX()); + } + } + } + + if (clefEvt) + clef = Clef(*clefEvt); + if (keyEvt) + key = ::Rosegarden::Key(*keyEvt); + + int height = targetStaff->getHeightAtCanvasCoords(x, y); + int pitch = clickedPitch; + + if (height != clickedHeight) + pitch = + Pitch + (height, clef, key, clickedAccidental).getPerformancePitch(); + + if (pitch < clickedPitch) { + if (height < -10) { + height = -10; + pitch = Pitch + (height, clef, key, clickedAccidental).getPerformancePitch(); + } + } else if (pitch > clickedPitch) { + if (height > 18) { + height = 18; + pitch = Pitch + (height, clef, key, clickedAccidental).getPerformancePitch(); + } + } + + bool singleNonNotePreview = !m_clickedElement->isNote() && + selection->getSegmentEvents().size() == 1; + + if (!final && !singleNonNotePreview) { + + if ((pitch != m_lastDragPitch || dragTime != m_lastDragTime) && + m_clickedElement->isNote()) { + + m_nParentView->showPreviewNote(targetStaff->getId(), + layoutX, pitch, height, + Note::getNearestNote(duration), + m_clickedElement->isGrace()); + m_lastDragPitch = pitch; + m_lastDragTime = dragTime; + } + + } else { + + m_nParentView->clearPreviewNote(); + + KMacroCommand *command = new KMacroCommand(MoveCommand::getGlobalName()); + bool haveSomething = false; + + MoveCommand *mc = 0; + Event *lastInsertedEvent = 0; + + if (pitch != clickedPitch && m_clickedElement->isNote()) { + command->addCommand(new TransposeCommand(pitch - clickedPitch, + *selection)); + haveSomething = true; + } + + if (targetStaff != m_selectedStaff) { + command->addCommand(new MoveAcrossSegmentsCommand + (m_selectedStaff->getSegment(), + targetStaff->getSegment(), + dragTime - clickedTime + selection->getStartTime(), + true, + *selection)); + haveSomething = true; + } else { + if (dragTime != clickedTime) { + mc = new MoveCommand + (m_selectedStaff->getSegment(), //!!!sort + dragTime - clickedTime, true, *selection); + command->addCommand(mc); + haveSomething = true; + } + } + + if (haveSomething) { + + m_nParentView->addCommandToHistory(command); + + if (mc && singleNonNotePreview) { + + lastInsertedEvent = mc->getLastInsertedEvent(); + + if (lastInsertedEvent) { + m_nParentView->setSingleSelectedEvent(targetStaff->getId(), + lastInsertedEvent); + + ViewElementList::iterator vli = + targetStaff->findEvent(lastInsertedEvent); + + if (vli != targetStaff->getViewElementList()->end()) { + m_clickedElement = dynamic_cast<NotationElement *>(*vli); + } else { + m_clickedElement = 0; + } + + m_selectionRect->setX(x); + m_selectionRect->setY(y); + } + } + } else { + delete command; + } + } +} + +void NotationSelector::dragFine(int x, int y, bool final) +{ + NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl; + + if (!m_clickedElement || !m_selectedStaff) + return ; + + EventSelection *selection = m_nParentView->getCurrentSelection(); + if (!selection) + selection = new EventSelection(m_selectedStaff->getSegment()); + if (!selection->contains(m_clickedElement->event())) + selection->addEvent(m_clickedElement->event()); + m_nParentView->setCurrentSelection(selection); + + // Fine drag modifies the DISPLACED_X and DISPLACED_Y properties on + // each event. The modifications have to be relative to the previous + // values of these properties, not to zero, so for each event we need + // to store the previous value at the time the drag starts. + + static PropertyName xProperty("temporary-displaced-x"); + static PropertyName yProperty("temporary-displaced-y"); + + if (!m_startedFineDrag) { + // back up original properties + + for (EventSelection::eventcontainer::iterator i = + selection->getSegmentEvents().begin(); + i != selection->getSegmentEvents().end(); ++i) { + long prevX = 0, prevY = 0; + (*i)->get + <Int>(DISPLACED_X, prevX); + (*i)->get + <Int>(DISPLACED_Y, prevY); + (*i)->setMaybe<Int>(xProperty, prevX); + (*i)->setMaybe<Int>(yProperty, prevY); + } + + m_startedFineDrag = true; + } + + // We want the displacements in 1/1000ths of a staff space + + double dx = x - m_selectionRect->x(); + double dy = y - m_selectionRect->y(); + + double noteBodyWidth = m_nParentView->getNotePixmapFactory()->getNoteBodyWidth(); + double lineSpacing = m_nParentView->getNotePixmapFactory()->getLineSpacing(); + dx = (1000.0 * dx) / noteBodyWidth; + dy = (1000.0 * dy) / lineSpacing; + + if (final) { + + // reset original values (and remove backup values) before + // applying command + + for (EventSelection::eventcontainer::iterator i = + selection->getSegmentEvents().begin(); + i != selection->getSegmentEvents().end(); ++i) { + long prevX = 0, prevY = 0; + (*i)->get + <Int>(xProperty, prevX); + (*i)->get + <Int>(yProperty, prevY); + (*i)->setMaybe<Int>(DISPLACED_X, prevX); + (*i)->setMaybe<Int>(DISPLACED_Y, prevY); + (*i)->unset(xProperty); + (*i)->unset(yProperty); + } + + IncrementDisplacementsCommand *command = new IncrementDisplacementsCommand + (*selection, long(dx), long(dy)); + m_nParentView->addCommandToHistory(command); + + } else { + + timeT startTime = 0, endTime = 0; + + for (EventSelection::eventcontainer::iterator i = + selection->getSegmentEvents().begin(); + i != selection->getSegmentEvents().end(); ++i) { + long prevX = 0, prevY = 0; + (*i)->get + <Int>(xProperty, prevX); + (*i)->get + <Int>(yProperty, prevY); + (*i)->setMaybe<Int>(DISPLACED_X, prevX + long(dx)); + (*i)->setMaybe<Int>(DISPLACED_Y, prevY + long(dy)); + if (i == selection->getSegmentEvents().begin()) { + startTime = (*i)->getAbsoluteTime(); + } + endTime = (*i)->getAbsoluteTime() + (*i)->getDuration(); + } + + if (startTime == endTime) + ++endTime; + selection->getSegment().updateRefreshStatuses(startTime, endTime); + m_nParentView->update(); + } +} + +void NotationSelector::ready() +{ + m_selectionRect = new QCanvasRectangle(m_nParentView->canvas()); + + m_selectionRect->hide(); + m_selectionRect->setPen(GUIPalette::getColour(GUIPalette::SelectionRectangle)); + + m_nParentView->setCanvasCursor(Qt::arrowCursor); + m_nParentView->setHeightTracking(false); +} + +void NotationSelector::stow() +{ + delete m_selectionRect; + m_selectionRect = 0; + m_nParentView->canvas()->update(); +} + +void NotationSelector::slotHideSelection() +{ + if (!m_selectionRect) + return ; + m_selectionRect->hide(); + m_selectionRect->setSize(0, 0); + m_nParentView->canvas()->update(); +} + +void NotationSelector::slotInsertSelected() +{ + m_nParentView->slotLastNoteAction(); +} + +void NotationSelector::slotEraseSelected() +{ + m_parentView->actionCollection()->action("erase")->activate(); +} + +void NotationSelector::slotCollapseRestsHard() +{ + m_parentView->actionCollection()->action("collapse_rests_aggressively")->activate(); +} + +void NotationSelector::slotRespellFlat() +{ + m_parentView->actionCollection()->action("respell_flat")->activate(); +} + +void NotationSelector::slotRespellSharp() +{ + m_parentView->actionCollection()->action("respell_sharp")->activate(); +} + +void NotationSelector::slotRespellNatural() +{ + m_parentView->actionCollection()->action("respell_natural")->activate(); +} + +void NotationSelector::slotCollapseNotes() +{ + m_parentView->actionCollection()->action("collapse_notes")->activate(); +} + +void NotationSelector::slotInterpret() +{ + m_parentView->actionCollection()->action("interpret")->activate(); +} + +void NotationSelector::slotStaffAbove() +{ + m_parentView->actionCollection()->action("move_events_up_staff")->activate(); +} + +void NotationSelector::slotStaffBelow() +{ + m_parentView->actionCollection()->action("move_events_down_staff")->activate(); +} + +void NotationSelector::slotMakeInvisible() +{ + m_parentView->actionCollection()->action("make_invisible")->activate(); +} + +void NotationSelector::slotMakeVisible() +{ + m_parentView->actionCollection()->action("make_visible")->activate(); +} + +void NotationSelector::setViewCurrentSelection(bool preview) +{ + EventSelection *selection = getSelection(); + + if (m_selectionToMerge) { + if (selection && + m_selectionToMerge->getSegment() == selection->getSegment()) { + selection->addFromSelection(m_selectionToMerge); + } else { + return ; + } + } + + m_nParentView->setCurrentSelection(selection, preview, true); +} + +NotationStaff * +NotationSelector::getStaffForElement(NotationElement *elt) +{ + for (int i = 0; i < m_nParentView->getStaffCount(); ++i) { + NotationStaff *staff = m_nParentView->getNotationStaff(i); + if (staff->getSegment().findSingle(elt->event()) != + staff->getSegment().end()) + return staff; + } + return 0; +} + +EventSelection* NotationSelector::getSelection() +{ + // If selection rect is not visible or too small, + // return 0 + // + if (!m_selectionRect->visible()) return 0; + + // NOTATION_DEBUG << "Selection x,y: " << m_selectionRect->x() << "," + // << m_selectionRect->y() << "; w,h: " << m_selectionRect->width() << "," << m_selectionRect->height() << endl; + + if (m_selectionRect->width() > -3 && + m_selectionRect->width() < 3 && + m_selectionRect->height() > -3 && + m_selectionRect->height() < 3) return 0; + + QCanvasItemList itemList = m_selectionRect->collisions(false); + QCanvasItemList::Iterator it; + + QRect rect = m_selectionRect->rect().normalize(); + QCanvasNotationSprite *sprite = 0; + + if (!m_selectedStaff) { + + // Scan the list of collisions, looking for a valid notation + // element; if we find one, initialise m_selectedStaff from it. + // If we don't find one, we have no selection. This is a little + // inefficient but we only do it for the first event in the + // selection. + + for (it = itemList.begin(); it != itemList.end(); ++it) { + + if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) { + + NotationElement &el = sprite->getNotationElement(); + + NotationStaff *staff = getStaffForElement(&el); + if (!staff) continue; + + int x = (int)(*it)->x(); + bool shifted = false; + int nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth(); + + + // #957364 (Notation: Hard to select upper note in + // chords of seconds) -- adjust x-coord for shifted + // note head + if (el.event()->get<Rosegarden::Bool> + (staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) { + x += nbw; + } + + if (!rect.contains(x, int((*it)->y()), true)) { + // #988217 (Notation: Special column of pixels + // prevents sweep selection) -- for notes, test + // again with centred x-coord + if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) { + continue; + } + } + + m_selectedStaff = staff; + break; + } + } + } + + if (!m_selectedStaff) return 0; + Segment& originalSegment = m_selectedStaff->getSegment(); + + // If we selected the whole staff, force that to happen explicitly + // rather than relying on collisions with the rectangle -- because + // events way off the currently visible area might not even have + // been drawn yet, and so will not appear in the collision list. + // (We did still need the collision list to determine which staff + // to use though.) + + if (m_wholeStaffSelectionComplete) { + EventSelection *selection = new EventSelection(originalSegment, + originalSegment.getStartTime(), + originalSegment.getEndMarkerTime()); + return selection; + } + + EventSelection* selection = new EventSelection(originalSegment); + + for (it = itemList.begin(); it != itemList.end(); ++it) { + + if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) { + + NotationElement &el = sprite->getNotationElement(); + + int x = (int)(*it)->x(); + bool shifted = false; + int nbw = m_selectedStaff->getNotePixmapFactory(false).getNoteBodyWidth(); + + // #957364 (Notation: Hard to select upper note in chords + // of seconds) -- adjust x-coord for shifted note head + if (el.event()->get<Rosegarden::Bool> + (m_selectedStaff->getProperties().NOTE_HEAD_SHIFTED, shifted) + && shifted) { + x += nbw; + } + + // check if the element's rect + // is actually included in the selection rect. + // + if (!rect.contains(x, int((*it)->y()), true)) { + // #988217 (Notation: Special column of pixels + // prevents sweep selection) -- for notes, test again + // with centred x-coord + if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) { + continue; + } + } + + // must be in the same segment as we first started on, + // we can't select events across multiple segments + if (selection->getSegment().findSingle(el.event()) != + selection->getSegment().end()) { + selection->addEvent(el.event()); + } + } + } + + if (selection->getAddedEvents() > 0) { + return selection; + } else { + delete selection; + return 0; + } +} + +const QString NotationSelector::ToolName = "notationselector"; + +} +#include "NotationSelector.moc" |