void SegmentSelector::setContextHelpFor(QPoint pos, Qt::KeyboardModifiers modifiers) { // If we are moving something if (m_selectionMoveStarted) { const bool shift = ((modifiers & Qt::ShiftModifier) != 0); // If shift isn't being held down if (!shift) { setContextHelp(tr("Hold Shift to avoid snapping to beat grid")); } else { clearContextHelp(); } return; } ChangingSegmentPtr segment = m_canvas->getModel()->getSegmentAt(pos); // If the mouse is hovering over the background if (!segment) { setContextHelp(tr("Click and drag to select segments; middle-click and drag to draw an empty segment")); return; } // The mouse is hovering over a segment. const bool ctrl = ((modifiers & Qt::ControlModifier) != 0); // If clicking would resize if (m_canvas->getModel()->getSelectedSegments().size() <= 1 && isNearEdge(segment->rect(), pos)) { if (!ctrl) { setContextHelp(tr("Click and drag to resize a segment; hold Ctrl as well to rescale its contents")); } else { setContextHelp(tr("Click and drag to rescale segment")); } } else { // clicking would move if (m_canvas->getModel()->haveMultipleSelection()) { if (!ctrl) { setContextHelp(tr("Click and drag to move segments; hold Ctrl as well to copy them; Ctrl + Alt for linked copies")); } else { setContextHelp(tr("Click and drag to copy segments")); } } else { if (!ctrl) { setContextHelp(tr("Click and drag to move segment; hold Ctrl as well to copy it; Ctrl + Alt for a linked copy; double-click to edit")); } else { setContextHelp(tr("Click and drag to copy segment")); } } } }
void SegmentMover::mousePressEvent(QMouseEvent *e) { // Let the baseclass have a go. SegmentTool::mousePressEvent(e); // We only care about the left mouse button. if (e->button() != Qt::LeftButton) return; // No need to propagate. e->accept(); QPoint pos = m_canvas->viewportToContents(e->pos()); CompositionModelImpl *model = m_canvas->getModel(); // Get the Segment that was clicked. ChangingSegmentPtr changingSegment = model->getSegmentAt(pos); // If the segment canvas background was clicked, clear the selection. if (!changingSegment) { model->clearSelected(); model->selectionHasChanged(); m_canvas->update(); return; } // The original segment in the composition. Segment *segment = changingSegment->getSegment(); // If we're clicking on a segment that isn't in the selection if (!model->isSelected(segment)) { // Clear the selection. model->clearSelected(); model->selectionHasChanged(); } setChangingSegment(changingSegment); m_clickPoint = pos; setSnapTime(e, SnapGrid::SnapToBeat); int x = int(m_canvas->grid().getRulerScale()->getXForTime( segment->getStartTime())); int y = m_canvas->grid().getYBinCoordinate(segment->getTrack()); m_canvas->drawGuides(x, y); // If some segments are already selected if (model->haveSelection()) { // Start the move on all selected segments model->startChangeSelection(CompositionModelImpl::ChangeMove); // Find the clicked segment in the model. ChangingSegmentPtr newChangingSegment = model->findChangingSegment(segment); if (newChangingSegment) { // Toss the local "changing" segment since it isn't going to // be moving at all. Swap it for the same changing segment in // the model. That one *will* be moving and can be used to // drive the guides. setChangingSegment(newChangingSegment); } } else { // Nothing selected, just move the one that was clicked model->startChange(changingSegment, CompositionModelImpl::ChangeMove); } m_canvas->update(); }
int SegmentMover::mouseMoveEvent(QMouseEvent *e) { // No need to propagate. e->accept(); // If we aren't moving anything, bail. if (!getChangingSegment()) return RosegardenScrollView::NoFollow; QPoint pos = m_canvas->viewportToContents(e->pos()); setSnapTime(e, SnapGrid::SnapToBeat); // If shift isn't being held down if ((e->modifiers() & Qt::ShiftModifier) == 0) { setContextHelp(tr("Hold Shift to avoid snapping to beat grid")); } else { clearContextHelp(); } const SnapGrid &grid = m_canvas->grid(); // Compute how far we've moved vertically. const int startTrackPos = grid.getYBin(m_clickPoint.y()); const int currentTrackPos = grid.getYBin(pos.y()); const int deltaTrack = currentTrackPos - startTrackPos; const int dx = pos.x() - m_clickPoint.x(); Composition &comp = m_doc->getComposition(); CompositionModelImpl::ChangingSegmentSet &changingSegments = m_canvas->getModel()->getChangingSegments(); // For each changing segment, move it for (CompositionModelImpl::ChangingSegmentSet::iterator it = changingSegments.begin(); it != changingSegments.end(); ++it) { ChangingSegmentPtr changingSegment = *it; const timeT newStartTime = grid.snapX(changingSegment->savedRect().x() + dx); const int newX = int(grid.getRulerScale()->getXForTime(newStartTime)); int trackPos = grid.getYBin(changingSegment->savedRect().y()) + deltaTrack; if (trackPos < 0) trackPos = 0; if (trackPos >= (int)comp.getNbTracks()) trackPos = comp.getNbTracks() - 1; const int newY = grid.getYBinCoordinate(trackPos); changingSegment->moveTo(newX, newY); m_changeMade = true; } if (m_changeMade) { // Make sure the segments are redrawn. m_canvas->slotUpdateAll(); } // Draw the guides int guideX = getChangingSegment()->rect().x(); int guideY = getChangingSegment()->rect().y(); m_canvas->drawGuides(guideX, guideY); // Format and draw the text float timeT guideTime = grid.snapX(guideX); RealTime time = comp.getElapsedRealTime(guideTime); int bar, beat, fraction, remainder; comp.getMusicalTimeForAbsoluteTime(guideTime, bar, beat, fraction, remainder); QString timeString = QString("%1.%2s (%3, %4, %5)") .arg(time.sec).arg(time.msec(), 3, 10, QChar('0')) .arg(bar + 1).arg(beat).arg(fraction); m_canvas->drawTextFloat(guideX + 10, guideY - 30, timeString); m_canvas->update(); return RosegardenScrollView::FollowHorizontal | RosegardenScrollView::FollowVertical; }
void SegmentMover::mouseReleaseEvent(QMouseEvent *e) { // We only care about the left mouse button. if (e->button() != Qt::LeftButton) return; // No need to propagate. e->accept(); // If we weren't moving anything, bail. if (!getChangingSegment()) return; if (m_changeMade) { QPoint pos = m_canvas->viewportToContents(e->pos()); // Compute how far we've moved vertically. const int startTrackPos = m_canvas->grid().getYBin(m_clickPoint.y()); const int currentTrackPos = m_canvas->grid().getYBin(pos.y()); const int deltaTrack = currentTrackPos - startTrackPos; CompositionModelImpl::ChangingSegmentSet &changingSegments = m_canvas->getModel()->getChangingSegments(); Composition &comp = m_doc->getComposition(); SegmentReconfigureCommand *command = new SegmentReconfigureCommand( changingSegments.size() == 1 ? tr("Move Segment") : tr("Move Segments"), &comp); // For each changing segment for (CompositionModelImpl::ChangingSegmentSet::iterator it = changingSegments.begin(); it != changingSegments.end(); ++it) { ChangingSegmentPtr changingSegment = *it; // The original Segment in the Composition. Segment *segment = changingSegment->getSegment(); // New Track ID TrackId origTrackId = segment->getTrack(); int trackPos = comp.getTrackPositionById(origTrackId) + deltaTrack; if (trackPos < 0) { trackPos = 0; } else if (trackPos >= (int)comp.getNbTracks()) { trackPos = comp.getNbTracks() - 1; } Track *newTrack = comp.getTrackByPosition(trackPos); int newTrackId = origTrackId; if (newTrack) newTrackId = newTrack->getId(); // New start time timeT newStartTime = changingSegment->getStartTime(m_canvas->grid()); // New end time // We absolutely don't want to snap the end time // to the grid. We want it to remain exactly the same // as it was, but relative to the new start time. timeT newEndTime = newStartTime + segment->getEndMarkerTime(false) - segment->getStartTime(); // Add the changed segment to the command command->addSegment(segment, newStartTime, newEndTime, newTrackId); } CommandHistory::getInstance()->addCommand(command); m_changeMade = false; } m_canvas->hideTextFloat(); m_canvas->hideGuides(); m_canvas->getModel()->endChange(); m_canvas->slotUpdateAll(); setChangingSegment(ChangingSegmentPtr()); setBasicContextHelp(); }
void SegmentSelector::mousePressEvent(QMouseEvent *e) { // Let the baseclass have a go. SegmentTool::mousePressEvent(e); // We only care about the left and middle mouse buttons. if (e->button() != Qt::LeftButton && e->button() != Qt::MidButton) return; // No need to propagate. e->accept(); QPoint pos = m_canvas->viewportToContents(e->pos()); // Get the segment that was clicked. ChangingSegmentPtr item = m_canvas->getModel()->getSegmentAt(pos); // If middle button... if (e->button() == Qt::MidButton) { // If clicked on the background, create a new segment. if (!item) { m_dispatchTool = m_canvas->getToolBox()->getTool(SegmentPencil::ToolName()); if (m_dispatchTool) { m_dispatchTool->ready(); // set mouse cursor m_dispatchTool->mousePressEvent(e); } } return; } // Left button was pressed. // ??? Should we split this into a midPress(e) and a leftPress(e)? // Might improve readability a little. // *** Adjust Selection // Shift key adds to selection. m_segmentAddMode = ((e->modifiers() & Qt::ShiftModifier) != 0); // if a segment was clicked if (item) { bool selected = m_canvas->getModel()->isSelected(item->getSegment()); if (m_segmentAddMode) { // toggle item selection m_canvas->getModel()->setSelected(item->getSegment(), !selected); } else { if (!selected) { // make the item the selection m_canvas->getModel()->clearSelected(); m_canvas->getModel()->setSelected(item->getSegment()); } } } else { // the background was clicked if (!m_segmentAddMode) { // clear the selection m_canvas->getModel()->clearSelected(); } } // *** Perform Functions bool ctrl = ((e->modifiers() & Qt::ControlModifier) != 0); // if a segment was clicked if (item) { // * Resize // if only one segment is selected and clicked near the edge, resize if (m_canvas->getModel()->getSelectedSegments().size() == 1 && isNearEdge(item->rect(), pos)) { SegmentResizer *segmentResizer = dynamic_cast<SegmentResizer *>( m_canvas->getToolBox()->getTool(SegmentResizer::ToolName())); // Turn it over to SegmentResizer. m_dispatchTool = segmentResizer; m_dispatchTool->ready(); // set mouse cursor m_dispatchTool->mousePressEvent(e); return; } // * Move // We don't dispatch to SegmentMover because SegmentMover // doesn't support copying/linking. bool alt = ((e->modifiers() & Qt::AltModifier) != 0); // Ctrl and Alt+Ctrl are segment copy. m_segmentCopyMode = ctrl; // Alt+Ctrl is copy as link. m_segmentCopyingAsLink = (alt && ctrl); // If the segment is selected, put it in move mode. if (m_canvas->getModel()->isSelected(item->getSegment())) { m_canvas->getModel()->startChange( item, CompositionModelImpl::ChangeMove); } setChangingSegment(item); m_clickPoint = pos; int guideX = item->rect().x(); int guideY = item->rect().y(); m_canvas->drawGuides(guideX, guideY); setSnapTime(e, SnapGrid::SnapToBeat); } else { // the background was clicked if (ctrl) { // * Create Segment m_dispatchTool = m_canvas->getToolBox()->getTool( SegmentPencil::ToolName()); if (m_dispatchTool) { m_dispatchTool->ready(); // set mouse cursor m_dispatchTool->mousePressEvent(e); } return; } // * Rubber Band m_canvas->drawSelectionRectPos1(pos); } // Make sure the Segment Parameters box is updated. See // RosegardenMainViewWidget::slotSelectedSegments(). m_canvas->getModel()->selectionHasChanged(); m_passedInertiaEdge = false; }
int SegmentSelector::mouseMoveEvent(QMouseEvent *e) { // No need to propagate. e->accept(); QPoint pos = m_canvas->viewportToContents(e->pos()); m_lastMousePos = pos; // If no buttons are pressed, update the context help and bail. // Note: Mouse tracking must be on for this to work. See // QWidget::setMouseTracking(). if (e->buttons() == Qt::NoButton) { setContextHelpFor(pos, e->modifiers()); return RosegardenScrollView::NoFollow; } // If another tool has taken over, delegate. if (m_dispatchTool) return m_dispatchTool->mouseMoveEvent(e); // We only handle the left button. The middle button is handled by // the dispatch tool (segment pencil) or ignored. if (e->buttons() != Qt::LeftButton) return RosegardenScrollView::NoFollow; // If we aren't moving anything, rubber band. if (!getChangingSegment()) { m_canvas->drawSelectionRectPos2(pos); m_canvas->getModel()->selectionHasChanged(); return RosegardenScrollView::FollowHorizontal | RosegardenScrollView::FollowVertical; } // Moving // If the segment that was clicked on isn't selected, bail. if (!m_canvas->getModel()->isSelected(getChangingSegment()->getSegment())) return RosegardenScrollView::NoFollow; const int dx = pos.x() - m_clickPoint.x(); const int dy = pos.y() - m_clickPoint.y(); const int inertiaDistance = 8; // If we've not already exceeded the inertia distance, and we // still haven't, bail. if (!m_passedInertiaEdge && abs(dx) < inertiaDistance && abs(dy) < inertiaDistance) { return RosegardenScrollView::NoFollow; } m_passedInertiaEdge = true; m_canvas->viewport()->setCursor(Qt::SizeAllCursor); #if 0 // ??? Moving to mouseReleaseEvent(). if (m_segmentCopyMode && !m_segmentQuickCopyDone) { // Make copies of the original Segment(s). These copies will // take the place of the originals as the user drags the // the originals to a new location. MacroCommand *macroCommand = 0; if (m_segmentCopyingAsLink) { macroCommand = new MacroCommand( SegmentQuickLinkCommand::getGlobalName()); } else { macroCommand = new MacroCommand( SegmentQuickCopyCommand::getGlobalName()); } SegmentSelection selectedItems = m_canvas->getSelectedSegments(); // for each selected segment for (SegmentSelection::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { Segment *segment = *it; Command *command = 0; if (m_segmentCopyingAsLink) { command = new SegmentQuickLinkCommand(segment); } else { // if it's a link, copy as link if (segment->isTrulyLinked()) command = new SegmentQuickLinkCommand(segment); else // copy as a non-link segment command = new SegmentQuickCopyCommand(segment); } macroCommand->addCommand(command); } CommandHistory::getInstance()->addCommand(macroCommand); m_canvas->update(); // Make sure we don't do it again. m_segmentQuickCopyDone = true; } #endif setSnapTime(e, SnapGrid::SnapToBeat); // start move on selected items only once if (!m_selectionMoveStarted) { m_selectionMoveStarted = true; m_canvas->getModel()->startChangeSelection( m_segmentCopyMode ? CompositionModelImpl::ChangeCopy : CompositionModelImpl::ChangeMove); // The call to startChangeSelection() generates a new changing segment. // Get it. ChangingSegmentPtr newChangingSegment = m_canvas->getModel()->findChangingSegment( getChangingSegment()->getSegment()); if (newChangingSegment) { // Toss the local "changing" segment since it isn't going to // be moving at all. Swap it for the same changing segment in // CompositionModelImpl. That one *will* be moving and can be // used to drive the guides. setChangingSegment(newChangingSegment); } } // Display help for the Shift key. setContextHelpFor(pos, e->modifiers()); Composition &comp = m_doc->getComposition(); const int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y()); const int currentTrackPos = m_canvas->grid().getYBin(pos.y()); const int trackDiff = currentTrackPos - startDragTrackPos; CompositionModelImpl::ChangingSegmentSet &changingSegments = m_canvas->getModel()->getChangingSegments(); // For each changing segment for (CompositionModelImpl::ChangingSegmentSet::iterator it = changingSegments.begin(); it != changingSegments.end(); ++it) { ChangingSegmentPtr changingSegment = *it; const timeT newStartTime = m_canvas->grid().snapX( changingSegment->savedRect().x() + dx); const int newX = lround( m_canvas->grid().getRulerScale()->getXForTime(newStartTime)); int newTrackPos = m_canvas->grid().getYBin( changingSegment->savedRect().y()) + trackDiff; // Limit to [0, comp.getNbTracks()-1]. if (newTrackPos < 0) newTrackPos = 0; if (newTrackPos > static_cast<int>(comp.getNbTracks()) - 1) newTrackPos = comp.getNbTracks() - 1; const int newY = m_canvas->grid().getYBinCoordinate(newTrackPos); changingSegment->moveTo(newX, newY); m_changeMade = true; } if (m_changeMade) { // Make sure the segments are redrawn. // Note: The update() call below doesn't cause the segments to be // redrawn. It just updates from the cache. m_canvas->slotUpdateAll(); } // Guides const int guideX = getChangingSegment()->rect().x(); const int guideY = getChangingSegment()->rect().y(); m_canvas->drawGuides(guideX, guideY); // Text Float const timeT guideTime = m_canvas->grid().snapX(guideX); RealTime time = comp.getElapsedRealTime(guideTime); QString msecs; msecs.sprintf("%03d", time.msec()); int bar; int beat; int fraction; int remainder; comp.getMusicalTimeForAbsoluteTime(guideTime, bar, beat, fraction, remainder); QString posString = QString("%1.%2s (%3, %4, %5)"). arg(time.sec).arg(msecs).arg(bar + 1).arg(beat).arg(fraction); m_canvas->drawTextFloat(guideX + 10, guideY - 30, posString); m_canvas->update(); return RosegardenScrollView::FollowHorizontal | RosegardenScrollView::FollowVertical; }
void SegmentSelector::mouseReleaseEvent(QMouseEvent *e) { // We only care about the left and middle mouse buttons. if (e->button() != Qt::LeftButton && e->button() != Qt::MidButton) return; // No need to propagate. e->accept(); QPoint pos = m_canvas->viewportToContents(e->pos()); // If another tool (SegmentPencil or SegmentResizer) has taken // over, delegate. if (m_dispatchTool) { m_dispatchTool->mouseReleaseEvent(e); m_dispatchTool->stow(); // Forget about the tool. // Note that this is not a memory leak. There is only one instance // of each tool stored in BaseToolBox::m_tools. m_dispatchTool = 0; // Back to this tool. ready(); return; } // We only handle the left button. The middle button is handled by // the dispatch tool (segment pencil) or ignored. if (e->button() != Qt::LeftButton) return; // The left button has been released. m_canvas->hideGuides(); m_canvas->hideTextFloat(); // If rubber band mode if (!getChangingSegment()) { m_canvas->hideSelectionRect(); m_canvas->getModel()->finalizeSelectionRect(); m_canvas->getModel()->selectionHasChanged(); return; } m_canvas->viewport()->setCursor(Qt::ArrowCursor); if (m_canvas->getModel()->isSelected(getChangingSegment()->getSegment())) { if (m_changeMade) { MacroCommand *macroCommand = 0; CompositionModelImpl::ChangingSegmentSet &changingSegments = m_canvas->getModel()->getChangingSegments(); if (m_segmentCopyMode) { if (m_segmentCopyingAsLink) { macroCommand = new MacroCommand( tr("Copy %n Segment(s) as link(s)", "", changingSegments.size())); } else { macroCommand = new MacroCommand( tr("Copy %n Segment(s)", "", changingSegments.size())); } } else { macroCommand = new MacroCommand( tr("Move %n Segment(s)", "", changingSegments.size())); } if (m_segmentCopyMode) { // Make copies of the original Segment(s). These copies will // take the place of the originals. SegmentSelection selectedItems = m_canvas->getSelectedSegments(); // for each selected segment for (SegmentSelection::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { Segment *segment = *it; Command *command = 0; if (m_segmentCopyingAsLink) { command = new SegmentQuickLinkCommand(segment); } else { // if it's a link, copy as link if (segment->isTrulyLinked()) command = new SegmentQuickLinkCommand(segment); else // copy as a non-link segment command = new SegmentQuickCopyCommand(segment); } macroCommand->addCommand(command); } } const int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y()); const int currentTrackPos = m_canvas->grid().getYBin(pos.y()); const int trackDiff = currentTrackPos - startDragTrackPos; Composition &comp = m_doc->getComposition(); SegmentReconfigureCommand *segmentReconfigureCommand = new SegmentReconfigureCommand("", &comp); // For each changing segment, add the segment to the // SegmentReconfigureCommand. for (CompositionModelImpl::ChangingSegmentSet::iterator it = changingSegments.begin(); it != changingSegments.end(); ++it) { ChangingSegmentPtr changingSegment = *it; Segment *segment = changingSegment->getSegment(); TrackId origTrackId = segment->getTrack(); int newTrackPos = comp.getTrackPositionById(origTrackId) + trackDiff; // Limit to [0, comp.getNbTracks()-1] if (newTrackPos < 0) newTrackPos = 0; if (newTrackPos > static_cast<int>(comp.getNbTracks()) - 1) newTrackPos = comp.getNbTracks() - 1; Track *newTrack = comp.getTrackByPosition(newTrackPos); int newTrackId = origTrackId; if (newTrack) newTrackId = newTrack->getId(); timeT startTime = changingSegment->getStartTime(m_canvas->grid()); // endTime = startTime + segment duration // We absolutely don't want to snap the end time to // the grid. We want it to remain exactly the same as // it was, but relative to the new start time. timeT endTime = startTime + segment->getEndMarkerTime(false) - segment->getStartTime(); segmentReconfigureCommand->addSegment( segment, startTime, endTime, newTrackId); } macroCommand->addCommand(segmentReconfigureCommand); CommandHistory::getInstance()->addCommand(macroCommand); m_canvas->update(); } m_canvas->getModel()->endChange(); m_canvas->slotUpdateAll(); } // Get ready for the next button press. m_segmentQuickCopyDone = false; m_changeMade = false; m_selectionMoveStarted = false; setChangingSegment(ChangingSegmentPtr()); setContextHelpFor(pos); }