/*
Rosegarden
A MIDI and audio sequencer and musical notation editor .
This program is Copyright 2000 - 2008
Guillaume Laurent < glaurent @ telegraph - road . org > ,
Chris Cannam < cannam @ all - day - breakfast . com > ,
Richard Bown < richard . bown @ ferventsoftware . com >
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 "SegmentMover.h"
# include "base/Event.h"
# include <tdelocale.h>
# include "misc/Debug.h"
# include "base/Composition.h"
# include "base/RealTime.h"
# include "base/Track.h"
# include "base/SnapGrid.h"
# include "commands/segment/SegmentReconfigureCommand.h"
# include "CompositionItemHelper.h"
# include "CompositionModel.h"
# include "CompositionView.h"
# include "document/RosegardenGUIDoc.h"
# include "gui/general/BaseTool.h"
# include "gui/general/RosegardenCanvasView.h"
# include "SegmentTool.h"
# include "SegmentToolBox.h"
# include "SegmentSelector.h"
# include <kcommand.h>
# include <tqcursor.h>
# include <tqevent.h>
# include <tqpoint.h>
# include <tqrect.h>
# include <tqstring.h>
# include <tdelocale.h>
namespace Rosegarden
{
SegmentMover : : SegmentMover ( CompositionView * c , RosegardenGUIDoc * d )
: SegmentTool ( c , d )
{
RG_DEBUG < < " SegmentMover() \n " ;
}
void SegmentMover : : ready ( )
{
m_canvas - > viewport ( ) - > setCursor ( TQt : : sizeAllCursor ) ;
connect ( m_canvas , TQ_SIGNAL ( contentsMoving ( int , int ) ) ,
this , TQ_SLOT ( slotCanvasScrolled ( int , int ) ) ) ;
setBasicContextHelp ( ) ;
}
void SegmentMover : : stow ( )
{
disconnect ( m_canvas , TQ_SIGNAL ( contentsMoving ( int , int ) ) ,
this , TQ_SLOT ( slotCanvasScrolled ( int , int ) ) ) ;
}
void SegmentMover : : slotCanvasScrolled ( int newX , int newY )
{
TQMouseEvent tmpEvent ( TQEvent : : MouseMove ,
m_canvas - > viewport ( ) - > mapFromGlobal ( TQCursor : : pos ( ) ) + TQPoint ( newX , newY ) ,
TQt : : NoButton , TQt : : NoButton ) ;
handleMouseMove ( & tmpEvent ) ;
}
void SegmentMover : : handleMouseButtonPress ( TQMouseEvent * e )
{
CompositionItem item = m_canvas - > getFirstItemAt ( e - > pos ( ) ) ;
SegmentSelector * selector = dynamic_cast < SegmentSelector * >
( getToolBox ( ) - > getTool ( " segmentselector " ) ) ;
// #1027303: Segment move issue
// Clear selection if we're clicking on an item that's not in it
// and we're not in add mode
if ( selector & & item & &
! m_canvas - > getModel ( ) - > isSelected ( item ) & & ! selector - > isSegmentAdding ( ) ) {
m_canvas - > getModel ( ) - > clearSelected ( ) ;
m_canvas - > getModel ( ) - > signalSelection ( ) ;
m_canvas - > updateContents ( ) ;
}
if ( item ) {
setCurrentItem ( item ) ;
m_clickPoint = e - > pos ( ) ;
Segment * s = CompositionItemHelper : : getSegment ( m_currentItem ) ;
int x = int ( m_canvas - > grid ( ) . getRulerScale ( ) - > getXForTime ( s - > getStartTime ( ) ) ) ;
int y = int ( m_canvas - > grid ( ) . getYBinCoordinate ( s - > getTrack ( ) ) ) ;
m_canvas - > setGuidesPos ( x , y ) ;
m_canvas - > setDrawGuides ( true ) ;
if ( m_canvas - > getModel ( ) - > haveSelection ( ) ) {
RG_DEBUG < < " SegmentMover::handleMouseButtonPress() : haveSelection \n " ;
// startChange on all selected segments
m_canvas - > getModel ( ) - > startChangeSelection ( CompositionModel : : ChangeMove ) ;
CompositionModel : : itemcontainer & changingItems = m_canvas - > getModel ( ) - > getChangingItems ( ) ;
// set m_currentItem to its "sibling" among selected (now moving) items
setCurrentItem ( CompositionItemHelper : : findSiblingCompositionItem ( changingItems , m_currentItem ) ) ;
} else {
RG_DEBUG < < " SegmentMover::handleMouseButtonPress() : no selection \n " ;
m_canvas - > getModel ( ) - > startChange ( item , CompositionModel : : ChangeMove ) ;
}
m_canvas - > updateContents ( ) ;
m_passedInertiaEdge = false ;
} else {
// check for addmode - clear the selection if not
RG_DEBUG < < " SegmentMover::handleMouseButtonPress() : clear selection \n " ;
m_canvas - > getModel ( ) - > clearSelected ( ) ;
m_canvas - > getModel ( ) - > signalSelection ( ) ;
m_canvas - > updateContents ( ) ;
}
}
void SegmentMover : : handleMouseButtonRelease ( TQMouseEvent * e )
{
Composition & comp = m_doc - > getComposition ( ) ;
int startDragTrackPos = m_canvas - > grid ( ) . getYBin ( m_clickPoint . y ( ) ) ;
int currentTrackPos = m_canvas - > grid ( ) . getYBin ( e - > pos ( ) . y ( ) ) ;
int trackDiff = currentTrackPos - startDragTrackPos ;
if ( m_currentItem ) {
if ( changeMade ( ) ) {
CompositionModel : : itemcontainer & changingItems = m_canvas - > getModel ( ) - > getChangingItems ( ) ;
SegmentReconfigureCommand * command =
new SegmentReconfigureCommand
( changingItems . size ( ) = = 1 ? i18n ( " Move Segment " ) : i18n ( " Move Segments " ) ) ;
CompositionModel : : itemcontainer : : iterator it ;
for ( it = changingItems . begin ( ) ;
it ! = changingItems . end ( ) ;
it + + ) {
CompositionItem item = * it ;
Segment * segment = CompositionItemHelper : : getSegment ( item ) ;
TrackId origTrackId = segment - > getTrack ( ) ;
int trackPos = comp . getTrackPositionById ( origTrackId ) ;
trackPos + = trackDiff ;
if ( trackPos < 0 ) {
trackPos = 0 ;
} else if ( trackPos > = comp . getNbTracks ( ) ) {
trackPos = comp . getNbTracks ( ) - 1 ;
}
Track * newTrack = comp . getTrackByPosition ( trackPos ) ;
int newTrackId = origTrackId ;
if ( newTrack ) newTrackId = newTrack - > getId ( ) ;
timeT newStartTime = CompositionItemHelper : : getStartTime ( item , m_canvas - > grid ( ) ) ;
// 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 ( )
- segment - > getStartTime ( ) ;
command - > addSegment ( segment ,
newStartTime ,
newEndTime ,
newTrackId ) ;
}
addCommandToHistory ( command ) ;
}
m_canvas - > hideTextFloat ( ) ;
m_canvas - > setDrawGuides ( false ) ;
m_canvas - > getModel ( ) - > endChange ( ) ;
m_canvas - > slotUpdateSegmentsDrawBuffer ( ) ;
}
setChangeMade ( false ) ;
m_currentItem = CompositionItem ( ) ;
setBasicContextHelp ( ) ;
}
int SegmentMover : : handleMouseMove ( TQMouseEvent * e )
{
m_canvas - > setSnapGrain ( true ) ;
Composition & comp = m_doc - > getComposition ( ) ;
if ( ! m_currentItem ) {
setBasicContextHelp ( ) ;
return RosegardenCanvasView : : NoFollow ;
}
if ( ! m_canvas - > isFineGrain ( ) ) {
setContextHelp ( i18n ( " Hold Shift to avoid snapping to beat grid " ) ) ;
} else {
clearContextHelp ( ) ;
}
CompositionModel : : itemcontainer & changingItems = m_canvas - > getModel ( ) - > getChangingItems ( ) ;
// RG_DEBUG << "SegmentMover::handleMouseMove : nb changingItems = "
// << changingItems.size() << endl;
CompositionModel : : itemcontainer : : iterator it ;
int guideX = 0 ;
int guideY = 0 ;
TQRect updateRect ;
for ( it = changingItems . begin ( ) ;
it ! = changingItems . end ( ) ;
it + + ) {
// it->second->showRepeatRect(false);
int dx = e - > pos ( ) . x ( ) - m_clickPoint . x ( ) ,
dy = e - > pos ( ) . y ( ) - m_clickPoint . y ( ) ;
const int inertiaDistance = m_canvas - > grid ( ) . getYSnap ( ) / 3 ;
if ( ! m_passedInertiaEdge & &
( dx < inertiaDistance & & dx > - inertiaDistance ) & &
( dy < inertiaDistance & & dy > - inertiaDistance ) ) {
return RosegardenCanvasView : : NoFollow ;
} else {
m_passedInertiaEdge = true ;
}
timeT newStartTime = m_canvas - > grid ( ) . snapX ( ( * it ) - > savedRect ( ) . x ( ) + dx ) ;
int newX = int ( m_canvas - > grid ( ) . getRulerScale ( ) - > getXForTime ( newStartTime ) ) ;
int startDragTrackPos = m_canvas - > grid ( ) . getYBin ( m_clickPoint . y ( ) ) ;
int currentTrackPos = m_canvas - > grid ( ) . getYBin ( e - > pos ( ) . y ( ) ) ;
int trackDiff = currentTrackPos - startDragTrackPos ;
int trackPos = m_canvas - > grid ( ) . getYBin ( ( * it ) - > savedRect ( ) . y ( ) ) ;
// std::cerr << "segment " << *it << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", trackPos from " << trackPos << " to ";
trackPos + = trackDiff ;
// std::cerr << trackPos << std::endl;
if ( trackPos < 0 ) {
trackPos = 0 ;
} else if ( trackPos > = comp . getNbTracks ( ) ) {
trackPos = comp . getNbTracks ( ) - 1 ;
}
/*!!!
int newY = m_canvas - > grid ( ) . snapY ( ( * it ) - > savedRect ( ) . y ( ) + dy ) ;
// Make sure we don't set a non-existing track
if ( newY < 0 ) {
newY = 0 ;
}
int trackPos = m_canvas - > grid ( ) . getYBin ( newY ) ;
// RG_DEBUG << "SegmentMover::handleMouseMove: orig y "
// << (*it)->savedRect().y()
// << ", dy " << dy << ", newY " << newY
// << ", track " << track << endl;
// Make sure we don't set a non-existing track (c'td)
// TODO: make this suck less. Either the tool should
// not allow it in the first place, or we automatically
// create new tracks - might make undo very tricky though
//
if ( trackPos > = comp . getNbTracks ( ) )
trackPos = comp . getNbTracks ( ) - 1 ;
*/
int newY = m_canvas - > grid ( ) . getYBinCoordinate ( trackPos ) ;
// RG_DEBUG << "SegmentMover::handleMouseMove: moving to "
// << newX << "," << newY << endl;
updateRect | = ( * it ) - > rect ( ) ;
( * it ) - > moveTo ( newX , newY ) ;
updateRect | = ( * it ) - > rect ( ) ;
setChangeMade ( true ) ;
}
if ( changeMade ( ) )
m_canvas - > getModel ( ) - > signalContentChange ( ) ;
guideX = m_currentItem - > rect ( ) . x ( ) ;
guideY = m_currentItem - > rect ( ) . y ( ) ;
m_canvas - > setGuidesPos ( guideX , guideY ) ;
timeT currentItemStartTime = m_canvas - > grid ( ) . snapX ( m_currentItem - > rect ( ) . x ( ) ) ;
RealTime time = comp . getElapsedRealTime ( currentItemStartTime ) ;
TQString ms ;
ms . sprintf ( " %03d " , time . msec ( ) ) ;
int bar , beat , fraction , remainder ;
comp . getMusicalTimeForAbsoluteTime ( currentItemStartTime , bar , beat , fraction , remainder ) ;
TQString posString = TQString ( " %1.%2s (%3, %4, %5) " )
. arg ( time . sec ) . arg ( ms )
. arg ( bar + 1 ) . arg ( beat ) . arg ( fraction ) ;
m_canvas - > setTextFloat ( guideX + 10 , guideY - 30 , posString ) ;
m_canvas - > updateContents ( ) ;
return RosegardenCanvasView : : FollowHorizontal | RosegardenCanvasView : : FollowVertical ;
}
void SegmentMover : : setBasicContextHelp ( )
{
setContextHelp ( i18n ( " Click and drag to move a segment " ) ) ;
}
const TQString SegmentMover : : ToolName = " segmentmover " ;
}
# include "SegmentMover.moc"