You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
13 KiB
392 lines
13 KiB
/*
|
|
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 "SegmentResizer.h"
|
|
|
|
#include "base/Event.h"
|
|
#include <tdelocale.h>
|
|
#include "misc/Debug.h"
|
|
#include "base/Composition.h"
|
|
#include "base/NotationTypes.h"
|
|
#include "base/Segment.h"
|
|
#include "base/Track.h"
|
|
#include "base/SnapGrid.h"
|
|
#include "commands/segment/AudioSegmentResizeFromStartCommand.h"
|
|
#include "commands/segment/AudioSegmentRescaleCommand.h"
|
|
#include "commands/segment/SegmentRescaleCommand.h"
|
|
#include "commands/segment/SegmentReconfigureCommand.h"
|
|
#include "commands/segment/SegmentResizeFromStartCommand.h"
|
|
#include "CompositionItemHelper.h"
|
|
#include "CompositionModel.h"
|
|
#include "CompositionView.h"
|
|
#include "document/RosegardenGUIDoc.h"
|
|
#include "gui/general/BaseTool.h"
|
|
#include "gui/application/RosegardenGUIApp.h"
|
|
#include "gui/general/RosegardenCanvasView.h"
|
|
#include "gui/widgets/ProgressDialog.h"
|
|
#include "SegmentTool.h"
|
|
#include <kcommand.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tqcursor.h>
|
|
#include <tqevent.h>
|
|
#include <tqpoint.h>
|
|
#include <tqrect.h>
|
|
#include <tqstring.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
SegmentResizer::SegmentResizer(CompositionView *c, RosegardenGUIDoc *d,
|
|
int edgeThreshold)
|
|
: SegmentTool(c, d),
|
|
m_edgeThreshold(edgeThreshold)
|
|
{
|
|
RG_DEBUG << "SegmentResizer()\n";
|
|
}
|
|
|
|
void SegmentResizer::ready()
|
|
{
|
|
m_canvas->viewport()->setCursor(TQt::sizeHorCursor);
|
|
connect(m_canvas, TQ_SIGNAL(contentsMoving (int, int)),
|
|
this, TQ_SLOT(slotCanvasScrolled(int, int)));
|
|
setBasicContextHelp(false);
|
|
}
|
|
|
|
void SegmentResizer::stow()
|
|
{
|
|
disconnect(m_canvas, TQ_SIGNAL(contentsMoving (int, int)),
|
|
this, TQ_SLOT(slotCanvasScrolled(int, int)));
|
|
}
|
|
|
|
void SegmentResizer::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 SegmentResizer::handleMouseButtonPress(TQMouseEvent *e)
|
|
{
|
|
RG_DEBUG << "SegmentResizer::handleMouseButtonPress" << endl;
|
|
m_canvas->getModel()->clearSelected();
|
|
|
|
CompositionItem item = m_canvas->getFirstItemAt(e->pos());
|
|
|
|
if (item) {
|
|
RG_DEBUG << "SegmentResizer::handleMouseButtonPress - got item" << endl;
|
|
setCurrentItem(item);
|
|
|
|
// Are we resizing from start or end?
|
|
if (item->rect().x() + item->rect().width() / 2 > e->pos().x()) {
|
|
m_resizeStart = true;
|
|
} else {
|
|
m_resizeStart = false;
|
|
}
|
|
|
|
m_canvas->getModel()->startChange(item, m_resizeStart ? CompositionModel::ChangeResizeFromStart : CompositionModel::ChangeResizeFromEnd);
|
|
|
|
}
|
|
}
|
|
|
|
void SegmentResizer::handleMouseButtonRelease(TQMouseEvent *e)
|
|
{
|
|
RG_DEBUG << "SegmentResizer::handleMouseButtonRelease" << endl;
|
|
|
|
bool rescale = (e->state() & TQt::ControlButton);
|
|
|
|
if (m_currentItem) {
|
|
|
|
Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
|
|
|
|
// We only want to snap the end that we were actually resizing.
|
|
|
|
timeT oldStartTime, oldEndTime;
|
|
|
|
oldStartTime = segment->getStartTime();
|
|
oldEndTime = segment->getEndMarkerTime();
|
|
|
|
timeT newStartTime, newEndTime;
|
|
|
|
if (m_resizeStart) {
|
|
newStartTime = CompositionItemHelper::getStartTime
|
|
(m_currentItem, m_canvas->grid());
|
|
newEndTime = oldEndTime;
|
|
} else {
|
|
newEndTime = CompositionItemHelper::getEndTime
|
|
(m_currentItem, m_canvas->grid());
|
|
newStartTime = oldStartTime;
|
|
}
|
|
|
|
if (changeMade()) {
|
|
|
|
if (newStartTime > newEndTime) std::swap(newStartTime, newEndTime);
|
|
|
|
if (rescale) {
|
|
|
|
if (segment->getType() == Segment::Audio) {
|
|
|
|
try {
|
|
m_doc->getAudioFileManager().testAudioPath();
|
|
} catch (AudioFileManager::BadAudioPathException) {
|
|
if (KMessageBox::warningContinueCancel
|
|
(0,
|
|
i18n("The audio file path does not exist or is not writable.\nYou must set the audio file path to a valid directory in Document Properties before rescaling an audio file.\nWould you like to set it now?"),
|
|
i18n("Warning"),
|
|
i18n("Set audio file path")) == KMessageBox::Continue) {
|
|
RosegardenGUIApp::self()->slotOpenAudioPathSettings();
|
|
}
|
|
}
|
|
|
|
float ratio = float(newEndTime - newStartTime) /
|
|
float(oldEndTime - oldStartTime);
|
|
|
|
AudioSegmentRescaleCommand *command =
|
|
new AudioSegmentRescaleCommand(m_doc, segment, ratio,
|
|
newStartTime, newEndTime);
|
|
|
|
ProgressDialog progressDlg
|
|
(i18n("Rescaling audio file..."), 100, 0);
|
|
progressDlg.setAutoClose(false);
|
|
progressDlg.setAutoReset(false);
|
|
progressDlg.show();
|
|
command->connectProgressDialog(&progressDlg);
|
|
|
|
addCommandToHistory(command);
|
|
|
|
progressDlg.setLabel(i18n("Generating audio preview..."));
|
|
command->disconnectProgressDialog(&progressDlg);
|
|
connect(&m_doc->getAudioFileManager(), TQ_SIGNAL(setProgress(int)),
|
|
progressDlg.progressBar(), TQ_SLOT(setValue(int)));
|
|
connect(&progressDlg, TQ_SIGNAL(cancelClicked()),
|
|
&m_doc->getAudioFileManager(), TQ_SLOT(slotStopPreview()));
|
|
|
|
int fid = command->getNewAudioFileId();
|
|
if (fid >= 0) {
|
|
RosegardenGUIApp::self()->slotAddAudioFile(fid);
|
|
m_doc->getAudioFileManager().generatePreview(fid);
|
|
}
|
|
|
|
} else {
|
|
|
|
SegmentRescaleCommand *command =
|
|
new SegmentRescaleCommand(segment,
|
|
newEndTime - newStartTime,
|
|
oldEndTime - oldStartTime,
|
|
newStartTime);
|
|
addCommandToHistory(command);
|
|
}
|
|
} else {
|
|
|
|
if (m_resizeStart) {
|
|
|
|
if (segment->getType() == Segment::Audio) {
|
|
addCommandToHistory(new AudioSegmentResizeFromStartCommand
|
|
(segment, newStartTime));
|
|
} else {
|
|
addCommandToHistory(new SegmentResizeFromStartCommand
|
|
(segment, newStartTime));
|
|
}
|
|
|
|
} else {
|
|
|
|
SegmentReconfigureCommand *command =
|
|
new SegmentReconfigureCommand("Resize Segment");
|
|
|
|
int trackPos = CompositionItemHelper::getTrackPos
|
|
(m_currentItem, m_canvas->grid());
|
|
|
|
Composition &comp = m_doc->getComposition();
|
|
Track *track = comp.getTrackByPosition(trackPos);
|
|
|
|
command->addSegment(segment,
|
|
newStartTime,
|
|
newEndTime,
|
|
track->getId());
|
|
addCommandToHistory(command);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_canvas->getModel()->endChange();
|
|
m_canvas->updateContents();
|
|
setChangeMade(false);
|
|
m_currentItem = CompositionItem();
|
|
setBasicContextHelp();
|
|
}
|
|
|
|
int SegmentResizer::handleMouseMove(TQMouseEvent *e)
|
|
{
|
|
// RG_DEBUG << "SegmentResizer::handleMouseMove" << endl;
|
|
|
|
bool rescale = (e->state() & TQt::ControlButton);
|
|
|
|
if (!m_currentItem) {
|
|
setBasicContextHelp(rescale);
|
|
return RosegardenCanvasView::NoFollow;
|
|
}
|
|
|
|
if (rescale) {
|
|
if (!m_canvas->isFineGrain()) {
|
|
setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
|
|
} else {
|
|
clearContextHelp();
|
|
}
|
|
} else {
|
|
if (!m_canvas->isFineGrain()) {
|
|
setContextHelp(i18n("Hold Shift to avoid snapping to beat grid; hold Ctrl as well to rescale contents"));
|
|
} else {
|
|
setContextHelp("Hold Ctrl to rescale contents");
|
|
}
|
|
}
|
|
|
|
Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
|
|
|
|
// Don't allow Audio segments to resize yet
|
|
//
|
|
/*!!!
|
|
if (segment->getType() == Segment::Audio)
|
|
{
|
|
m_currentItem = CompositionItem();
|
|
KMessageBox::information(m_canvas,
|
|
i18n("You can't yet resize an audio segment!"));
|
|
return RosegardenCanvasView::NoFollow;
|
|
}
|
|
*/
|
|
|
|
TQRect oldRect = m_currentItem->rect();
|
|
|
|
m_canvas->setSnapGrain(true);
|
|
|
|
timeT time = m_canvas->grid().snapX(e->pos().x());
|
|
timeT snap = m_canvas->grid().getSnapTime(double(e->pos().x()));
|
|
if (snap == 0)
|
|
snap = Note(Note::Shortest).getDuration();
|
|
|
|
// We only want to snap the end that we were actually resizing.
|
|
|
|
timeT itemStartTime, itemEndTime;
|
|
|
|
if (m_resizeStart) {
|
|
itemStartTime = CompositionItemHelper::getStartTime
|
|
(m_currentItem, m_canvas->grid());
|
|
itemEndTime = segment->getEndMarkerTime();
|
|
} else {
|
|
itemEndTime = CompositionItemHelper::getEndTime
|
|
(m_currentItem, m_canvas->grid());
|
|
itemStartTime = segment->getStartTime();
|
|
}
|
|
|
|
timeT duration = 0;
|
|
|
|
if (m_resizeStart) {
|
|
|
|
duration = itemEndTime - time;
|
|
// RG_DEBUG << "SegmentResizer::handleMouseMove() resize start : duration = "
|
|
// << duration << " - snap = " << snap
|
|
// << " - itemEndTime : " << itemEndTime
|
|
// << " - time : " << time
|
|
// << endl;
|
|
|
|
timeT newStartTime = 0;
|
|
|
|
if ((duration > 0 && duration < snap) ||
|
|
(duration < 0 && duration > -snap)) {
|
|
|
|
newStartTime = itemEndTime - (duration < 0 ? -snap : snap);
|
|
|
|
} else {
|
|
|
|
newStartTime = itemEndTime - duration;
|
|
|
|
}
|
|
|
|
CompositionItemHelper::setStartTime(m_currentItem,
|
|
newStartTime,
|
|
m_canvas->grid());
|
|
} else { // resize end
|
|
|
|
duration = time - itemStartTime;
|
|
|
|
timeT newEndTime = 0;
|
|
|
|
// RG_DEBUG << "SegmentResizer::handleMouseMove() resize end : duration = "
|
|
// << duration << " - snap = " << snap
|
|
// << " - itemEndTime : " << itemEndTime
|
|
// << " - time : " << time
|
|
// << endl;
|
|
|
|
if ((duration > 0 && duration < snap) ||
|
|
(duration < 0 && duration > -snap)) {
|
|
|
|
newEndTime = (duration < 0 ? -snap : snap) + itemStartTime;
|
|
|
|
} else {
|
|
|
|
newEndTime = duration + itemStartTime;
|
|
|
|
}
|
|
|
|
CompositionItemHelper::setEndTime(m_currentItem,
|
|
newEndTime,
|
|
m_canvas->grid());
|
|
}
|
|
|
|
if (duration != 0)
|
|
setChangeMade(true);
|
|
|
|
m_canvas->slotUpdateSegmentsDrawBuffer(m_currentItem->rect() | oldRect);
|
|
|
|
return RosegardenCanvasView::FollowHorizontal;
|
|
}
|
|
|
|
bool SegmentResizer::cursorIsCloseEnoughToEdge(const CompositionItem& p, const TQPoint &coord,
|
|
int edgeThreshold, bool &start)
|
|
{
|
|
if (abs(p->rect().x() + p->rect().width() - coord.x()) < edgeThreshold) {
|
|
start = false;
|
|
return true;
|
|
} else if (abs(p->rect().x() - coord.x()) < edgeThreshold) {
|
|
start = true;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SegmentResizer::setBasicContextHelp(bool ctrlPressed)
|
|
{
|
|
if (ctrlPressed) {
|
|
setContextHelp(i18n("Click and drag to resize a segment; hold Ctrl as well to rescale its contents"));
|
|
} else {
|
|
setContextHelp(i18n("Click and drag to rescale segment"));
|
|
}
|
|
}
|
|
|
|
const TQString SegmentResizer::ToolName = "segmentresizer";
|
|
|
|
}
|
|
#include "SegmentResizer.moc"
|