summaryrefslogtreecommitdiffstats
path: root/src/gui/editors/segment/segmentcanvas
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/gui/editors/segment/segmentcanvas
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/editors/segment/segmentcanvas')
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp316
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h79
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp267
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h99
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp149
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h90
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp62
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.h69
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp150
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h61
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h74
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.cpp43
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.h179
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp1328
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h239
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.cpp42
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.h108
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.cpp1591
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.h366
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.h62
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp88
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp37
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h91
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp73
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.h70
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.cpp348
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.h78
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp48
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.h59
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp295
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp393
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.h87
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp532
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.h109
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp175
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.cpp115
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.h105
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp102
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.h63
46 files changed, 8677 insertions, 0 deletions
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
new file mode 100644
index 0000000..1b982dc
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
@@ -0,0 +1,316 @@
+/* -*- 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 "AudioPreviewPainter.h"
+
+#include "CompositionModelImpl.h"
+#include "CompositionColourCache.h"
+#include "base/Composition.h"
+#include "base/Track.h"
+#include "base/AudioLevel.h"
+#include "base/Studio.h"
+
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+
+#include <qimage.h>
+#include <qapplication.h>
+
+#include <kapp.h>
+#include <kconfig.h>
+
+namespace Rosegarden {
+
+AudioPreviewPainter::AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment)
+ : m_model(model),
+ m_apData(apData),
+ m_composition(composition),
+ m_segment(segment),
+ m_rect(model.computeSegmentRect(*(segment))),
+ m_image(1, 1, 8, 4),
+ m_defaultCol(CompositionColourCache::getInstance()->SegmentAudioPreview),
+ m_height(model.grid().getYSnap()/2)
+{
+ int pixWidth = std::min(m_rect.getBaseWidth(), tileWidth());
+
+ m_image = QImage(pixWidth, m_rect.height(), 8, 4);
+ m_image.setAlphaBuffer(true);
+
+ m_penWidth = (std::max(1U, m_rect.getPen().width()) * 2);
+ m_halfRectHeight = m_model.grid().getYSnap()/2 - m_penWidth / 2 - 2;
+}
+
+int AudioPreviewPainter::tileWidth()
+{
+ static int tw = -1;
+ if (tw == -1) tw = QApplication::desktop()->width();
+ return tw;
+}
+
+void AudioPreviewPainter::paintPreviewImage()
+{
+ const std::vector<float>& values = m_apData->getValues();
+
+ if (values.size() == 0)
+ return;
+
+ float gain[2] = { 1.0, 1.0 };
+ int instrumentChannels = 2;
+ TrackId trackId = m_segment->getTrack();
+ Track *track = m_model.getComposition().getTrackById(trackId);
+ if (track) {
+ Instrument *instrument = m_model.getStudio().getInstrumentById(track->getInstrument());
+ if (instrument) {
+ float level = AudioLevel::dB_to_multiplier(instrument->getLevel());
+ float pan = instrument->getPan() - 100.0;
+ gain[0] = level * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
+ gain[1] = level * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+ instrumentChannels = instrument->getAudioChannels();
+ }
+ }
+
+ bool showMinima = m_apData->showsMinima();
+ unsigned int channels = m_apData->getChannels();
+ if (channels == 0) {
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage : problem with audio file for segment "
+ << m_segment->getLabel().c_str() << endl;
+ return;
+ }
+
+ int samplePoints = values.size() / (channels * (showMinima ? 2 : 1));
+ float h1, h2, l1 = 0, l2 = 0;
+ double sampleScaleFactor = samplePoints / double(m_rect.getBaseWidth());
+ m_sliceNb = 0;
+
+ m_image.fill(0);
+
+ int centre = m_image.height() / 2;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage width = " << m_rect.getBaseWidth() << ", height = " << m_rect.height() << ", halfRectHeight = " << m_halfRectHeight << endl;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage: channels = " << channels << ", gain left = " << gain[0] << ", right = " << gain[1] << endl;
+
+ double audioDuration = double(m_segment->getAudioEndTime().sec) +
+ double(m_segment->getAudioEndTime().nsec) / 1000000000.0;
+
+ // We need to take each pixel value and map it onto a point within
+ // the preview. We have samplePoints preview points in a known
+ // duration of audioDuration. Thus each point spans a real time
+ // of audioDuration / samplePoints. We need to convert the
+ // accumulated real time back into musical time, and map this
+ // proportionately across the segment width.
+
+ RealTime startRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getStartTime());
+ double startTime = double(startRT.sec) + double(startRT.nsec) / 1000000000.0;
+
+ RealTime endRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getEndMarkerTime());
+ double endTime = double(endRT.sec) + double(endRT.nsec) / 1000000000.0;
+
+ bool haveTempoChange = false;
+
+ int finalTempoChangeNumber =
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getEndMarkerTime());
+
+ if ((finalTempoChangeNumber >= 0) &&
+
+ (finalTempoChangeNumber >
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getStartTime()))) {
+
+ haveTempoChange = true;
+ }
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ bool meterLevels = (config->readUnsignedNumEntry("audiopreviewstyle", 1)
+ == 1);
+
+ for (int i = 0; i < m_rect.getBaseWidth(); ++i) {
+
+ // i is the x coordinate within the rectangle. We need to
+ // calculate the position within the audio preview from which
+ // to draw the peak for this coordinate. It's possible there
+ // may be more than one, in which case we need to find the
+ // peak of all of them.
+
+ int position = 0;
+
+ if (haveTempoChange) {
+
+ // First find the time corresponding to this i.
+ timeT musicalTime =
+ m_model.grid().getRulerScale()->getTimeForX(m_rect.x() + i);
+ RealTime realTime =
+ m_model.getComposition().getElapsedRealTime(musicalTime);
+
+ double time = double(realTime.sec) +
+ double(realTime.nsec) / 1000000000.0;
+ double offset = time - startTime;
+
+ if (endTime > startTime) {
+ position = offset * m_rect.getBaseWidth() / (endTime - startTime);
+ position = int(channels * position);
+ }
+
+ } else {
+
+ position = int(channels * i * sampleScaleFactor);
+ }
+
+ if (position < 0) continue;
+
+ if (position >= values.size() - channels) {
+ finalizeCurrentSlice();
+ break;
+ }
+
+ if (channels == 1) {
+
+ h1 = values[position++];
+ h2 = h1;
+
+ if (showMinima) {
+ l1 = values[position++];
+ l2 = l1;
+ }
+ } else {
+
+ h1 = values[position++];
+ if (showMinima) l1 = values[position++];
+
+ h2 = values[position++];
+ if (showMinima) l2 = values[position++];
+
+ }
+
+ if (instrumentChannels == 1 && channels == 2) {
+ h1 = h2 = (h1 + h2) / 2;
+ l1 = l2 = (l1 + l2) / 2;
+ }
+
+ h1 *= gain[0];
+ h2 *= gain[1];
+
+ l1 *= gain[0];
+ l2 *= gain[1];
+
+ int width = 1;
+ int pixel;
+
+ // h1 left, h2 right
+ if (h1 >= 1.0) { h1 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ int h;
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h1, m_height);
+ } else {
+ h = h1 * m_height;
+ }
+ if (h <= 0) h = 1;
+ if (h > m_halfRectHeight) h = m_halfRectHeight;
+
+ int rectX = i % tileWidth();
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre - py, pixel);
+ }
+
+ if (h2 >= 1.0) { h2 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h2, m_height);
+ } else {
+ h = h2 * m_height;
+ }
+ if (h < 0) h = 0;
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre + py, pixel);
+ }
+
+ if (((i+1) % tileWidth()) == 0 || i == (m_rect.getBaseWidth() - 1)) {
+ finalizeCurrentSlice();
+ }
+ }
+
+/* Auto-fade not yet implemented.
+
+ if (m_segment->isAutoFading()) {
+
+ Composition &comp = m_model.getComposition();
+
+ int audioFadeInEnd = int(
+ m_model.grid().getRulerScale()->getXForTime(comp.
+ getElapsedTimeForRealTime(m_segment->getFadeInTime()) +
+ m_segment->getStartTime()) -
+ m_model.grid().getRulerScale()->getXForTime(m_segment->getStartTime()));
+
+ m_p.setPen(Qt::blue);
+ m_p.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ m_pb.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ }
+
+ m_p.end();
+ m_pb.end();
+*/
+}
+
+void AudioPreviewPainter::finalizeCurrentSlice()
+{
+// RG_DEBUG << "AudioPreviewPainter::finalizeCurrentSlice : copying pixmap to image at " << m_sliceNb * tileWidth() << endl;
+
+ // transparent background
+ m_image.setColor(0, qRgba(255, 255, 255, 0));
+
+ // foreground from computeSegmentPreviewColor
+ QColor c = m_model.computeSegmentPreviewColor(m_segment);
+ QRgb rgba = qRgba(c.red(), c.green(), c.blue(), 255);
+ m_image.setColor(1, rgba);
+
+ // red for clipping
+ m_image.setColor(2, qRgba(255, 0, 0, 255));
+
+ m_previewPixmaps.push_back(m_image.copy());
+
+ m_image.fill(0);
+
+ ++m_sliceNb;
+}
+
+PixmapArray AudioPreviewPainter::getPreviewImage()
+{
+ return m_previewPixmaps;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
new file mode 100644
index 0000000..b3c1cac
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
@@ -0,0 +1,79 @@
+/* -*- 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.
+*/
+
+#ifndef _RG_AUDIOPREVIEWPAINTER_H_
+#define _RG_AUDIOPREVIEWPAINTER_H_
+
+#include "CompositionModel.h"
+
+#include <qpainter.h>
+#include <qcolor.h>
+
+namespace Rosegarden {
+
+class CompositionModelImpl;
+class Composition;
+class Segment;
+class CompositionRect;
+
+class AudioPreviewPainter {
+public:
+ AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment);
+
+ void paintPreviewImage();
+ PixmapArray getPreviewImage();
+ const CompositionRect& getSegmentRect() { return m_rect; }
+
+ static int tileWidth();
+
+protected:
+ void finalizeCurrentSlice();
+
+ //--------------- Data members ---------------------------------
+ CompositionModelImpl& m_model;
+ CompositionModel::AudioPreviewData* m_apData;
+ const Composition &m_composition;
+ const Segment* m_segment;
+ CompositionRect m_rect;
+
+ QImage m_image;
+ PixmapArray m_previewPixmaps;
+
+ QPainter m_p;
+ QPainter m_pb;
+ QColor m_defaultCol;
+ int m_penWidth;
+ int m_height;
+ int m_halfRectHeight;
+ int m_sliceNb;
+
+};
+
+}
+
+#endif
+
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
new file mode 100644
index 0000000..ae64134
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
@@ -0,0 +1,267 @@
+/* -*- 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 "AudioPreviewThread.h"
+
+#include "base/RealTime.h"
+#include "sound/AudioFileManager.h"
+#include "sound/PeakFileManager.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qobject.h>
+#include <qthread.h>
+
+
+namespace Rosegarden
+{
+
+AudioPreviewThread::AudioPreviewThread(AudioFileManager *manager) :
+ m_manager(manager),
+ m_nextToken(0),
+ m_exiting(false),
+ m_emptyQueueListener(0)
+{}
+
+void
+AudioPreviewThread::run()
+{
+ bool emptyQueueSignalled = false;
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::run entering\n";
+#endif
+
+ while (!m_exiting) {
+
+ if (m_queue.empty()) {
+ if (m_emptyQueueListener && !emptyQueueSignalled) {
+ QApplication::postEvent(m_emptyQueueListener,
+ new QCustomEvent(AudioPreviewQueueEmpty, 0));
+ emptyQueueSignalled = true;
+ }
+
+ usleep(300000);
+ } else {
+ process();
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::run exiting\n";
+#endif
+}
+
+void
+AudioPreviewThread::finish()
+{
+ m_exiting = true;
+}
+
+bool
+AudioPreviewThread::process()
+{
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process()\n";
+#endif
+
+ if (!m_queue.empty()) {
+
+ int failed = 0;
+ int inQueue = 0;
+ //int count = 0;
+
+ m_mutex.lock();
+
+ // process 1st request and leave
+ inQueue = m_queue.size();
+ RequestQueue::iterator i = m_queue.begin();
+
+ // i->first is width, which we use only to provide an ordering to
+ // ensure we do smaller previews first. We don't use it here.
+
+ RequestRec &rec = i->second;
+ int token = rec.first;
+ Request req = rec.second;
+ m_mutex.unlock();
+
+ std::vector<float> results;
+
+ try {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() file id " << req.audioFileId << std::endl;
+#endif
+
+ // Requires thread-safe AudioFileManager::getPreview
+ results = m_manager->getPreview(req.audioFileId,
+ req.audioStartTime,
+ req.audioEndTime,
+ req.width,
+ req.showMinima);
+ } catch (AudioFileManager::BadAudioPathException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad audio path: " << e.getMessage() << std::endl;
+#endif
+
+ // OK, we hope this just means we're still recording -- so
+ // leave this one in the queue
+ ++failed;
+
+ } catch (PeakFileManager::BadPeakFileException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad peak file: " << e.getMessage() << std::endl;
+#endif
+
+ // As above
+ ++failed;
+ }
+
+ m_mutex.lock();
+
+ // We need to check that the token is still in the queue
+ // (i.e. hasn't been cancelled). Otherwise we shouldn't notify
+
+ bool found = false;
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ found = true;
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ if (found) {
+ unsigned int channels =
+ m_manager->getAudioFile(req.audioFileId)->getChannels();
+ m_results[token] = ResultsPair(channels, results);
+ QObject *notify = req.notify;
+ QApplication::postEvent
+ (notify,
+ new QCustomEvent(AudioPreviewReady, (void *)token));
+ }
+
+ m_mutex.unlock();
+
+ if (failed > 0 && failed == inQueue) {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return true\n";
+#endif
+
+ return true; // delay and try again
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return false\n";
+#endif
+
+ return false;
+}
+
+int
+AudioPreviewThread::requestPreview(const Request &request)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview for file id " << request.audioFileId << ", start " << request.audioStartTime << ", end " << request.audioEndTime << ", width " << request.width << ", notify " << request.notify << std::endl;
+#endif
+ /*!!!
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.second.notify == request.notify) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+ */
+ int token = m_nextToken;
+ m_queue.insert(RequestQueue::value_type(request.width,
+ RequestRec(token, request)));
+ ++m_nextToken;
+ m_mutex.unlock();
+
+ // if (!running()) start();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview : thread running : " << running()
+ << " - thread finished : " << finished() << std::endl;
+
+ std::cerr << "AudioPreviewThread::requestPreview - token = " << token << std::endl;
+#endif
+
+ return token;
+}
+
+void
+AudioPreviewThread::cancelPreview(int token)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::cancelPreview for token " << token << std::endl;
+#endif
+
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ m_mutex.unlock();
+}
+
+void
+AudioPreviewThread::getPreview(int token, unsigned int &channels,
+ std::vector<float> &values)
+{
+ m_mutex.lock();
+
+ values.clear();
+ if (m_results.find(token) == m_results.end()) {
+ channels = 0;
+ m_mutex.unlock();
+ return ;
+ }
+
+ channels = m_results[token].first;
+ values = m_results[token].second;
+ m_results.erase(m_results.find(token));
+
+ m_mutex.unlock();
+
+ return ;
+}
+
+const QEvent::Type AudioPreviewThread::AudioPreviewReady = QEvent::Type(QEvent::User + 1);
+const QEvent::Type AudioPreviewThread::AudioPreviewQueueEmpty = QEvent::Type(QEvent::User + 2);
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
new file mode 100644
index 0000000..ae3ac81
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
@@ -0,0 +1,99 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_AUDIOPREVIEWTHREAD_H_
+#define _RG_AUDIOPREVIEWTHREAD_H_
+
+#include "base/RealTime.h"
+#include <map>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <utility>
+#include <vector>
+
+
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class AudioFileManager;
+
+
+class AudioPreviewThread : public QThread
+{
+public:
+ AudioPreviewThread(AudioFileManager *manager);
+
+ virtual void run();
+ virtual void finish();
+
+ struct Request {
+ int audioFileId;
+ RealTime audioStartTime;
+ RealTime audioEndTime;
+ int width;
+ bool showMinima;
+ QObject *notify;
+ };
+
+ virtual int requestPreview(const Request &request);
+ virtual void cancelPreview(int token);
+ virtual void getPreview(int token, unsigned int &channels,
+ std::vector<float> &values);
+
+ void setEmptyQueueListener(QObject* o) { m_emptyQueueListener = o; }
+
+ static const QEvent::Type AudioPreviewReady;
+ static const QEvent::Type AudioPreviewQueueEmpty;
+
+
+protected:
+ virtual bool process();
+
+
+ AudioFileManager *m_manager;
+ int m_nextToken;
+ bool m_exiting;
+
+ QObject* m_emptyQueueListener;
+
+ typedef std::pair<int, Request> RequestRec;
+ typedef std::multimap<int, RequestRec> RequestQueue;
+ RequestQueue m_queue;
+
+ typedef std::pair<unsigned int, std::vector<float> > ResultsPair;
+ typedef std::map<int, ResultsPair> ResultsQueue;
+ ResultsQueue m_results;
+
+ QMutex m_mutex;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
new file mode 100644
index 0000000..76497b9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
@@ -0,0 +1,149 @@
+/* -*- 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 "AudioPreviewUpdater.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "CompositionModelImpl.h"
+#include <qevent.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+static int apuExtantCount = 0;
+
+AudioPreviewUpdater::AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition& c, const Segment* s,
+ const QRect& r,
+ CompositionModelImpl* parent)
+ : QObject(parent),
+ m_thread(thread),
+ m_composition(c),
+ m_segment(s),
+ m_rect(r),
+ m_showMinima(false),
+ m_channels(0),
+ m_previewToken( -1)
+{
+ ++apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::AudioPreviewUpdater " << this << " (now " << apuExtantCount << " extant)" << endl;
+}
+
+AudioPreviewUpdater::~AudioPreviewUpdater()
+{
+ --apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::~AudioPreviewUpdater on " << this << " ( token " << m_previewToken << ") (now " << apuExtantCount << " extant)" << endl;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+}
+
+void AudioPreviewUpdater::update()
+{
+ // Get sample start and end times and work out duration
+ //
+ RealTime audioStartTime = m_segment->getAudioStartTime();
+ RealTime audioEndTime = audioStartTime +
+ m_composition.getElapsedRealTime(m_segment->getEndMarkerTime()) -
+ m_composition.getElapsedRealTime(m_segment->getStartTime()) ;
+
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::update() - for file id "
+ << m_segment->getAudioFileId() << " requesting values - thread running : "
+ << m_thread.running() << " - thread finished : " << m_thread.finished() << endl;
+
+ AudioPreviewThread::Request request;
+ request.audioFileId = m_segment->getAudioFileId();
+ request.audioStartTime = audioStartTime;
+ request.audioEndTime = audioEndTime;
+ request.width = m_rect.width();
+ request.showMinima = m_showMinima;
+ request.notify = this;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = m_thread.requestPreview(request);
+ if (!m_thread.running())
+ m_thread.start();
+}
+
+void AudioPreviewUpdater::cancel()
+{
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = -1;
+}
+
+bool AudioPreviewUpdater::event(QEvent *e)
+{
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::event (" << e << ")" << endl;
+
+ if (e->type() == AudioPreviewThread::AudioPreviewReady) {
+ QCustomEvent *ev = dynamic_cast<QCustomEvent *>(e);
+ if (ev) {
+ intptr_t token = (intptr_t)ev->data();
+ m_channels = 0; // to be filled as getPreview return value
+
+ RG_DEBUG << "AudioPreviewUpdater::token " << token << ", my token " << m_previewToken << endl;
+
+ if (m_previewToken >= 0 && token >= m_previewToken) {
+
+ m_previewToken = -1;
+ m_thread.getPreview(token, m_channels, m_values);
+
+ if (m_channels == 0) {
+ RG_DEBUG << "AudioPreviewUpdater: failed to find preview!\n";
+ } else {
+
+ RG_DEBUG << "AudioPreviewUpdater: got correct preview (" << m_channels
+ << " channels, " << m_values.size() << " samples)\n";
+ }
+
+ emit audioPreviewComplete(this);
+
+ } else {
+
+ // this one is out of date already
+ std::vector<float> tmp;
+ unsigned int tmpChannels;
+ m_thread.getPreview(token, tmpChannels, tmp);
+
+ RG_DEBUG << "AudioPreviewUpdater: got obsolete preview (" << tmpChannels
+ << " channels, " << tmp.size() << " samples)\n";
+ }
+
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+}
+#include "AudioPreviewUpdater.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
new file mode 100644
index 0000000..ffc97c9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
@@ -0,0 +1,90 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_AUDIOPREVIEWUPDATER_H_
+#define _RG_AUDIOPREVIEWUPDATER_H_
+
+#include <qobject.h>
+#include <qrect.h>
+#include <vector>
+
+
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class CompositionModelImpl;
+class Composition;
+class AudioPreviewThread;
+
+
+class AudioPreviewUpdater : public QObject
+{
+ Q_OBJECT
+
+public:
+ AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition &composition,
+ const Segment *segment,
+ const QRect &displayExtent,
+ CompositionModelImpl *parent);
+ ~AudioPreviewUpdater();
+
+ void update();
+ void cancel();
+
+ QRect getDisplayExtent() const { return m_rect; }
+ void setDisplayExtent(const QRect &rect) { m_rect = rect; }
+
+ const Segment *getSegment() const { return m_segment; }
+
+ const std::vector<float> &getComputedValues(unsigned int &channels) const
+ { channels = m_channels; return m_values; }
+
+signals:
+ void audioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ virtual bool event(QEvent*);
+
+ AudioPreviewThread& m_thread;
+
+ const Composition& m_composition;
+ const Segment* m_segment;
+ QRect m_rect;
+ bool m_showMinima;
+ unsigned int m_channels;
+ std::vector<float> m_values;
+
+ intptr_t m_previewToken;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
new file mode 100644
index 0000000..b36d6e0
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 "CompositionColourCache.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+void CompositionColourCache::init()
+{
+ SegmentCanvas = GUIPalette::getColour(GUIPalette::SegmentCanvas);
+ SegmentAudioPreview = GUIPalette::getColour(GUIPalette::SegmentAudioPreview);
+ SegmentInternalPreview = GUIPalette::getColour(GUIPalette::SegmentInternalPreview);
+ SegmentLabel = GUIPalette::getColour(GUIPalette::SegmentLabel);
+ SegmentBorder = GUIPalette::getColour(GUIPalette::SegmentBorder);
+ RepeatSegmentBorder = GUIPalette::getColour(GUIPalette::RepeatSegmentBorder);
+ RecordingSegmentBorder = GUIPalette::getColour(GUIPalette::RecordingSegmentBorder);
+ RecordingAudioSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingAudioSegmentBlock);
+ RecordingInternalSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingInternalSegmentBlock);
+ RotaryFloatBackground = GUIPalette::getColour(GUIPalette::RotaryFloatBackground);
+ RotaryFloatForeground = GUIPalette::getColour(GUIPalette::RotaryFloatForeground);
+
+}
+
+CompositionColourCache* CompositionColourCache::getInstance()
+{
+ if (!m_instance) {
+ m_instance = new CompositionColourCache();
+ }
+
+ return m_instance;
+}
+
+CompositionColourCache* CompositionColourCache::m_instance = 0;
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
new file mode 100644
index 0000000..32d4719
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
@@ -0,0 +1,69 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONCOLOURCACHE_H_
+#define _RG_COMPOSITIONCOLOURCACHE_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class CompositionColourCache
+{
+public:
+ static CompositionColourCache* getInstance();
+
+ void init();
+
+ QColor SegmentCanvas;
+ QColor SegmentAudioPreview;
+ QColor SegmentInternalPreview;
+ QColor SegmentLabel;
+ QColor SegmentBorder;
+ QColor RepeatSegmentBorder;
+ QColor RecordingSegmentBorder;
+ QColor RecordingAudioSegmentBlock;
+ QColor RecordingInternalSegmentBlock;
+ QColor Pointer;
+ QColor MovementGuide;
+ QColor RotaryFloatBackground;
+ QColor RotaryFloatForeground;
+
+protected:
+ CompositionColourCache() { init(); }
+ static CompositionColourCache* m_instance;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
new file mode 100644
index 0000000..798178a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
@@ -0,0 +1,34 @@
+/* -*- 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 "CompositionItem.h"
+
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.h b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
new file mode 100644
index 0000000..b5e749b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
@@ -0,0 +1,67 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONITEM_H_
+#define _RG_COMPOSITIONITEM_H_
+
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+class _CompositionItem : public QObject {
+public:
+ virtual bool isRepeating() const = 0;
+ virtual QRect rect() const = 0;
+ virtual void moveBy(int x, int y) = 0;
+ virtual void moveTo(int x, int y) = 0;
+ virtual void setX(int x) = 0;
+ virtual void setY(int y) = 0;
+ virtual void setZ(unsigned int z) = 0;
+ virtual int x() = 0;
+ virtual int y() = 0;
+ virtual unsigned int z() = 0;
+ virtual void setWidth(int w) = 0;
+
+ // used by itemcontainer
+ virtual long hashKey() = 0;
+
+ QRect savedRect() const { return m_savedRect; }
+ void saveRect() const { m_savedRect = rect(); }
+
+protected:
+ mutable QRect m_savedRect;
+};
+
+typedef QGuardedPtr<_CompositionItem> CompositionItem;
+bool operator<(const CompositionItem&, const CompositionItem&);
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
new file mode 100644
index 0000000..e1705cd
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
@@ -0,0 +1,150 @@
+/* -*- 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 <cmath>
+
+#include "CompositionItemHelper.h"
+
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "misc/Debug.h"
+#include "CompositionModel.h"
+#include "CompositionItemImpl.h"
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+namespace Rosegarden
+{
+
+timeT CompositionItemHelper::getStartTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ // t = std::max(grid.snapX(item->rect().x()), 0L); - wrong, we can have negative start times,
+ // and if we do this we 'crop' segments when they are moved before the start of the composition
+ t = grid.snapX(item->rect().x());
+
+// RG_DEBUG << "CompositionItemHelper::getStartTime(): item is repeating : " << item->isRepeating()
+// << " - startTime = " << t
+// << endl;
+ }
+
+ return t;
+}
+
+timeT CompositionItemHelper::getEndTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ QRect itemRect = item->rect();
+
+ t = std::max(grid.snapX(itemRect.x() + itemRect.width()), 0L);
+
+// RG_DEBUG << "CompositionItemHelper::getEndTime() : rect width = "
+// << itemRect.width()
+// << " - item is repeating : " << item->isRepeating()
+// << " - endTime = " << t
+// << endl;
+
+ }
+
+ return t;
+}
+
+void CompositionItemHelper::setStartTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+
+ RG_DEBUG << "CompositionItemHelper::setStartTime() time = " << time
+ << " -> x = " << x << endl;
+
+ int curX = item->rect().x();
+ item->setX(x);
+ if (item->isRepeating()) {
+ int deltaX = curX - x;
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ int curW = sr.getBaseWidth();
+ sr.setBaseWidth(curW + deltaX);
+ }
+
+ }
+
+}
+
+void CompositionItemHelper::setEndTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+ QRect r = item->rect();
+ QPoint topRight = r.topRight();
+ topRight.setX(x);
+ r.setTopRight(topRight);
+ item->setWidth(r.width());
+
+ if (item->isRepeating()) {
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ sr.setBaseWidth(r.width());
+ }
+ }
+}
+
+int CompositionItemHelper::getTrackPos(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ return grid.getYBin(item->rect().y());
+}
+
+Rosegarden::Segment* CompositionItemHelper::getSegment(CompositionItem item)
+{
+ return (dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item))->getSegment();
+}
+
+CompositionItem CompositionItemHelper::makeCompositionItem(Rosegarden::Segment* segment)
+{
+ return CompositionItem(new CompositionItemImpl(*segment, QRect()));
+}
+
+CompositionItem CompositionItemHelper::findSiblingCompositionItem(const CompositionModel::itemcontainer& items,
+ const CompositionItem& referenceItem)
+{
+ CompositionModel::itemcontainer::const_iterator it;
+ Rosegarden::Segment* currentSegment = CompositionItemHelper::getSegment(referenceItem);
+
+ for (it = items.begin(); it != items.end(); it++) {
+ CompositionItem item = *it;
+ Rosegarden::Segment* segment = CompositionItemHelper::getSegment(item);
+ if (segment == currentSegment) {
+ return item;
+ }
+ }
+
+ return referenceItem;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
new file mode 100644
index 0000000..1b3ad95
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
@@ -0,0 +1,61 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONITEMHELPER_H_
+#define _RG_COMPOSITIONITEMHELPER_H_
+
+#include "CompositionModel.h"
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class Segment;
+
+
+class CompositionItemHelper {
+public:
+ static timeT getStartTime(const CompositionItem&, const SnapGrid&);
+ static timeT getEndTime(const CompositionItem&, const SnapGrid&);
+ static int getTrackPos(const CompositionItem&, const SnapGrid&);
+ static void setStartTime(CompositionItem&, timeT, const SnapGrid&);
+ static void setEndTime(CompositionItem&, timeT, const SnapGrid&);
+ static Segment* getSegment(CompositionItem);
+ static CompositionItem makeCompositionItem(Segment*);
+ /**
+ * return the CompositionItem in the model which references the same segment as referenceItem
+ */
+ static CompositionItem findSiblingCompositionItem(const CompositionModel::itemcontainer& items, const CompositionItem& referenceItem);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
new file mode 100644
index 0000000..5508ad2
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 "CompositionItemImpl.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "CompositionRect.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+CompositionItemImpl::CompositionItemImpl(Segment& s, const CompositionRect& rect)
+ : m_segment(s),
+ m_rect(rect),
+ m_z(0)
+{}
+
+QRect CompositionItemImpl::rect() const
+{
+ QRect res = m_rect;
+ if (m_rect.isRepeating()) {
+ CompositionRect::repeatmarks repeatMarks = m_rect.getRepeatMarks();
+ int neww = m_rect.getBaseWidth();
+
+ // RG_DEBUG << "CompositionItemImpl::rect() - width = "
+ // << m_rect.width() << " - base w = " << neww << endl;
+ res.setWidth(neww);
+ } else {
+ // RG_DEBUG << "CompositionItemImpl::rect() m_rect not repeating\n";
+ }
+
+
+ return res;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
new file mode 100644
index 0000000..b5b3ef7
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
@@ -0,0 +1,74 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONITEMIMPL_H_
+#define _RG_COMPOSITIONITEMIMPL_H_
+
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+#include <qrect.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class CompositionItemImpl : public _CompositionItem {
+public:
+ CompositionItemImpl(Segment& s, const CompositionRect&);
+ virtual bool isRepeating() const { return m_rect.isRepeating(); }
+ virtual QRect rect() const;
+ virtual void moveBy(int x, int y) { m_rect.moveBy(x, y); }
+ virtual void moveTo(int x, int y) { m_rect.setRect(x, y, m_rect.width(), m_rect.height()); }
+ virtual void setX(int x) { m_rect.setX(x); }
+ virtual void setY(int y) { m_rect.setY(y); }
+ virtual void setZ(unsigned int z) { m_z = z; }
+ virtual int x() { return m_rect.x(); }
+ virtual int y() { return m_rect.y(); }
+ virtual unsigned int z() { return m_z; }
+ virtual void setWidth(int w) { m_rect.setWidth(w); }
+ // use segment address as hash key
+ virtual long hashKey() { return (long)getSegment(); }
+
+ Segment* getSegment() { return &m_segment; }
+ const Segment* getSegment() const { return &m_segment; }
+ CompositionRect& getCompRect() { return m_rect; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ Segment& m_segment;
+ CompositionRect m_rect;
+ unsigned int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
new file mode 100644
index 0000000..9701c8a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
@@ -0,0 +1,43 @@
+/* -*- 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 "CompositionModel.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include "CompositionItemHelper.h"
+
+
+namespace Rosegarden
+{
+
+bool CompositionModel::CompositionItemCompare::operator()(const CompositionItem &c1, const CompositionItem &c2) const
+{
+ return CompositionItemHelper::getSegment(c1) < CompositionItemHelper::getSegment(c2);
+}
+
+}
+#include "CompositionModel.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.h b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
new file mode 100644
index 0000000..beafc2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
@@ -0,0 +1,179 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONMODEL_H_
+#define _RG_COMPOSITIONMODEL_H_
+
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <set>
+#include <qcolor.h>
+#include <qobject.h>
+#include <qimage.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+typedef std::vector<QImage> PixmapArray;
+
+
+class CompositionModel : public QObject, public CompositionObserver, public SegmentObserver
+{
+ Q_OBJECT
+public:
+
+ struct CompositionItemCompare {
+ bool operator()(const CompositionItem &c1, const CompositionItem &c2) const;
+ };
+
+ typedef std::vector<QRect> rectlist;
+ typedef std::vector<int> heightlist;
+ typedef std::vector<CompositionRect> rectcontainer;
+ typedef std::set<CompositionItem, CompositionItemCompare> itemcontainer;
+
+ struct AudioPreviewDrawDataItem {
+ AudioPreviewDrawDataItem(PixmapArray p, QPoint bp, QRect r) :
+ pixmap(p), basePoint(bp), rect(r), resizeOffset(0) {};
+ PixmapArray pixmap;
+ QPoint basePoint;
+ QRect rect;
+
+ // when showing a segment that is being resized from the
+ // beginning, this contains the offset between the current
+ // rect of the segment and the resized one
+ int resizeOffset;
+ };
+
+ typedef std::vector<AudioPreviewDrawDataItem> AudioPreviewDrawData;
+
+ struct RectRange {
+ std::pair<rectlist::iterator, rectlist::iterator> range;
+ QPoint basePoint;
+ QColor color;
+ };
+
+ typedef std::vector<RectRange> RectRanges;
+
+ class AudioPreviewData {
+ public:
+ AudioPreviewData(bool showMinima, unsigned int channels) : m_showMinima(showMinima), m_channels(channels) {};
+ // ~AudioPreviewData();
+
+ bool showsMinima() { return m_showMinima; }
+ void setShowMinima(bool s) { m_showMinima = s; }
+
+ unsigned int getChannels() { return m_channels; }
+ void setChannels(unsigned int c) { m_channels = c; }
+
+ const std::vector<float> &getValues() const { return m_values; }
+ void setValues(const std::vector<float>&v) { m_values = v; }
+
+ QRect getSegmentRect() { return m_segmentRect; }
+ void setSegmentRect(const QRect& r) { m_segmentRect = r; }
+
+ protected:
+ std::vector<float> m_values;
+ bool m_showMinima;
+ unsigned int m_channels;
+ QRect m_segmentRect;
+
+ private:
+ // no copy ctor
+ AudioPreviewData(const AudioPreviewData&);
+ };
+
+
+ virtual ~CompositionModel() {};
+
+ virtual unsigned int getNbRows() = 0;
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects) = 0;
+
+ virtual heightlist getTrackDividersIn(const QRect& rect) = 0;
+
+ virtual itemcontainer getItemsAt (const QPoint&) = 0;
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&) = 0;
+
+ virtual SnapGrid& grid() = 0;
+
+ virtual void setPointerPos(int xPos) = 0;
+ virtual void setSelected(const CompositionItem&, bool selected = true) = 0;
+ virtual bool isSelected(const CompositionItem&) const = 0;
+ virtual void setSelected(const itemcontainer&) = 0;
+ virtual void clearSelected() = 0;
+ virtual bool haveSelection() const = 0;
+ virtual bool haveMultipleSelection() const = 0;
+ virtual void signalSelection() = 0;
+ virtual void setSelectionRect(const QRect&) = 0;
+ virtual void finalizeSelectionRect() = 0;
+ virtual QRect getSelectionContentsRect() = 0;
+ virtual void signalContentChange() = 0;
+
+ virtual void addRecordingItem(const CompositionItem&) = 0;
+ virtual void removeRecordingItem(const CompositionItem&) = 0;
+ virtual void clearRecordingItems() = 0;
+ virtual bool haveRecordingItems() = 0;
+
+ enum ChangeType { ChangeMove, ChangeResizeFromStart, ChangeResizeFromEnd };
+
+ virtual void startChange(const CompositionItem&, ChangeType change) = 0;
+ virtual void startChangeSelection(ChangeType change) = 0;
+ virtual itemcontainer& getChangingItems() = 0;
+ virtual void endChange() = 0;
+ virtual ChangeType getChangeType() = 0;
+
+ virtual void setLength(int width) = 0;
+ virtual int getLength() = 0;
+
+signals:
+ void needContentUpdate();
+ void needContentUpdate(const QRect&);
+ void needArtifactsUpdate();
+
+protected:
+ CompositionItem* m_currentCompositionItem;
+};
+
+class AudioPreviewThread;
+class AudioPreviewUpdater;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
new file mode 100644
index 0000000..39deb2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
@@ -0,0 +1,1328 @@
+/* -*- 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 <cmath>
+#include <algorithm>
+#include "CompositionModelImpl.h"
+
+#include "base/BaseProperties.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPreviewThread.h"
+#include "AudioPreviewUpdater.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include "CompositionColourCache.h"
+#include "AudioPreviewPainter.h"
+#include "gui/general/GUIPalette.h"
+#include "SegmentOrderer.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qregexp.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+
+namespace Rosegarden
+{
+
+CompositionModelImpl::CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep)
+ : m_composition(compo),
+ m_studio(studio),
+ m_grid(rulerScale, vStep),
+ m_pointerTimePos(0),
+ m_audioPreviewThread(0)
+{
+ m_notationPreviewDataCache.setAutoDelete(true);
+ m_audioPreviewDataCache.setAutoDelete(true);
+ m_composition.addObserver(this);
+
+ setTrackHeights();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->addObserver(this);
+ }
+}
+
+CompositionModelImpl::~CompositionModelImpl()
+{
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl()" << endl;
+
+ if (!isCompositionDeleted()) {
+
+ m_composition.removeObserver(this);
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->removeObserver(this);
+ }
+ }
+
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl(): removal from Segment & Composition observers OK" << endl;
+
+ if (m_audioPreviewThread) {
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+ }
+}
+
+struct RectCompare {
+ bool operator()(const QRect &r1, const QRect &r2) const {
+ return r1.x() < r2.x();
+ }
+};
+
+void CompositionModelImpl::makeNotationPreviewRects(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& clipRect)
+{
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end();
+
+ rectlist::iterator npi = std::lower_bound(cachedNPData->begin(), npEnd, clipRect, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != cachedNPData->begin())
+ --npi;
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int segEndX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getEndMarkerTime())));
+ int xLim = std::min(clipRect.topRight().x(), segEndX);
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRects : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setX(0);
+ interval.basePoint.setY(basePoint.y());
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeNotationPreviewRectsMovingSegment(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& currentSR)
+{
+ CompositionRect unmovedSR = computeSegmentRect(*segment);
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end(),
+ npBegin = cachedNPData->begin();
+
+ rectlist::iterator npi;
+
+ if (getChangeType() == ChangeResizeFromStart)
+ npi = std::lower_bound(npBegin, npEnd, currentSR, RectCompare());
+ else
+ npi = std::lower_bound(npBegin, npEnd, unmovedSR, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != npBegin && getChangeType() != ChangeResizeFromStart) {
+ --npi;
+ }
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int xLim = getChangeType() == ChangeMove ? unmovedSR.topRight().x() : currentSR.topRight().x();
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRectsMovingSegment : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setY(basePoint.y());
+
+ if (getChangeType() == ChangeMove)
+ interval.basePoint.setX(basePoint.x() - unmovedSR.x());
+ else
+ interval.basePoint.setX(0);
+
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment* segment,
+ const CompositionRect& segRect, const QRect& clipRect)
+{
+ Profiler profiler("CompositionModelImpl::makeAudioPreviewRects", true);
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewRects - segRect = " << segRect << endl;
+
+ PixmapArray previewImage = getAudioPreviewPixmap(segment);
+
+ QPoint basePoint = segRect.topLeft();
+
+ AudioPreviewDrawDataItem previewItem(previewImage, basePoint, segRect);
+
+ if (getChangeType() == ChangeResizeFromStart) {
+ CompositionRect originalRect = computeSegmentRect(*segment);
+ previewItem.resizeOffset = segRect.x() - originalRect.x();
+ }
+
+ apRects->push_back(previewItem);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionItem& item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ computeRepeatMarks(sr, s);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionRect& sr, const Segment* s)
+{
+ if (s->isRepeating()) {
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ if (repeatInterval <= 0) {
+ // std::cerr << "WARNING: CompositionModelImpl::computeRepeatMarks: Segment at " << startTime << " has repeatInterval " << repeatInterval << std::endl;
+ // std::cerr << kdBacktrace() << std::endl;
+ return ;
+ }
+
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s->getRepeatEndTime();
+ sr.setWidth(int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime))));
+
+ CompositionRect::repeatmarks repeatMarks;
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : repeatStart = "
+ // << repeatStart << " - repeatEnd = " << repeatEnd << endl;
+
+ for (timeT repeatMark = repeatStart; repeatMark < repeatEnd; repeatMark += repeatInterval) {
+ int mark = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatMark)));
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : mark at " << mark << endl;
+ repeatMarks.push_back(mark);
+ }
+ sr.setRepeatMarks(repeatMarks);
+ if (repeatMarks.size() > 0)
+ sr.setBaseWidth(repeatMarks[0] - sr.x());
+ else {
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : no repeat marks\n";
+ sr.setBaseWidth(sr.width());
+ }
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : s = "
+ // << s << " base width = " << sr.getBaseWidth()
+ // << " - nb repeat marks = " << repeatMarks.size() << endl;
+
+ }
+}
+
+void CompositionModelImpl::setAudioPreviewThread(AudioPreviewThread *thread)
+{
+ // std::cerr << "\nCompositionModelImpl::setAudioPreviewThread()\n" << std::endl;
+
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+
+ m_audioPreviewThread = thread;
+}
+
+void CompositionModelImpl::clearPreviewCache()
+{
+ RG_DEBUG << "CompositionModelImpl::clearPreviewCache\n";
+
+ m_notationPreviewDataCache.clear();
+ m_audioPreviewDataCache.clear();
+ m_audioSegmentPreviewMap.clear();
+
+ for (AudioPreviewUpdaterMap::iterator i = m_audioPreviewUpdaterMap.begin();
+ i != m_audioPreviewUpdaterMap.end(); ++i) {
+ i->second->cancel();
+ }
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // This will create the audio preview updater. The
+ // preview won't be calculated and cached until the
+ // updater completes and calls back.
+ updatePreviewCacheForAudioSegment((*i), 0);
+ }
+ }
+}
+
+void CompositionModelImpl::updatePreviewCacheForNotationSegment(const Segment* segment, rectlist* npData)
+{
+ npData->clear();
+
+ int segStartX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getStartTime())));
+
+ bool isPercussion = false;
+ Track *track = m_composition.getTrackById(segment->getTrack());
+ if (track) {
+ InstrumentId iid = track->getInstrument();
+ Instrument *instrument = m_studio.getInstrumentById(iid);
+ if (instrument && instrument->isPercussion()) isPercussion = true;
+ }
+
+ for (Segment::iterator i = segment->begin();
+ i != segment->end(); ++i) {
+
+ long pitch = 0;
+ if (!(*i)->isa(Note::EventType) ||
+ !(*i)->get<Int>(BaseProperties::PITCH, pitch)) {
+ continue;
+ }
+
+ timeT eventStart = (*i)->getAbsoluteTime();
+ timeT eventEnd = eventStart + (*i)->getDuration();
+ // if (eventEnd > segment->getEndMarkerTime()) {
+ // eventEnd = segment->getEndMarkerTime();
+ // }
+
+ int x = int(nearbyint(m_grid.getRulerScale()->getXForTime(eventStart)));
+ int width = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(eventStart,
+ eventEnd - eventStart)));
+
+ if (x <= segStartX) {
+ ++x;
+ if (width > 1)
+ --width;
+ }
+ if (width > 1)
+ --width;
+ if (width < 1)
+ ++width;
+
+ double y0 = 0;
+ double y1 = m_grid.getYSnap();
+ double y = y1 + ((y0 - y1) * (pitch - 16)) / 96;
+
+ int height = 2;
+
+ if (isPercussion) {
+ height = 3;
+ if (width > 2) width = 2;
+ }
+
+ if (y < y0)
+ y = y0;
+ if (y > y1 - height + 1)
+ y = y1 - height + 1;
+
+ QRect r(x, (int)y, width, height);
+
+ // RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForNotationSegment() : npData = "
+ // << npData << ", preview rect = "
+ // << r << endl;
+ npData->push_back(r);
+ }
+
+}
+
+QColor CompositionModelImpl::computeSegmentPreviewColor(const Segment* segment)
+{
+ // compute the preview color so it's as visible as possible over the segment's color
+ QColor segColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(segment->getColourIndex()));
+ int h, s, v;
+ segColor.hsv(&h, &s, &v);
+
+ // colors with saturation lower than value should be pastel tints, and
+ // they get a value of 0; yellow and green hues close to the dead center
+ // point for that hue were taking a value of 255 with the (s < v)
+ // formula, so I added an extra hack to force hues in those two narrow
+ // ranges toward black. Black always looks good, while white washes out
+ // badly against intense yellow, and doesn't look very good against
+ // intense green either... hacky, but this produces pleasant results against
+ // every bizarre extreme of color I could cook up to throw at it, plus
+ // (the real reason for all this convoluted fiddling, it does all that while keeping
+ // white against bright reds and blues, which looks better than black)
+ if ( ((((h > 57) && (h < 66)) || ((h > 93) && (h < 131))) && (s > 127) && (v > 127) ) ||
+ (s < v) ) {
+ v = 0;
+ } else {
+ v = 255;
+ }
+ s = 31;
+ h += 180;
+
+ segColor.setHsv(h, s, v);
+
+ return segColor;
+}
+
+void CompositionModelImpl::updatePreviewCacheForAudioSegment(const Segment* segment, AudioPreviewData* apData)
+{
+ if (m_audioPreviewThread) {
+ // std::cerr << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - new audio preview started" << std::endl;
+
+ CompositionRect segRect = computeSegmentRect(*segment);
+ segRect.setWidth(segRect.getBaseWidth()); // don't use repeating area
+ segRect.moveTopLeft(QPoint(0, 0));
+
+ if (apData)
+ apData->setSegmentRect(segRect);
+
+ if (m_audioPreviewUpdaterMap.find(segment) ==
+ m_audioPreviewUpdaterMap.end()) {
+
+ AudioPreviewUpdater *updater = new AudioPreviewUpdater
+ (*m_audioPreviewThread, m_composition, segment, segRect, this);
+
+ connect(updater, SIGNAL(audioPreviewComplete(AudioPreviewUpdater*)),
+ this, SLOT(slotAudioPreviewComplete(AudioPreviewUpdater*)));
+
+ m_audioPreviewUpdaterMap[segment] = updater;
+
+ } else {
+
+ m_audioPreviewUpdaterMap[segment]->setDisplayExtent(segRect);
+ }
+
+ m_audioPreviewUpdaterMap[segment]->update();
+
+ } else {
+ RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - no audio preview thread set\n";
+ }
+}
+
+void CompositionModelImpl::slotAudioPreviewComplete(AudioPreviewUpdater* apu)
+{
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete()\n";
+
+ AudioPreviewData *apData = getAudioPreviewData(apu->getSegment());
+ QRect updateRect;
+
+ if (apData) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete(" << apu << "): apData contains " << apData->getValues().size() << " values already" << endl;
+ unsigned int channels = 0;
+ const std::vector<float> &values = apu->getComputedValues(channels);
+ if (channels > 0) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete: set "
+ << values.size() << " samples on " << channels << " channels\n";
+ apData->setChannels(channels);
+ apData->setValues(values);
+ updateRect = postProcessAudioPreview(apData, apu->getSegment());
+ }
+ }
+
+ if (!updateRect.isEmpty())
+ emit needContentUpdate(updateRect);
+}
+
+QRect CompositionModelImpl::postProcessAudioPreview(AudioPreviewData* apData, const Segment* segment)
+{
+ // RG_DEBUG << "CompositionModelImpl::postProcessAudioPreview()\n";
+
+ AudioPreviewPainter previewPainter(*this, apData, m_composition, segment);
+ previewPainter.paintPreviewImage();
+
+ m_audioSegmentPreviewMap[segment] = previewPainter.getPreviewImage();
+
+ return previewPainter.getSegmentRect();
+}
+
+void CompositionModelImpl::slotInstrumentParametersChanged(InstrumentId id)
+{
+ std::cerr << "CompositionModelImpl::slotInstrumentParametersChanged()\n";
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ TrackId trackId = s->getTrack();
+ Track *track = getComposition().getTrackById(trackId);
+
+ // We need to update the cache for audio segments, because the
+ // instrument playback level is reflected in the audio
+ // preview. And we need to update it for midi segments,
+ // because the preview style differs depending on whether the
+ // segment is on a percussion instrument or not
+
+ if (track && track->getInstrument() == id) {
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+ }
+}
+
+void CompositionModelImpl::slotAudioFileFinalized(Segment* s)
+{
+ // RG_DEBUG << "CompositionModelImpl::slotAudioFileFinalized()\n";
+ removePreviewCache(s);
+}
+
+PixmapArray CompositionModelImpl::getAudioPreviewPixmap(const Segment* s)
+{
+ getAudioPreviewData(s);
+ return m_audioSegmentPreviewMap[s];
+}
+
+void CompositionModelImpl::eventAdded(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventAdded()\n";
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::eventRemoved(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventRemoved" << endl;
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::appearanceChanged(const Segment *s)
+{
+ // RG_DEBUG << "CompositionModelImpl::appearanceChanged" << endl;
+ clearInCache(s, true);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Segment *s, bool shorten)
+{
+ // RG_DEBUG << "CompositionModelImpl::endMarkerTimeChanged(" << shorten << ")" << endl;
+ clearInCache(s, true);
+ if (shorten) {
+ emit needContentUpdate(); // no longer know former segment dimension
+ } else {
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+}
+
+void CompositionModelImpl::makePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ makeNotationPreviewDataCache(s);
+ } else {
+ makeAudioPreviewDataCache(s);
+ }
+}
+
+void CompositionModelImpl::removePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ m_notationPreviewDataCache.remove(const_cast<Segment*>(s));
+ } else {
+ m_audioPreviewDataCache.remove(const_cast<Segment*>(s));
+ m_audioSegmentPreviewMap.erase(s);
+ }
+
+}
+
+void CompositionModelImpl::segmentAdded(const Composition *, Segment *s)
+{
+ std::cerr << "CompositionModelImpl::segmentAdded: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ setTrackHeights(s);
+
+ makePreviewCache(s);
+ s->addObserver(this);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentRemoved(const Composition *, Segment *s)
+{
+ setTrackHeights();
+
+ QRect r = computeSegmentRect(*s);
+
+ m_selectedSegments.erase(s);
+
+ clearInCache(s, true);
+ s->removeObserver(this);
+ m_recordingSegments.erase(s); // this could be a recording segment
+ emit needContentUpdate(r);
+}
+
+void CompositionModelImpl::segmentTrackChanged(const Composition *, Segment *s, TrackId tid)
+{
+ std::cerr << "CompositionModelImpl::segmentTrackChanged: segment " << s << " on track " << tid << ", calling setTrackHeights" << std::endl;
+
+ // we don't call setTrackHeights(s), because some of the tracks
+ // above s may have changed height as well (if s was moved off one
+ // of them)
+ if (setTrackHeights()) {
+ std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentStartChanged(const Composition *, Segment *s, timeT)
+{
+// std::cerr << "CompositionModelImpl::segmentStartChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentEndMarkerChanged(const Composition *, Segment *s, bool)
+{
+// std::cerr << "CompositionModelImpl::segmentEndMarkerChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) {
+// std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentRepeatChanged(const Composition *, Segment *s, bool)
+{
+ clearInCache(s);
+ setTrackHeights(s);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Composition *, bool)
+{
+ emit needSizeUpdate();
+}
+
+void CompositionModelImpl::setSelectionRect(const QRect& r)
+{
+ m_selectionRect = r.normalize();
+
+ m_previousTmpSelectedSegments = m_tmpSelectedSegments;
+ m_tmpSelectedSegments.clear();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ QRect updateRect = m_selectionRect;
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ m_tmpSelectedSegments.insert(s);
+ updateRect |= sr;
+ }
+ }
+
+ updateRect = updateRect.normalize();
+
+ if (!updateRect.isNull() && !m_previousSelectionUpdateRect.isNull()) {
+
+ if (m_tmpSelectedSegments != m_previousTmpSelectedSegments)
+ emit needContentUpdate(updateRect | m_previousSelectionUpdateRect);
+
+ emit needArtifactsUpdate();
+ }
+
+
+ m_previousSelectionUpdateRect = updateRect;
+
+}
+
+void CompositionModelImpl::finalizeSelectionRect()
+{
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ setSelected(s);
+ }
+ }
+
+ m_previousSelectionUpdateRect = m_selectionRect = QRect();
+ m_tmpSelectedSegments.clear();
+}
+
+QRect CompositionModelImpl::getSelectionContentsRect()
+{
+ QRect selectionRect;
+
+ SegmentSelection sel = getSelectedSegments();
+ for (SegmentSelection::iterator i = sel.begin();
+ i != sel.end(); ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ selectionRect |= sr;
+ }
+
+ return selectionRect;
+}
+
+void CompositionModelImpl::addRecordingItem(const CompositionItem& item)
+{
+ m_recordingSegments.insert(CompositionItemHelper::getSegment(item));
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::addRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::removeRecordingItem(const CompositionItem &item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+
+ m_recordingSegments.erase(s);
+ clearInCache(s, true);
+
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::removeRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::clearRecordingItems()
+{
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i)
+ clearInCache(*i, true);
+
+ m_recordingSegments.clear();
+
+ emit needContentUpdate();
+ RG_DEBUG << "CompositionModelImpl::clearRecordingItem\n";
+}
+
+bool CompositionModelImpl::isMoving(const Segment* sm) const
+{
+ itemcontainer::const_iterator movEnd = m_changingItems.end();
+
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ const CompositionItemImpl* ci = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)(*i));
+ const Segment* s = ci->getSegment();
+ if (sm == s)
+ return true;
+ }
+
+ return false;
+}
+
+bool CompositionModelImpl::isRecording(const Segment* s) const
+{
+ return m_recordingSegments.find(const_cast<Segment*>(s)) != m_recordingSegments.end();
+}
+
+CompositionModel::itemcontainer CompositionModelImpl::getItemsAt(const QPoint& point)
+{
+ itemcontainer res;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segments.end(); ++i) {
+
+ Segment* s = *i;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.contains(point)) {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() adding " << sr << " for segment " << s << endl;
+ CompositionItem item(new CompositionItemImpl(*s, sr));
+ unsigned int z = computeZForSegment(s);
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() z = " << z << endl;
+ item->setZ(z);
+ res.insert(item);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() skiping " << sr << endl;
+ }
+
+ }
+
+ if (res.size() == 1) { // only one segment under click point
+ Segment* s = CompositionItemHelper::getSegment(*(res.begin()));
+ m_segmentOrderer.segmentClicked(s);
+ }
+
+ return res;
+}
+
+void CompositionModelImpl::setPointerPos(int xPos)
+{
+ m_pointerTimePos = grid().getRulerScale()->getTimeForX(xPos);
+
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i) {
+ emit needContentUpdate(computeSegmentRect(**i));
+ }
+}
+
+void CompositionModelImpl::setSelected(const CompositionItem& item, bool selected)
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)item);
+ if (itemImpl) {
+ Segment* segment = const_cast<Segment*>(itemImpl->getSegment());
+ setSelected(segment, selected);
+ }
+}
+
+void CompositionModelImpl::setSelected(const itemcontainer& items)
+{
+ for (itemcontainer::const_iterator i = items.begin(); i != items.end(); ++i) {
+ setSelected(*i);
+ }
+}
+
+void CompositionModelImpl::setSelected(const Segment* segment, bool selected)
+{
+ RG_DEBUG << "CompositionModelImpl::setSelected " << segment << " - " << selected << endl;
+ if (selected) {
+ if (!isSelected(segment))
+ m_selectedSegments.insert(const_cast<Segment*>(segment));
+ } else {
+ SegmentSelection::iterator i = m_selectedSegments.find(const_cast<Segment*>(segment));
+ if (i != m_selectedSegments.end())
+ m_selectedSegments.erase(i);
+ }
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::signalSelection()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalSelection()\n";
+ emit selectedSegments(getSelectedSegments());
+}
+
+void CompositionModelImpl::signalContentChange()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalContentChange" << endl;
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::clearSelected()
+{
+ RG_DEBUG << "CompositionModelImpl::clearSelected" << endl;
+ m_selectedSegments.clear();
+ emit needContentUpdate();
+}
+
+bool CompositionModelImpl::isSelected(const CompositionItem& ci) const
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)ci);
+ return itemImpl ? isSelected(itemImpl->getSegment()) : 0;
+}
+
+bool CompositionModelImpl::isSelected(const Segment* s) const
+{
+ return m_selectedSegments.find(const_cast<Segment*>(s)) != m_selectedSegments.end();
+}
+
+bool CompositionModelImpl::isTmpSelected(const Segment* s) const
+{
+ return m_tmpSelectedSegments.find(const_cast<Segment*>(s)) != m_tmpSelectedSegments.end();
+}
+
+bool CompositionModelImpl::wasTmpSelected(const Segment* s) const
+{
+ return m_previousTmpSelectedSegments.find(const_cast<Segment*>(s)) != m_previousTmpSelectedSegments.end();
+}
+
+void CompositionModelImpl::startChange(const CompositionItem& item, CompositionModel::ChangeType change)
+{
+ m_changeType = change;
+
+ itemcontainer::iterator i = m_changingItems.find(item);
+
+ // if an "identical" composition item has already been inserted, drop this one
+ if (i != m_changingItems.end()) {
+ RG_DEBUG << "CompositionModelImpl::startChange : item already in\n";
+ m_itemGC.push_back(item);
+ } else {
+ item->saveRect();
+ m_changingItems.insert(item);
+ }
+}
+
+void CompositionModelImpl::startChangeSelection(CompositionModel::ChangeType change)
+{
+ SegmentSelection::iterator i = m_selectedSegments.begin();
+ for (; i != m_selectedSegments.end(); ++i) {
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ startChange(CompositionItem(new CompositionItemImpl(*s, sr)), change);
+ }
+
+}
+
+void CompositionModelImpl::endChange()
+{
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != m_changingItems.end(); ++i) {
+ delete *i;
+ }
+
+ m_changingItems.clear();
+
+ for (itemgc::iterator i = m_itemGC.begin(); i != m_itemGC.end(); ++i) {
+ delete *i;
+ }
+ m_itemGC.clear();
+ RG_DEBUG << "CompositionModelImpl::endChange\n";
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::setLength(int width)
+{
+ timeT endMarker = m_grid.snapX(width);
+ m_composition.setEndMarker(endMarker);
+}
+
+int CompositionModelImpl::getLength()
+{
+ timeT endMarker = m_composition.getEndMarker();
+ int w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(0, endMarker)));
+ return w;
+}
+
+timeT CompositionModelImpl::getRepeatTimeAt(const QPoint& p, const CompositionItem& cItem)
+{
+ // timeT timeAtClick = m_grid.getRulerScale()->getTimeForX(p.x());
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)cItem);
+
+ const Segment* s = itemImpl->getSegment();
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ int rWidth = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatInterval)));
+
+ int count = (p.x() - int(itemImpl->rect().x())) / rWidth;
+ RG_DEBUG << "CompositionModelImpl::getRepeatTimeAt() : count = " << count << endl;
+
+ return count != 0 ? startTime + (count * (s->getEndMarkerTime() - s->getStartTime())) : 0;
+}
+
+bool CompositionModelImpl::setTrackHeights(Segment *s)
+{
+ bool heightsChanged = false;
+
+// std::cerr << "CompositionModelImpl::setTrackHeights" << std::endl;
+
+ for (Composition::trackcontainer::const_iterator i =
+ m_composition.getTracks().begin();
+ i != m_composition.getTracks().end(); ++i) {
+
+ if (s && i->first != s->getTrack()) continue;
+
+ int max = m_composition.getMaxContemporaneousSegmentsOnTrack(i->first);
+ if (max == 0) max = 1;
+
+// std::cerr << "for track " << i->first << ": height = " << max << ", old height = " << m_trackHeights[i->first] << std::endl;
+
+ if (max != m_trackHeights[i->first]) {
+ heightsChanged = true;
+ m_trackHeights[i->first] = max;
+ }
+
+ m_grid.setBinHeightMultiple(i->second->getPosition(), max);
+ }
+
+ if (heightsChanged) {
+// std::cerr << "CompositionModelImpl::setTrackHeights: heights have changed" << std::endl;
+ for (Composition::segmentcontainer::iterator i = m_composition.begin();
+ i != m_composition.end(); ++i) {
+ computeSegmentRect(**i);
+ }
+ }
+
+ return heightsChanged;
+}
+
+QPoint CompositionModelImpl::computeSegmentOrigin(const Segment& s)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentOrigin", true);
+
+ int trackPosition = m_composition.getTrackPositionById(s.getTrack());
+ timeT startTime = s.getStartTime();
+
+ QPoint res;
+
+ res.setX(int(nearbyint(m_grid.getRulerScale()->getXForTime(startTime))));
+
+ res.setY(m_grid.getYBinCoordinate(trackPosition) +
+ m_composition.getSegmentVoiceIndex(&s) *
+ m_grid.getYSnap() + 1);
+
+ return res;
+}
+
+bool CompositionModelImpl::isCachedRectCurrent(const Segment& s, const CompositionRect& r, QPoint cachedSegmentOrigin, timeT cachedSegmentEndTime)
+{
+ return s.isRepeating() == r.isRepeating() &&
+ ((cachedSegmentOrigin.x() != r.x() && s.getEndMarkerTime() != cachedSegmentEndTime) ||
+ (cachedSegmentOrigin.x() == r.x() && s.getEndMarkerTime() == cachedSegmentEndTime));
+}
+
+void CompositionModelImpl::clearInCache(const Segment* s, bool clearPreview)
+{
+ if (s) {
+ m_segmentRectMap.erase(s);
+ m_segmentEndTimeMap.erase(s);
+ if (clearPreview)
+ removePreviewCache(s);
+ } else { // clear the whole cache
+ m_segmentRectMap.clear();
+ m_segmentEndTimeMap.clear();
+ if (clearPreview)
+ clearPreviewCache();
+ }
+}
+
+void CompositionModelImpl::putInCache(const Segment*s, const CompositionRect& cr)
+{
+ m_segmentRectMap[s] = cr;
+ m_segmentEndTimeMap[s] = s->getEndMarkerTime();
+}
+
+CompositionRect CompositionModelImpl::computeSegmentRect(const Segment& s, bool computeZ)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentRect", true);
+
+ QPoint origin = computeSegmentOrigin(s);
+
+ bool isRecordingSegment = isRecording(&s);
+
+ if (!isRecordingSegment) {
+ timeT endTime = 0;
+
+ CompositionRect cachedCR = getFromCache(&s, endTime);
+ // don't cache repeating segments - it's just hopeless, because the segment's rect may have to be recomputed
+ // in other cases than just when the segment itself is moved,
+ // for instance if another segment is moved over it
+ if (!s.isRepeating() && cachedCR.isValid() && isCachedRectCurrent(s, cachedCR, origin, endTime)) {
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect() : using cache for seg "
+ // << &s << " - cached rect repeating = " << cachedCR.isRepeating() << " - base width = "
+ // << cachedCR.getBaseWidth() << endl;
+
+ bool xChanged = origin.x() != cachedCR.x();
+ bool yChanged = origin.y() != cachedCR.y();
+
+ cachedCR.moveTopLeft(origin);
+
+ if (s.isRepeating() && (xChanged || yChanged)) { // update repeat marks
+
+ // this doesn't work in the general case (if there's another segment on the same track for instance),
+ // it's better to simply recompute all the marks
+ // CompositionRect::repeatmarks repeatMarks = cachedCR.getRepeatMarks();
+ // for(unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ // repeatMarks[i] += deltaX;
+ // }
+ // cachedCR.setRepeatMarks(repeatMarks);
+ computeRepeatMarks(cachedCR, &s);
+ }
+ putInCache(&s, cachedCR);
+ return cachedCR;
+ }
+ }
+
+ timeT startTime = s.getStartTime();
+ timeT endTime = isRecordingSegment ? m_pointerTimePos /*s.getEndTime()*/ : s.getEndMarkerTime();
+
+
+ int h = m_grid.getYSnap() - 2;
+ int w;
+
+ RG_DEBUG << "CompositionModelImpl::computeSegmentRect: x " << origin.x() << ", y " << origin.y() << " startTime " << startTime << ", endTime " << endTime << endl;
+
+ if (s.isRepeating()) {
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s.getRepeatEndTime();
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is repeating - repeatStart = "
+ // << repeatStart << " - repeatEnd : " << repeatEnd
+ // << " w = " << w << endl;
+ } else {
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime, endTime - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is NOT repeating"
+ // << " w = " << w << " (x for time at start is " << m_grid.getRulerScale()->getXForTime(startTime) << ", end is " << m_grid.getRulerScale()->getXForTime(endTime) << ")" << endl;
+ }
+
+ CompositionRect cr(origin, QSize(w, h));
+ QString label = strtoqstr(s.getLabel());
+ if (s.getType() == Segment::Audio) {
+ static QRegExp re1("( *\\([^)]*\\))*$"); // (inserted) (copied) (etc)
+ static QRegExp re2("\\.[^.]+$"); // filename suffix
+ label.replace(re1, "").replace(re2, "");
+ }
+ cr.setLabel(label);
+
+ if (s.isRepeating()) {
+ computeRepeatMarks(cr, &s);
+ } else {
+ cr.setBaseWidth(cr.width());
+ }
+
+ putInCache(&s, cr);
+
+ return cr;
+}
+
+unsigned int CompositionModelImpl::computeZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentOrderer.getZForSegment(s);
+}
+
+const CompositionRect& CompositionModelImpl::getFromCache(const Rosegarden::Segment* s, timeT& endTime)
+{
+ endTime = m_segmentEndTimeMap[s];
+ return m_segmentRectMap[s];
+}
+
+unsigned int CompositionModelImpl::getNbRows()
+{
+ return m_composition.getNbTracks();
+}
+
+const CompositionModel::rectcontainer& CompositionModelImpl::getRectanglesIn(const QRect& rect,
+ RectRanges* npData,
+ AudioPreviewDrawData* apData)
+{
+ // Profiler profiler("CompositionModelImpl::getRectanglesIn", true);
+
+ m_res.clear();
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: ruler scale is "
+ // << (dynamic_cast<SimpleRulerScale *>(m_grid.getRulerScale()))->getUnitsPerPixel() << endl;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: Composition contains segment " << *i << " (" << (*i)->getStartTime() << "->" << (*i)->getEndTime() << ")"<< endl;
+
+ Segment* s = *i;
+
+ if (isMoving(s))
+ continue;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: seg rect = " << sr << endl;
+
+ if (sr.intersects(rect)) {
+ bool tmpSelected = isTmpSelected(s),
+ pTmpSelected = wasTmpSelected(s);
+
+// RG_DEBUG << "CompositionModelImpl::getRectanglesIn: segment " << s
+// << " selected : " << isSelected(s) << " - tmpSelected : " << isTmpSelected(s) << endl;
+
+ if (isSelected(s) || isTmpSelected(s) || sr.intersects(m_selectionRect)) {
+ sr.setSelected(true);
+ }
+
+ if (pTmpSelected != tmpSelected)
+ sr.setNeedsFullUpdate(true);
+
+ bool isAudio = (s && s->getType() == Segment::Audio);
+
+ if (!isRecording(s)) {
+ QColor brushColor = GUIPalette::convertColour(m_composition.
+ getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ } else {
+ // border is the same for both audio and MIDI
+ sr.setPen(CompositionColourCache::getInstance()->RecordingSegmentBorder);
+ // audio color
+ if (isAudio) {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingAudioSegmentBlock);
+ // MIDI/default color
+ } else {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingInternalSegmentBlock);
+ }
+ }
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRects(npData, QPoint(0, sr.y()), s, rect);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: - segment out of rect\n";
+ }
+
+ }
+
+ // changing items
+
+ itemcontainer::iterator movEnd = m_changingItems.end();
+ for (itemcontainer::iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ CompositionRect sr((*i)->rect());
+ if (sr.intersects(rect)) {
+ Segment* s = CompositionItemHelper::getSegment(*i);
+ sr.setSelected(true);
+ QColor brushColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRectsMovingSegment(npData, sr.topLeft(), s, sr);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ }
+ }
+
+ return m_res;
+}
+
+CompositionModel::heightlist
+CompositionModelImpl::getTrackDividersIn(const QRect& rect)
+{
+ int top = m_grid.getYBin(rect.y());
+ int bottom = m_grid.getYBin(rect.y() + rect.height());
+
+// std::cerr << "CompositionModelImpl::getTrackDividersIn: rect "
+// << rect.x() << ", " << rect.y() << ", "
+// << rect.width() << "x" << rect.height() << ", top = " << top
+// << ", bottom = " << bottom << std::endl;
+
+ CompositionModel::heightlist list;
+
+ for (int pos = top; pos <= bottom; ++pos) {
+ int divider = m_grid.getYBinCoordinate(pos);
+ list.push_back(divider);
+// std::cerr << "divider at " << divider << std::endl;
+ }
+
+ return list;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::getNotationPreviewData(const Segment* s)
+{
+ rectlist* npData = m_notationPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!npData) {
+ npData = makeNotationPreviewDataCache(s);
+ }
+
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::getAudioPreviewData(const Segment* s)
+{
+ // Profiler profiler("CompositionModelImpl::getAudioPreviewData", true);
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData\n";
+
+ AudioPreviewData* apData = m_audioPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!apData) {
+ apData = makeAudioPreviewDataCache(s);
+ }
+
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData returning\n";
+ return apData;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::makeNotationPreviewDataCache(const Segment *s)
+{
+ rectlist* npData = new rectlist();
+ updatePreviewCacheForNotationSegment(s, npData);
+ m_notationPreviewDataCache.insert(const_cast<Segment*>(s), npData);
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::makeAudioPreviewDataCache(const Segment *s)
+{
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewDataCache(" << s << ")" << endl;
+
+ AudioPreviewData* apData = new AudioPreviewData(false, 0); // 0 channels -> empty
+ updatePreviewCacheForAudioSegment(s, apData);
+ m_audioPreviewDataCache.insert(const_cast<Segment*>(s), apData);
+ return apData;
+}
+
+}
+#include "CompositionModelImpl.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
new file mode 100644
index 0000000..6e1c9d6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
@@ -0,0 +1,239 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONMODELIMPL_H_
+#define _RG_COMPOSITIONMODELIMPL_H_
+
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include <map>
+#include "SegmentOrderer.h"
+#include <set>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qptrdict.h>
+#include <qrect.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+class AudioPreviewData;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Segment;
+class RulerScale;
+class Event;
+class Composition;
+class AudioPreviewUpdater;
+class AudioPreviewThread;
+
+
+class CompositionModelImpl : public CompositionModel
+{
+ Q_OBJECT
+public:
+
+ CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep);
+
+ virtual ~CompositionModelImpl();
+
+ virtual unsigned int getNbRows();
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects);
+ virtual heightlist getTrackDividersIn(const QRect& rect);
+ virtual itemcontainer getItemsAt (const QPoint&);
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&);
+
+ virtual SnapGrid& grid() { return m_grid; }
+
+ virtual void setPointerPos(int xPos);
+ virtual void setSelected(const CompositionItem&, bool selected = true);
+ virtual bool isSelected(const CompositionItem&) const;
+ virtual void setSelected(const itemcontainer&);
+ virtual void clearSelected();
+ virtual bool haveSelection() const { return !m_selectedSegments.empty(); }
+ virtual bool haveMultipleSelection() const { return m_selectedSegments.size() > 1; }
+ virtual void signalSelection();
+ virtual void setSelectionRect(const QRect&);
+ virtual void finalizeSelectionRect();
+ virtual QRect getSelectionContentsRect();
+ virtual void signalContentChange();
+
+ virtual void addRecordingItem(const CompositionItem&);
+ virtual void removeRecordingItem(const CompositionItem &);
+ virtual void clearRecordingItems();
+ virtual bool haveRecordingItems() { return m_recordingSegments.size() > 0; }
+
+ virtual void startChange(const CompositionItem&, ChangeType change);
+ virtual void startChangeSelection(ChangeType change);
+ virtual itemcontainer& getChangingItems() { return m_changingItems; }
+ virtual void endChange();
+ virtual ChangeType getChangeType() { return m_changeType; }
+
+ virtual void setLength(int width);
+ virtual int getLength();
+
+ void setAudioPreviewThread(AudioPreviewThread *thread);
+ AudioPreviewThread* getAudioPreviewThread() { return m_audioPreviewThread; }
+
+ void clearPreviewCache();
+ void clearSegmentRectsCache(bool clearPreviews = false) { clearInCache(0, clearPreviews); }
+
+ rectlist* makeNotationPreviewDataCache(const Segment *s);
+ AudioPreviewData* makeAudioPreviewDataCache(const Segment *s);
+
+ CompositionRect computeSegmentRect(const Segment&, bool computeZ = false);
+ QColor computeSegmentPreviewColor(const Segment*);
+ QPoint computeSegmentOrigin(const Segment&);
+ void computeRepeatMarks(CompositionItem&);
+
+ SegmentSelection getSelectedSegments() { return m_selectedSegments; }
+ Composition& getComposition() { return m_composition; }
+ Studio& getStudio() { return m_studio; }
+
+
+ // CompositionObserver
+ virtual void segmentAdded(const Composition *, Segment *);
+ virtual void segmentRemoved(const Composition *, Segment *);
+ virtual void segmentRepeatChanged(const Composition *, Segment *, bool);
+ virtual void segmentStartChanged(const Composition *, Segment *, timeT);
+ virtual void segmentEndMarkerChanged(const Composition *, Segment *, bool);
+ virtual void segmentTrackChanged(const Composition *, Segment *, TrackId);
+ virtual void endMarkerTimeChanged(const Composition *, bool /*shorten*/);
+
+ // SegmentObserver
+ virtual void eventAdded(const Segment *, Event *);
+ virtual void eventRemoved(const Segment *, Event *);
+ virtual void appearanceChanged(const Segment *);
+ virtual void endMarkerTimeChanged(const Segment *, bool /*shorten*/);
+ virtual void segmentDeleted(const Segment*) { /* nothing to do - handled by CompositionObserver::segmentRemoved() */ };
+
+signals:
+ void selectedSegments(const SegmentSelection &);
+ void needSizeUpdate();
+
+public slots:
+ void slotAudioFileFinalized(Segment*);
+ void slotInstrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotAudioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ bool setTrackHeights(Segment *changed = 0); // true if something changed
+
+ void setSelected(const Segment*, bool selected = true);
+ bool isSelected(const Segment*) const;
+ bool isTmpSelected(const Segment*) const;
+ bool wasTmpSelected(const Segment*) const;
+ bool isMoving(const Segment*) const;
+ bool isRecording(const Segment*) const;
+
+ void computeRepeatMarks(CompositionRect& sr, const Segment* s);
+ unsigned int computeZForSegment(const Segment* s);
+
+ // segment preview stuff
+
+ void updatePreviewCacheForNotationSegment(const Segment* s, rectlist*);
+ void updatePreviewCacheForAudioSegment(const Segment* s, AudioPreviewData*);
+ rectlist* getNotationPreviewData(const Segment* s);
+ AudioPreviewData* getAudioPreviewData(const Segment* s);
+ PixmapArray getAudioPreviewPixmap(const Segment* s);
+ QRect postProcessAudioPreview(AudioPreviewData*, const Segment*);
+
+ void makePreviewCache(const Segment* s);
+ void removePreviewCache(const Segment* s);
+ void makeNotationPreviewRects(RectRanges* npData, QPoint basePoint, const Segment*, const QRect&);
+ void makeNotationPreviewRectsMovingSegment(RectRanges* npData, QPoint basePoint, const Segment*,
+ const QRect&);
+ void makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment*,
+ const CompositionRect& segRect, const QRect& clipRect);
+
+ void clearInCache(const Segment*, bool clearPreviewCache = false);
+ void putInCache(const Segment*, const CompositionRect&);
+ const CompositionRect& getFromCache(const Segment*, timeT& endTime);
+ bool isCachedRectCurrent(const Segment& s, const CompositionRect& r,
+ QPoint segmentOrigin, timeT segmentEndTime);
+
+ //--------------- Data members ---------------------------------
+ Composition& m_composition;
+ Studio& m_studio;
+ SnapGrid m_grid;
+ SegmentSelection m_selectedSegments;
+ SegmentSelection m_tmpSelectedSegments;
+ SegmentSelection m_previousTmpSelectedSegments;
+
+ timeT m_pointerTimePos;
+
+ typedef std::set<Segment *> recordingsegmentset;
+ recordingsegmentset m_recordingSegments;
+
+ typedef std::vector<CompositionItem> itemgc;
+
+ AudioPreviewThread* m_audioPreviewThread;
+
+ typedef QPtrDict<rectlist> NotationPreviewDataCache;
+ typedef QPtrDict<AudioPreviewData> AudioPreviewDataCache;
+
+ NotationPreviewDataCache m_notationPreviewDataCache;
+ AudioPreviewDataCache m_audioPreviewDataCache;
+
+ rectcontainer m_res;
+ itemcontainer m_changingItems;
+ ChangeType m_changeType;
+ itemgc m_itemGC;
+
+ QRect m_selectionRect;
+ QRect m_previousSelectionUpdateRect;
+
+ std::map<const Segment*, CompositionRect> m_segmentRectMap;
+ std::map<const Segment*, timeT> m_segmentEndTimeMap;
+ std::map<const Segment*, PixmapArray> m_audioSegmentPreviewMap;
+ std::map<TrackId, int> m_trackHeights;
+
+ typedef std::map<const Segment*, AudioPreviewUpdater *>
+ AudioPreviewUpdaterMap;
+ AudioPreviewUpdaterMap m_audioPreviewUpdaterMap;
+
+ SegmentOrderer m_segmentOrderer;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
new file mode 100644
index 0000000..9e34d71
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
@@ -0,0 +1,42 @@
+/* -*- 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 "CompositionRect.h"
+#include "base/ColourMap.h"
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+ const QColor CompositionRect::DefaultPenColor = Qt::black;
+ const QColor CompositionRect::DefaultBrushColor = QColor(COLOUR_DEF_R, COLOUR_DEF_G, COLOUR_DEF_B);
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.h b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
new file mode 100644
index 0000000..3c3d2b6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
@@ -0,0 +1,108 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONRECT_H_
+#define _RG_COMPOSITIONRECT_H_
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+
+class QSize;
+class QPoint;
+
+
+namespace Rosegarden
+{
+
+class CompositionRect : public QRect
+{
+public:
+ typedef QValueVector<int> repeatmarks;
+
+ friend bool operator<(const CompositionRect&, const CompositionRect&);
+
+ CompositionRect() : QRect(), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor) {};
+ CompositionRect(const QRect& r) : QRect(r), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QPoint & bottomRight)
+ : QRect(topLeft, bottomRight), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QSize & size)
+ : QRect(topLeft, size), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(int left, int top, int width, int height)
+ : QRect(left, top, width, height), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+
+ void setResized(bool s) { m_resized = s; }
+ bool isResized() const { return m_resized; }
+ void setSelected(bool s) { m_selected = s; }
+ bool isSelected() const { return m_selected; }
+ bool needsFullUpdate() const { return m_needUpdate; }
+ void setNeedsFullUpdate(bool s) { m_needUpdate = s; }
+
+ void setZ(int z) { m_z = z; }
+ int z() const { return m_z; }
+
+ // brush, pen draw info
+ void setBrush(QBrush b) { m_brush = b; }
+ QBrush getBrush() const { return m_brush; }
+ void setPen(QPen b) { m_pen = b; }
+ QPen getPen() const { return m_pen; }
+
+ // repeating segments
+ void setRepeatMarks(const repeatmarks& rm) { m_repeatMarks = rm; }
+ const repeatmarks& getRepeatMarks() const { return m_repeatMarks; }
+ bool isRepeating() const { return m_repeatMarks.size() > 0; }
+ int getBaseWidth() const { return m_baseWidth; }
+ void setBaseWidth(int bw) { m_baseWidth = bw; }
+ QString getLabel() const { return m_label; }
+ void setLabel(QString l) { m_label = l; }
+
+ static const QColor DefaultPenColor;
+ static const QColor DefaultBrushColor;
+
+protected:
+ bool m_resized;
+ bool m_selected;
+ bool m_needUpdate;
+ QBrush m_brush;
+ QPen m_pen;
+ repeatmarks m_repeatMarks;
+ int m_baseWidth;
+ QString m_label;
+ int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.cpp b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
new file mode 100644
index 0000000..8e83a6b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
@@ -0,0 +1,1591 @@
+/* -*- 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 "CompositionView.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionColourCache.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionModelImpl.h"
+#include "CompositionRect.h"
+#include "AudioPreviewPainter.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/general/RosegardenScrollView.h"
+#include "SegmentSelector.h"
+#include "SegmentToolBox.h"
+#include "SegmentTool.h"
+#include <kmessagebox.h>
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qmemarray.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+CompositionView::CompositionView(RosegardenGUIDoc* doc,
+ CompositionModel* model,
+ QWidget * parent, const char * name, WFlags f)
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0)
+ : RosegardenScrollView(parent, name, f | WNoAutoErase | WStaticContents),
+#else
+ :
+ RosegardenScrollView(parent, name, f | WRepaintNoErase | WResizeNoErase | WStaticContents),
+#endif
+ m_model(model),
+ m_currentItem(0),
+ m_tool(0),
+ m_toolBox(0),
+ m_showPreviews(false),
+ m_showSegmentLabels(true),
+ m_fineGrain(false),
+ m_pencilOverExisting(false),
+ m_minWidth(m_model->getLength()),
+ m_stepSize(0),
+ m_rectFill(0xF0, 0xF0, 0xF0),
+ m_selectedRectFill(0x00, 0x00, 0xF0),
+ m_pointerPos(0),
+ m_pointerColor(GUIPalette::getColour(GUIPalette::Pointer)),
+ m_pointerWidth(4),
+ m_pointerPen(QPen(m_pointerColor, m_pointerWidth)),
+ m_tmpRect(QRect(QPoint(0, 0), QPoint( -1, -1))),
+ m_tmpRectFill(CompositionRect::DefaultBrushColor),
+ m_trackDividerColor(GUIPalette::getColour(GUIPalette::TrackDivider)),
+ m_drawGuides(false),
+ m_guideColor(GUIPalette::getColour(GUIPalette::MovementGuide)),
+ m_topGuidePos(0),
+ m_foreGuidePos(0),
+ m_drawSelectionRect(false),
+ m_drawTextFloat(false),
+ m_segmentsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_artifactsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_segmentsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_artifactsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_lastBufferRefreshX(0),
+ m_lastBufferRefreshY(0),
+ m_lastPointerRefreshX(0),
+ m_contextHelpShown(false)
+{
+ if (doc) {
+ m_toolBox = new SegmentToolBox(this, doc);
+
+ connect(m_toolBox, SIGNAL(showContextHelp(const QString &)),
+ this, SLOT(slotToolHelpChanged(const QString &)));
+ }
+
+ setDragAutoScroll(true);
+ setBackgroundMode(NoBackground);
+ viewport()->setBackgroundMode(NoBackground);
+ viewport()->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas));
+
+ slotUpdateSize();
+
+ QScrollBar* hsb = horizontalScrollBar();
+
+ // dynamically adjust content size when scrolling past current composition's end
+ connect(hsb, SIGNAL(nextLine()),
+ this, SLOT(scrollRight()));
+ connect(hsb, SIGNAL(prevLine()),
+ this, SLOT(scrollLeft()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotAllDrawBuffersNeedRefresh()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotContentsMoving(int, int)));
+
+ connect(model, SIGNAL(needContentUpdate()),
+ this, SLOT(slotUpdateSegmentsDrawBuffer()));
+ connect(model, SIGNAL(needContentUpdate(const QRect&)),
+ this, SLOT(slotUpdateSegmentsDrawBuffer(const QRect&)));
+ connect(model, SIGNAL(needArtifactsUpdate()),
+ this, SLOT(slotArtifactsDrawBufferNeedsRefresh()));
+ connect(model, SIGNAL(needSizeUpdate()),
+ this, SLOT(slotUpdateSize()));
+
+ if (doc) {
+ connect(doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotRefreshColourCache()));
+
+ // recording-related signals
+ connect(doc, SIGNAL(newMIDIRecordingSegment(Segment*)),
+ this, SLOT(slotNewMIDIRecordingSegment(Segment*)));
+ connect(doc, SIGNAL(newAudioRecordingSegment(Segment*)),
+ this, SLOT(slotNewAudioRecordingSegment(Segment*)));
+ // connect(doc, SIGNAL(recordMIDISegmentUpdated(Segment*, timeT)),
+ // this, SLOT(slotRecordMIDISegmentUpdated(Segment*, timeT)));
+ connect(doc, SIGNAL(stoppedAudioRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(stoppedMIDIRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(audioFileFinalized(Segment*)),
+ getModel(), SLOT(slotAudioFileFinalized(Segment*)));
+ }
+
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(&doc->getAudioPreviewThread());
+ }
+
+ if (doc) {
+ doc->getAudioPreviewThread().setEmptyQueueListener(this);
+ }
+
+ m_segmentsDrawBuffer.setOptimization(QPixmap::BestOptim);
+ m_artifactsDrawBuffer.setOptimization(QPixmap::BestOptim);
+
+ viewport()->setMouseTracking(true);
+}
+
+void CompositionView::endAudioPreviewGeneration()
+{
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(m_model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(0);
+ }
+}
+
+void CompositionView::setBackgroundPixmap(const QPixmap &m)
+{
+ m_backgroundPixmap = m;
+ // viewport()->setErasePixmap(m_backgroundPixmap);
+}
+
+void CompositionView::initStepSize()
+{
+ QScrollBar* hsb = horizontalScrollBar();
+ m_stepSize = hsb->lineStep();
+}
+
+void CompositionView::slotUpdateSize()
+{
+ int vStep = getModel()->grid().getYSnap();
+ int height = std::max(getModel()->getNbRows() * vStep, (unsigned)visibleHeight());
+
+ RulerScale *ruler = grid().getRulerScale();
+
+ int minWidth = sizeHint().width();
+ int computedWidth = int(nearbyint(ruler->getTotalWidth()));
+
+ int width = std::max(computedWidth, minWidth);
+
+ resizeContents(width, height);
+}
+
+void CompositionView::scrollRight()
+{
+ RG_DEBUG << "CompositionView::scrollRight()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::scrollLeft()
+{
+ RG_DEBUG << "CompositionView::scrollLeft()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ int cWidth = contentsWidth();
+
+ if (horizontalScrollBar()->value() < cWidth && cWidth > m_minWidth) {
+ resizeContents(cWidth - m_stepSize, contentsHeight());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::setSelectionRectPos(const QPoint& pos)
+{
+ m_selectionRect.setRect(pos.x(), pos.y(), 0, 0);
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setSelectionRectSize(int w, int h)
+{
+ m_selectionRect.setSize(QSize(w, h));
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setDrawSelectionRect(bool d)
+{
+ if (m_drawSelectionRect != d) {
+ m_drawSelectionRect = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+ slotUpdateSegmentsDrawBuffer(m_selectionRect);
+ }
+}
+
+void CompositionView::clearSegmentRectsCache(bool clearPreviews)
+{
+ dynamic_cast<CompositionModelImpl*>(getModel())->clearSegmentRectsCache(clearPreviews);
+}
+
+SegmentSelection
+CompositionView::getSelectedSegments()
+{
+ return (dynamic_cast<CompositionModelImpl*>(m_model))->getSelectedSegments();
+}
+
+void CompositionView::updateSelectionContents()
+{
+ if (!haveSelection())
+ return ;
+
+
+ QRect selectionRect = getModel()->getSelectionContentsRect();
+ updateContents(selectionRect);
+}
+
+void CompositionView::slotContentsMoving(int x, int y)
+{
+ // qDebug("contents moving : x=%d", x);
+}
+
+void CompositionView::slotSetTool(const QString& toolName)
+{
+ RG_DEBUG << "CompositionView::slotSetTool(" << toolName << ")"
+ << this << "\n";
+
+ if (m_tool)
+ m_tool->stow();
+
+ m_toolContextHelp = "";
+
+ m_tool = m_toolBox->getTool(toolName);
+
+ if (m_tool)
+ m_tool->ready();
+ else {
+ KMessageBox::error(0, QString("CompositionView::slotSetTool() : unknown tool name %1").arg(toolName));
+ }
+}
+
+void CompositionView::slotSelectSegments(const SegmentSelection &segments)
+{
+ RG_DEBUG << "CompositionView::slotSelectSegments\n";
+
+ static QRect dummy;
+
+ getModel()->clearSelected();
+
+ for (SegmentSelection::iterator i = segments.begin(); i != segments.end(); ++i) {
+ getModel()->setSelected(CompositionItem(new CompositionItemImpl(**i, dummy)));
+ }
+ slotUpdateSegmentsDrawBuffer();
+}
+
+SegmentSelector*
+CompositionView::getSegmentSelectorTool()
+{
+ return dynamic_cast<SegmentSelector*>(getToolBox()->getTool(SegmentSelector::ToolName));
+}
+
+void CompositionView::slotSetSelectAdd(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentAdd(value);
+}
+
+void CompositionView::slotSetSelectCopy(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentCopy(value);
+}
+
+void CompositionView::slotShowSplitLine(int x, int y)
+{
+ m_splitLinePos.setX(x);
+ m_splitLinePos.setY(y);
+}
+
+void CompositionView::slotHideSplitLine()
+{
+ m_splitLinePos.setX( -1);
+ m_splitLinePos.setY( -1);
+}
+
+void CompositionView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ e->accept();
+ wheelEvent(e);
+}
+
+CompositionItem CompositionView::getFirstItemAt(QPoint pos)
+{
+ CompositionModel::itemcontainer items = getModel()->getItemsAt(pos);
+
+ if (items.size()) {
+ // find topmost item
+ CompositionItem res = *(items.begin());
+
+ unsigned int maxZ = res->z();
+
+ CompositionModel::itemcontainer::iterator maxZItemPos = items.begin();
+
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i) {
+ CompositionItem ic = *i;
+ if (ic->z() > maxZ) {
+ RG_DEBUG << k_funcinfo << "found new topmost at z=" << ic->z() << endl;
+ res = ic;
+ maxZ = ic->z();
+ maxZItemPos = i;
+ }
+ }
+
+ // get rid of the rest;
+ items.erase(maxZItemPos);
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i)
+ delete *i;
+
+ return res;
+ } else {
+ RG_DEBUG << k_funcinfo << "no item under cursor\n";
+ }
+
+
+ return CompositionItem();
+}
+
+void CompositionView::setSnapGrain(bool fine)
+{
+ if (m_fineGrain) {
+ grid().setSnapTime(SnapGrid::NoSnap);
+ } else {
+ grid().setSnapTime(fine ? SnapGrid::SnapToBeat : SnapGrid::SnapToBar);
+ }
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer()
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer()\n";
+ slotAllDrawBuffersNeedRefresh();
+ updateContents();
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer() rect "
+ // << rect << " - valid : " << rect.isValid() << endl;
+
+ slotAllDrawBuffersNeedRefresh(rect);
+
+ if (rect.isValid()) {
+ updateContents(rect);
+ } else {
+ updateContents();
+ }
+}
+
+void CompositionView::slotRefreshColourCache()
+{
+ CompositionColourCache::getInstance()->init();
+ clearSegmentRectsCache();
+ slotUpdateSegmentsDrawBuffer();
+}
+
+void CompositionView::slotNewMIDIRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotNewAudioRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotStoppedRecording()
+{
+ getModel()->clearRecordingItems();
+}
+
+void CompositionView::resizeEvent(QResizeEvent* e)
+{
+ QScrollView::resizeEvent(e);
+ slotUpdateSize();
+
+ int w = std::max(m_segmentsDrawBuffer.width(), visibleWidth());
+ int h = std::max(m_segmentsDrawBuffer.height(), visibleHeight());
+
+ m_segmentsDrawBuffer.resize(w, h);
+ m_artifactsDrawBuffer.resize(w, h);
+ slotAllDrawBuffersNeedRefresh();
+ // RG_DEBUG << "CompositionView::resizeEvent() : drawBuffer size = " << m_segmentsDrawBuffer.size() << endl;
+}
+
+void CompositionView::viewportPaintEvent(QPaintEvent* e)
+{
+ QMemArray<QRect> rects = e->region().rects();
+
+ for (unsigned int i = 0; i < rects.size(); ++i) {
+ viewportPaintRect(rects[i]);
+ }
+}
+
+void CompositionView::viewportPaintRect(QRect r)
+{
+ QRect updateRect = r;
+
+ r &= viewport()->rect();
+ r.moveBy(contentsX(), contentsY());
+
+ // RG_DEBUG << "CompositionView::viewportPaintRect() r = " << r
+ // << " - moveBy " << contentsX() << "," << contentsY() << " - updateRect = " << updateRect
+ // << " - refresh " << m_segmentsDrawBufferRefresh << " artrefresh " << m_artifactsDrawBufferRefresh << endl;
+
+
+ bool scroll = false;
+ bool changed = checkScrollAndRefreshDrawBuffer(r, scroll);
+
+ if (changed || m_artifactsDrawBufferRefresh.isValid()) {
+
+ // r was modified by checkScrollAndRefreshDrawBuffer
+ QRect copyRect(r | m_artifactsDrawBufferRefresh);
+ copyRect.moveBy( -contentsX(), -contentsY());
+
+ // RG_DEBUG << "copying from segment to artifacts buffer: " << copyRect << endl;
+
+ bitBlt(&m_artifactsDrawBuffer,
+ copyRect.x(), copyRect.y(),
+ &m_segmentsDrawBuffer,
+ copyRect.x(), copyRect.y(), copyRect.width(), copyRect.height());
+ m_artifactsDrawBufferRefresh |= r;
+ }
+
+ if (m_artifactsDrawBufferRefresh.isValid()) {
+ refreshArtifactsDrawBuffer(m_artifactsDrawBufferRefresh);
+ m_artifactsDrawBufferRefresh = QRect();
+ }
+
+ if (scroll) {
+ bitBlt(viewport(), 0, 0,
+ &m_artifactsDrawBuffer, 0, 0,
+ m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ } else {
+ bitBlt(viewport(), updateRect.x(), updateRect.y(),
+ &m_artifactsDrawBuffer, updateRect.x(), updateRect.y(),
+ updateRect.width(), updateRect.height());
+ }
+
+ // DEBUG
+
+ // QPainter pdebug(viewport());
+ // static QPen framePen(Qt::red, 1);
+ // pdebug.setPen(framePen);
+ // pdebug.drawRect(updateRect);
+
+}
+
+bool CompositionView::checkScrollAndRefreshDrawBuffer(QRect &rect, bool& scroll)
+{
+ bool all = false;
+ QRect refreshRect = m_segmentsDrawBufferRefresh;
+
+ int w = visibleWidth(), h = visibleHeight();
+ int cx = contentsX(), cy = contentsY();
+
+ scroll = (cx != m_lastBufferRefreshX || cy != m_lastBufferRefreshY);
+
+ if (scroll) {
+
+ // RG_DEBUG << "checkScrollAndRefreshDrawBuffer: scrolling by ("
+ // << cx - m_lastBufferRefreshX << "," << cy - m_lastBufferRefreshY << ")" << endl;
+
+ if (refreshRect.isValid()) {
+
+ // If we've scrolled and there was an existing refresh
+ // rect, we can't be sure whether the refresh rect
+ // predated or postdated the internal update of scroll
+ // location. Cut our losses and refresh everything.
+
+ refreshRect.setRect(cx, cy, w, h);
+
+ } else {
+
+ // No existing refresh rect: we only need to handle the
+ // scroll
+
+ if (cx != m_lastBufferRefreshX) {
+
+ int dx = m_lastBufferRefreshX - cx;
+
+ if (dx > -w && dx < w) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(dx, 0, m_segmentsDrawBuffer);
+
+ if (dx < 0) {
+ refreshRect |= QRect(cx + w + dx, cy, -dx, h);
+ } else {
+ refreshRect |= QRect(cx, cy, dx, h);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+
+ if (cy != m_lastBufferRefreshY && !all) {
+
+ int dy = m_lastBufferRefreshY - cy;
+
+ if (dy > -h && dy < h) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(0, dy, m_segmentsDrawBuffer);
+
+ if (dy < 0) {
+ refreshRect |= QRect(cx, cy + h + dy, w, -dy);
+ } else {
+ refreshRect |= QRect(cx, cy, w, dy);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+ }
+ }
+
+ bool needRefresh = false;
+
+ if (refreshRect.isValid()) {
+ needRefresh = true;
+ }
+
+ if (needRefresh)
+ refreshSegmentsDrawBuffer(refreshRect);
+
+ m_segmentsDrawBufferRefresh = QRect();
+ m_lastBufferRefreshX = cx;
+ m_lastBufferRefreshY = cy;
+
+ rect |= refreshRect;
+ if (scroll)
+ rect.setRect(cx, cy, w, h);
+ return needRefresh;
+}
+
+void CompositionView::refreshSegmentsDrawBuffer(const QRect& rect)
+{
+ // Profiler profiler("CompositionView::refreshDrawBuffer", true);
+ // RG_DEBUG << "CompositionView::refreshSegmentsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p(&m_segmentsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+
+ if (!m_backgroundPixmap.isNull()) {
+ QPoint pp(rect.x() % m_backgroundPixmap.height(), rect.y() % m_backgroundPixmap.width());
+ p.drawTiledPixmap(rect, m_backgroundPixmap, pp);
+ } else {
+ p.eraseRect(rect);
+ }
+
+ drawArea(&p, rect);
+
+ // DEBUG - show what's updated
+ // QPen framePen(Qt::red, 1);
+ // p.setPen(framePen);
+ // p.drawRect(rect);
+
+ // m_segmentsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::refreshArtifactsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::refreshArtifactsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p;
+ p.begin(&m_artifactsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+ // QRect r(contentsX(), contentsY(), m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ drawAreaArtifacts(&p, rect);
+ p.end();
+
+ // m_artifactsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::drawArea(QPainter *p, const QRect& clipRect)
+{
+ // Profiler profiler("CompositionView::drawArea", true);
+
+ // RG_DEBUG << "CompositionView::drawArea() clipRect = " << clipRect << endl;
+
+ //
+ // Fetch track dividing lines
+ //
+ CompositionModel::heightlist lineHeights = getModel()->getTrackDividersIn(clipRect);
+
+ if (!lineHeights.empty()) {
+
+ p->save();
+ QColor light = m_trackDividerColor.light();
+ p->setPen(light);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-1,
+ clipRect.x() + clipRect.width() - 1, y-1);
+ }
+ if (y >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y,
+ clipRect.x() + clipRect.width() - 1, y);
+ }
+ }
+
+ p->setPen(m_trackDividerColor);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-2 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-2,
+ clipRect.x() + clipRect.width() - 1, y-2);
+ }
+ if (y+1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y+1,
+ clipRect.x() + clipRect.width() - 1, y+1);
+ }
+ }
+
+ p->restore();
+ }
+
+ CompositionModel::AudioPreviewDrawData* audioPreviewData = 0;
+ CompositionModel::RectRanges* notationPreviewData = 0;
+
+ //
+ // Fetch previews
+ //
+ if (m_showPreviews) {
+ notationPreviewData = &m_notationPreviewRects;
+ m_notationPreviewRects.clear();
+ audioPreviewData = &m_audioPreviewRects;
+ m_audioPreviewRects.clear();
+ }
+
+ //
+ // Fetch segment rectangles to draw
+ //
+ const CompositionModel::rectcontainer& rects = getModel()->getRectanglesIn(clipRect,
+ notationPreviewData, audioPreviewData);
+ CompositionModel::rectcontainer::const_iterator i = rects.begin();
+ CompositionModel::rectcontainer::const_iterator end = rects.end();
+
+ //
+ // Draw Segment Rectangles
+ //
+ p->save();
+ for (; i != end; ++i) {
+ p->setBrush(i->getBrush());
+ p->setPen(i->getPen());
+
+ // RG_DEBUG << "CompositionView::drawArea : draw comp rect " << *i << endl;
+ drawCompRect(*i, p, clipRect);
+ }
+
+ p->restore();
+
+ if (rects.size() > 1) {
+ // RG_DEBUG << "CompositionView::drawArea : drawing intersections\n";
+ drawIntersections(rects, p, clipRect);
+ }
+
+ //
+ // Previews
+ //
+ if (m_showPreviews) {
+ p->save();
+
+ // draw audio previews
+ //
+ drawAreaAudioPreviews(p, clipRect);
+
+ // draw notation previews
+ //
+ CompositionModel::RectRanges::const_iterator npi = m_notationPreviewRects.begin();
+ CompositionModel::RectRanges::const_iterator npEnd = m_notationPreviewRects.end();
+
+ for (; npi != npEnd; ++npi) {
+ CompositionModel::RectRange interval = *npi;
+ p->save();
+ p->translate(interval.basePoint.x(), interval.basePoint.y());
+ // RG_DEBUG << "CompositionView::drawArea : translating to x = " << interval.basePoint.x() << endl;
+ for (; interval.range.first != interval.range.second; ++interval.range.first) {
+
+ const PreviewRect& pr = *(interval.range.first);
+ QColor defaultCol = CompositionColourCache::getInstance()->SegmentInternalPreview;
+ QColor col = interval.color.isValid() ? interval.color : defaultCol;
+ p->setBrush(col);
+ p->setPen(col);
+ // RG_DEBUG << "CompositionView::drawArea : drawing preview rect at x = " << pr.x() << endl;
+ p->drawRect(pr);
+ }
+ p->restore();
+ }
+
+ p->restore();
+ }
+
+ //
+ // Draw segment labels (they must be drawn over the preview rects)
+ //
+ if (m_showSegmentLabels) {
+ for (i = rects.begin(); i != end; ++i) {
+ drawCompRectLabel(*i, p, clipRect);
+ }
+ }
+
+ // drawAreaArtifacts(p, clipRect);
+
+}
+
+void CompositionView::drawAreaAudioPreviews(QPainter * p, const QRect& clipRect)
+{
+ CompositionModel::AudioPreviewDrawData::const_iterator api = m_audioPreviewRects.begin();
+ CompositionModel::AudioPreviewDrawData::const_iterator apEnd = m_audioPreviewRects.end();
+ QRect rectToFill, // rect to fill on canvas
+ localRect; // the rect of the tile to draw on the canvas
+ QPoint basePoint, // origin of segment rect
+ drawBasePoint; // origin of rect to fill on canvas
+ QRect r;
+ for (; api != apEnd; ++api) {
+ rectToFill = api->rect;
+ basePoint = api->basePoint;
+ rectToFill.moveTopLeft(basePoint);
+ rectToFill &= clipRect;
+ r = rectToFill;
+ drawBasePoint = rectToFill.topLeft();
+ rectToFill.moveBy( -basePoint.x(), -basePoint.y());
+ int firstPixmapIdx = (r.x() - basePoint.x()) / AudioPreviewPainter::tileWidth();
+ if (firstPixmapIdx >= api->pixmap.size()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : WARNING - miscomputed pixmap array : r.x = "
+ // << r.x() << " - basePoint.x = " << basePoint.x() << " - firstPixmapIdx = " << firstPixmapIdx
+ // << endl;
+ continue;
+ }
+ int x = 0, idx = firstPixmapIdx;
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : clipRect = " << clipRect
+ // << " - firstPixmapIdx = " << firstPixmapIdx << endl;
+ while (x < clipRect.width()) {
+ int pixmapRectXOffset = idx * AudioPreviewPainter::tileWidth();
+ localRect.setRect(basePoint.x() + pixmapRectXOffset, basePoint.y(),
+ AudioPreviewPainter::tileWidth(), api->rect.height());
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : initial localRect = "
+ // << localRect << endl;
+ localRect &= r;
+ if (idx == firstPixmapIdx && api->resizeOffset != 0) {
+ // this segment is being resized from start, clip beginning of preview
+ localRect.moveBy(api->resizeOffset, 0);
+ }
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect = "
+ // << localRect << endl;
+ if (localRect.isEmpty()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect is empty\n";
+ break;
+ }
+ localRect.moveBy( -(basePoint.x() + pixmapRectXOffset), -basePoint.y());
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : drawing pixmap "
+ // << idx << " at " << drawBasePoint << " - localRect = " << localRect
+ // << " - preResizeOrigin : " << api->preResizeOrigin << endl;
+
+ p->drawImage(drawBasePoint, api->pixmap[idx], localRect,
+ Qt::ColorOnly | Qt::ThresholdDither | Qt::AvoidDither);
+
+ ++idx;
+ if (idx >= api->pixmap.size())
+ break;
+ drawBasePoint.setX(drawBasePoint.x() + localRect.width());
+ x += localRect.width();
+ }
+ }
+}
+
+void CompositionView::drawAreaArtifacts(QPainter * p, const QRect& clipRect)
+{
+ //
+ // Playback Pointer
+ //
+ drawPointer(p, clipRect);
+
+ //
+ // Tmp rect (rect displayed while drawing a new segment)
+ //
+ if (m_tmpRect.isValid() && m_tmpRect.intersects(clipRect)) {
+ p->setBrush(m_tmpRectFill);
+ p->setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ drawRect(m_tmpRect, p, clipRect);
+ }
+
+ //
+ // Tool guides (crosshairs)
+ //
+ if (m_drawGuides)
+ drawGuides(p, clipRect);
+
+ //
+ // Selection Rect
+ //
+ if (m_drawSelectionRect) {
+ drawRect(m_selectionRect, p, clipRect, false, 0, false);
+ }
+
+ //
+ // Floating Text
+ //
+ if (m_drawTextFloat)
+ drawTextFloat(p, clipRect);
+
+ //
+ // Split line
+ //
+ if (m_splitLinePos.x() > 0 && clipRect.contains(m_splitLinePos)) {
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(m_splitLinePos.x(), m_splitLinePos.y(),
+ m_splitLinePos.x(), m_splitLinePos.y() + getModel()->grid().getYSnap());
+ p->restore();
+ }
+}
+
+void CompositionView::drawGuides(QPainter * p, const QRect& /*clipRect*/)
+{
+ // no need to check for clipping, these guides are meant to follow the mouse cursor
+ QPoint guideOrig(m_topGuidePos, m_foreGuidePos);
+
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(guideOrig.x(), 0, guideOrig.x(), contentsHeight());
+ p->drawLine(0, guideOrig.y(), contentsWidth(), guideOrig.y());
+ p->restore();
+}
+
+void CompositionView::drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl, bool fill)
+{
+ p->save();
+
+ QBrush brush = r.getBrush();
+
+ if (r.isRepeating()) {
+ QColor brushColor = brush.color();
+ brush.setColor(brushColor.light(150));
+ }
+
+ p->setBrush(brush);
+ p->setPen(r.getPen());
+ drawRect(r, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+ if (r.isRepeating()) {
+
+ CompositionRect::repeatmarks repeatMarks = r.getRepeatMarks();
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeating rect " << r
+ // << " nb repeat marks = " << repeatMarks.size() << endl;
+
+ // draw 'start' rectangle with original brush
+ //
+ QRect startRect = r;
+ startRect.setWidth(repeatMarks[0] - r.x());
+ p->setBrush(r.getBrush());
+ drawRect(startRect, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+
+ // now draw the 'repeat' marks
+ //
+ p->setPen(CompositionColourCache::getInstance()->RepeatSegmentBorder);
+ int penWidth = std::max(r.getPen().width(), 1u);
+
+ for (unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ int pos = repeatMarks[i];
+ if (pos > clipRect.right())
+ break;
+
+ if (pos >= clipRect.left()) {
+ QPoint p1(pos, r.y() + penWidth),
+ p2(pos, r.y() + r.height() - penWidth - 1);
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeat mark at "
+ // << p1 << "-" << p2 << endl;
+ p->drawLine(p1, p2);
+ }
+
+ }
+
+ }
+
+ p->restore();
+}
+
+void CompositionView::drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect)
+{
+ // draw segment label
+ //
+#ifdef NOT_DEFINED
+ if (!r.getLabel().isEmpty() /* && !r.isSelected() */)
+ {
+ p->save();
+ p->setPen(GUIPalette::getColour(GUIPalette::SegmentLabel));
+ p->setBrush(white);
+ QRect textRect(r);
+ textRect.setX(textRect.x() + 3);
+ QString label = " " + r.getLabel() + " ";
+ QRect textBoundingRect = p->boundingRect(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->drawRect(textBoundingRect & r);
+ p->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->restore();
+ }
+#else
+ if (!r.getLabel().isEmpty()) {
+
+ p->save();
+
+ QFont font;
+ font.setPixelSize(r.height() / 2.2);
+ font.setWeight(QFont::Bold);
+ font.setItalic(false);
+ p->setFont(font);
+
+ QRect labelRect = QRect
+ (r.x(),
+ r.y() + ((r.height() - p->fontMetrics().height()) / 2) + 1,
+ r.width(),
+ p->fontMetrics().height());
+
+ int x = labelRect.x() + p->fontMetrics().width('x');
+ int y = labelRect.y();
+
+ QBrush brush = r.getBrush();
+ QColor surroundColour = brush.color().light(110);
+
+ int h, s, v;
+ surroundColour.hsv(&h, &s, &v);
+ if (v < 150)
+ surroundColour.setHsv(h, s, 225);
+ p->setPen(surroundColour);
+
+ for (int i = 0; i < 9; ++i) {
+
+ if (i == 4)
+ continue;
+
+ int wx = x, wy = y;
+
+ if (i < 3)
+ --wx;
+ if (i > 5)
+ ++wx;
+ if (i % 3 == 0)
+ --wy;
+ if (i % 3 == 2)
+ ++wy;
+
+ labelRect.setX(wx);
+ labelRect.setY(wy);
+
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignTop,
+ r.getLabel());
+ }
+
+ labelRect.setX(x);
+ labelRect.setY(y);
+
+ p->setPen(GUIPalette::getColour
+ (GUIPalette::SegmentLabel));
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignVCenter, r.getLabel());
+ p->restore();
+ }
+#endif
+}
+
+void CompositionView::drawRect(const QRect& r, QPainter *p, const QRect& clipRect,
+ bool isSelected, int intersectLvl, bool fill)
+{
+ // RG_DEBUG << "CompositionView::drawRect : intersectLvl = " << intersectLvl
+ // << " - brush col = " << p->brush().color() << endl;
+
+ // RG_DEBUG << "CompositionView::drawRect " << r << " - xformed : " << p->xForm(r)
+ // << " - contents x = " << contentsX() << ", contents y = " << contentsY() << endl;
+
+ p->save();
+
+ QRect rect = r;
+
+ if (fill) {
+ if (isSelected) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark(200);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : selected color : " << fillColor << endl;
+ }
+
+ if (intersectLvl > 0) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark((intersectLvl) * 105);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : intersected color : " << fillColor << " isSelected : " << isSelected << endl;
+ }
+ } else {
+ p->setBrush(Qt::NoBrush);
+ }
+
+ // Paint using the small coordinates...
+ QRect intersection = rect.intersect(clipRect);
+
+ if (clipRect.contains(rect)) {
+ p->drawRect(rect);
+ } else {
+ // draw only what's necessary
+ if (!intersection.isEmpty() && fill)
+ p->fillRect(intersection, p->brush());
+
+ int rectTopY = rect.y();
+
+ if (rectTopY >= clipRect.y() &&
+ rectTopY <= (clipRect.y() + clipRect.height())) {
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.topLeft(), intersection.topRight());
+ }
+
+ int rectBottomY = rect.y() + rect.height();
+ if (rectBottomY >= clipRect.y() &&
+ rectBottomY <= (clipRect.y() + clipRect.height()))
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.bottomLeft(), intersection.bottomRight());
+
+ int rectLeftX = rect.x();
+ if (rectLeftX >= clipRect.x() &&
+ rectLeftX <= (clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topLeft(), rect.bottomLeft());
+
+ unsigned int rectRightX = rect.x() + rect.width(); // make sure we don't overflow
+ if (rectRightX >= unsigned(clipRect.x()) &&
+ rectRightX <= unsigned(clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topRight(), rect.bottomRight());
+
+ }
+
+ p->restore();
+}
+
+QColor CompositionView::mixBrushes(QBrush a, QBrush b)
+{
+ QColor ac = a.color(), bc = b.color();
+
+ int aR = ac.red(), aG = ac.green(), aB = ac.blue(),
+ bR = bc.red(), bG = bc.green(), bB = ac.blue();
+
+ ac.setRgb((aR + bR) / 2, (aG + bG) / 2, (aB + bB) / 2);
+
+ return ac;
+}
+
+void CompositionView::drawIntersections(const CompositionModel::rectcontainer& rects,
+ QPainter * p, const QRect& clipRect)
+{
+ if (! (rects.size() > 1))
+ return ;
+
+ CompositionModel::rectcontainer intersections;
+
+ CompositionModel::rectcontainer::const_iterator i = rects.begin(),
+ j = rects.begin();
+
+ for (; j != rects.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == rects.end())
+ break;
+
+ for (; i != rects.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty()) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections.begin(),
+ intersections.end(), ri);
+ if (t == intersections.end()) {
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ ri.setSelected(testRect.isSelected() || i->isSelected());
+ intersections.push_back(ri);
+ }
+
+ }
+ }
+ }
+
+ //
+ // draw this level of intersections then compute and draw further ones
+ //
+ int intersectionLvl = 1;
+
+ while (!intersections.empty()) {
+
+ for (CompositionModel::rectcontainer::iterator intIter = intersections.begin();
+ intIter != intersections.end(); ++intIter) {
+ CompositionRect r = *intIter;
+ drawCompRect(r, p, clipRect, intersectionLvl);
+ }
+
+ if (intersections.size() > 10)
+ break; // put a limit on how many intersections we can compute and draw - this grows exponentially
+
+ ++intersectionLvl;
+
+ CompositionModel::rectcontainer intersections2;
+
+ CompositionModel::rectcontainer::iterator i = intersections.begin(),
+ j = intersections.begin();
+
+ for (; j != intersections.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == intersections.end())
+ break;
+
+ for (; i != intersections.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty() && ri != *i) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections2.begin(),
+ intersections2.end(), ri);
+ if (t == intersections2.end())
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ intersections2.push_back(ri);
+ }
+ }
+ }
+
+ intersections = intersections2;
+ }
+
+}
+
+void CompositionView::drawPointer(QPainter *p, const QRect& clipRect)
+{
+ // RG_DEBUG << "CompositionView::drawPointer: clipRect "
+ // << clipRect.x() << "," << clipRect.y() << " " << clipRect.width()
+ // << "x" << clipRect.height() << " pointer pos is " << m_pointerPos << endl;
+
+ if (m_pointerPos >= clipRect.x() && m_pointerPos <= (clipRect.x() + clipRect.width())) {
+ p->save();
+ p->setPen(m_pointerPen);
+ p->drawLine(m_pointerPos, clipRect.y(), m_pointerPos, clipRect.y() + clipRect.height());
+ p->restore();
+ }
+
+}
+
+void CompositionView::drawTextFloat(QPainter *p, const QRect& clipRect)
+{
+ QFontMetrics metrics(p->fontMetrics());
+
+ QRect bound = p->boundingRect(0, 0, 300, metrics.height() + 6, AlignAuto, m_textFloatText);
+
+ p->save();
+
+ bound.setLeft(bound.left() - 2);
+ bound.setRight(bound.right() + 2);
+ bound.setTop(bound.top() - 2);
+ bound.setBottom(bound.bottom() + 2);
+
+ QPoint pos(m_textFloatPos);
+ if (pos.y() < 0 && getModel()) {
+ if (pos.y() + bound.height() < 0) {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 3);
+ } else {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 2);
+ }
+ }
+
+ bound.moveTopLeft(pos);
+
+ if (bound.intersects(clipRect)) {
+
+ p->setBrush(CompositionColourCache::getInstance()->RotaryFloatBackground);
+
+ drawRect(bound, p, clipRect, false, 0, true);
+
+ p->setPen(CompositionColourCache::getInstance()->RotaryFloatForeground);
+
+ p->drawText(pos.x() + 2, pos.y() + 3 + metrics.ascent(), m_textFloatText);
+
+ }
+
+ p->restore();
+}
+
+bool CompositionView::event(QEvent* e)
+{
+ if (e->type() == AudioPreviewThread::AudioPreviewQueueEmpty) {
+ RG_DEBUG << "CompositionView::event - AudioPreviewQueueEmpty\n";
+ slotSegmentsDrawBufferNeedsRefresh();
+ viewport()->update();
+ return true;
+ }
+
+ return RosegardenScrollView::event(e);
+}
+
+void CompositionView::enterEvent(QEvent *e)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ emit showContextHelp(m_toolContextHelp);
+ m_contextHelpShown = true;
+}
+
+void CompositionView::leaveEvent(QEvent *e)
+{
+ emit showContextHelp("");
+ m_contextHelpShown = false;
+}
+
+void CompositionView::slotToolHelpChanged(const QString &text)
+{
+ if (m_toolContextHelp == text) return;
+ m_toolContextHelp = text;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ if (m_contextHelpShown) emit showContextHelp(text);
+}
+
+void CompositionView::contentsMousePressEvent(QMouseEvent* e)
+{
+ Qt::ButtonState bs = e->state();
+ slotSetSelectCopy((bs & Qt::ControlButton) != 0);
+ slotSetSelectAdd((bs & Qt::ShiftButton) != 0);
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton + Qt::ControlButton) != 0);
+
+ switch (e->button()) {
+ case LeftButton:
+ case MidButton:
+ startAutoScroll();
+
+ if (m_tool)
+ m_tool->handleMouseButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ case RightButton:
+ if (m_tool)
+ m_tool->handleRightButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ default:
+ break;
+ }
+}
+
+void CompositionView::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ RG_DEBUG << "CompositionView::contentsMouseReleaseEvent()\n";
+
+ stopAutoScroll();
+
+ if (!m_tool)
+ return ;
+
+ if (e->button() == LeftButton ||
+ e->button() == MidButton )
+ m_tool->handleMouseButtonRelease(e);
+}
+
+void CompositionView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ m_currentItem = getFirstItemAt(e->pos());
+
+ if (!m_currentItem) {
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - no currentItem\n";
+ RulerScale *ruler = grid().getRulerScale();
+ if (ruler) emit setPointerPosition(ruler->getTimeForX(e->pos().x()));
+ return ;
+ }
+
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - have currentItem\n";
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ if (m_currentItem->isRepeating()) {
+ timeT time = getModel()->getRepeatTimeAt(e->pos(), m_currentItem);
+
+ RG_DEBUG << "editRepeat at time " << time << endl;
+ if (time > 0)
+ emit editRepeat(itemImpl->getSegment(), time);
+ else
+ emit editSegment(itemImpl->getSegment());
+
+ } else {
+
+ emit editSegment(itemImpl->getSegment());
+ }
+}
+
+void CompositionView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (!m_tool)
+ return ;
+
+ Qt::ButtonState bs = e->state();
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton) != 0);
+
+ int follow = m_tool->handleMouseMove(e);
+ setScrollDirectionConstraint(follow);
+
+ if (follow != RosegardenCanvasView::NoFollow) {
+ doAutoScroll();
+
+ if (follow & RosegardenCanvasView::FollowHorizontal) {
+ slotScrollHorizSmallSteps(e->pos().x());
+
+ // enlarge composition if needed
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ slotUpdateSize();
+ }
+ }
+
+ if (follow & RosegardenCanvasView::FollowVertical)
+ slotScrollVertSmallSteps(e->pos().y());
+ }
+}
+
+void CompositionView::releaseCurrentItem()
+{
+ m_currentItem = CompositionItem();
+}
+
+void CompositionView::setPointerPos(int pos)
+{
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << ")\n";
+ int oldPos = m_pointerPos;
+ if (oldPos == pos)
+ return ;
+
+ m_pointerPos = pos;
+ getModel()->setPointerPos(pos);
+
+ // automagically grow contents width if pointer position goes beyond right end
+ //
+ if (pos >= (contentsWidth() - m_stepSize)) {
+ resizeContents(pos + m_stepSize, contentsHeight());
+ // grow composition too, if needed (it may not be the case if
+ if (getModel()->getLength() < contentsWidth())
+ getModel()->setLength(contentsWidth());
+ }
+
+
+ // interesting -- isAutoScrolling() never seems to return true?
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << "), isAutoScrolling " << isAutoScrolling() << ", contentsX " << contentsX() << ", m_lastPointerRefreshX " << m_lastPointerRefreshX << ", contentsHeight " << contentsHeight() << endl;
+
+ if (contentsX() != m_lastPointerRefreshX) {
+ m_lastPointerRefreshX = contentsX();
+ // We'll need to shift the whole canvas anyway, so
+ slotArtifactsDrawBufferNeedsRefresh();
+ return ;
+ }
+
+ int deltaW = abs(m_pointerPos - oldPos);
+
+ if (deltaW <= m_pointerPen.width() * 2) { // use one rect instead of two separate ones
+
+ QRect updateRect
+ (std::min(m_pointerPos, oldPos) - m_pointerPen.width(), 0,
+ deltaW + m_pointerPen.width() * 2, contentsHeight());
+
+ slotArtifactsDrawBufferNeedsRefresh(updateRect);
+
+ } else {
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(m_pointerPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(oldPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+ }
+}
+
+void CompositionView::setGuidesPos(int x, int y)
+{
+ m_topGuidePos = x;
+ m_foreGuidePos = y;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setGuidesPos(const QPoint& p)
+{
+ m_topGuidePos = p.x();
+ m_foreGuidePos = p.y();
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setDrawGuides(bool d)
+{
+ m_drawGuides = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setTmpRect(const QRect& r)
+{
+ setTmpRect(r, m_tmpRectFill);
+}
+
+void CompositionView::setTmpRect(const QRect& r, const QColor &c)
+{
+ QRect pRect = m_tmpRect;
+ m_tmpRect = r;
+ m_tmpRectFill = c;
+ slotUpdateSegmentsDrawBuffer(m_tmpRect | pRect);
+}
+
+void CompositionView::setTextFloat(int x, int y, const QString &text)
+{
+ m_textFloatPos.setX(x);
+ m_textFloatPos.setY(y);
+ m_textFloatText = text;
+ m_drawTextFloat = true;
+ slotArtifactsDrawBufferNeedsRefresh();
+
+ // most of the time when the floating text is drawn
+ // we want to update a larger part of the view
+ // so don't update here
+ // QRect r = fontMetrics().boundingRect(x, y, 300, 40, AlignAuto, m_textFloatText);
+ // slotUpdateSegmentsDrawBuffer(r);
+
+
+ // rgapp->slotSetStatusMessage(text);
+}
+
+void CompositionView::slotSetFineGrain(bool value)
+{
+ m_fineGrain = value;
+}
+
+void CompositionView::slotSetPencilOverExisting(bool value)
+{
+ m_pencilOverExisting = value;
+}
+
+void
+CompositionView::slotTextFloatTimeout()
+{
+ hideTextFloat();
+ slotArtifactsDrawBufferNeedsRefresh();
+ // rgapp->slotSetStatusMessage(QString::null);
+}
+
+}
+#include "CompositionView.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.h b/src/gui/editors/segment/segmentcanvas/CompositionView.h
new file mode 100644
index 0000000..ff0d440
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.h
@@ -0,0 +1,366 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_COMPOSITIONVIEW_H_
+#define _RG_COMPOSITIONVIEW_H_
+
+#include "base/Selection.h"
+#include "CompositionModel.h"
+#include "CompositionItem.h"
+#include "gui/general/RosegardenScrollView.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QWheelEvent;
+class QResizeEvent;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class SegmentToolBox;
+class SegmentTool;
+class SegmentSelector;
+class Segment;
+class RosegardenGUIDoc;
+class CompositionRect;
+
+
+class CompositionView : public RosegardenScrollView
+{
+ Q_OBJECT
+public:
+ CompositionView(RosegardenGUIDoc*, CompositionModel*,
+ QWidget * parent=0, const char* name=0, WFlags f=0);
+
+ void setPointerPos(int pos);
+ int getPointerPos() { return m_pointerPos; }
+
+ void setGuidesPos(int x, int y);
+ void setGuidesPos(const QPoint& p);
+ void setDrawGuides(bool d);
+
+ QRect getSelectionRect() const { return m_selectionRect; }
+ void setSelectionRectPos(const QPoint& pos);
+ void setSelectionRectSize(int w, int h);
+ void setDrawSelectionRect(bool d);
+
+ SnapGrid& grid() { return m_model->grid(); }
+
+ CompositionItem getFirstItemAt(QPoint pos);
+
+ SegmentToolBox* getToolBox() { return m_toolBox; }
+
+ CompositionModel* getModel() { return m_model; }
+
+ void setTmpRect(const QRect& r);
+ void setTmpRect(const QRect& r, const QColor &c);
+ const QRect& getTmpRect() const { return m_tmpRect; }
+
+ /**
+ * Set the snap resolution of the grid to something suitable.
+ *
+ * fineTool indicates whether the current tool is a fine-grain sort
+ * (such as the resize or move tools) or a coarse one (such as the
+ * segment creation pencil). If the user is requesting extra-fine
+ * resolution (through the setFineGrain method) that will also be
+ * taken into account.
+ */
+ void setSnapGrain(bool fine);
+
+ /**
+ * Find out whether the user is requesting extra-fine resolution
+ * (e.g. by holding Shift key). This is seldom necessary -- most
+ * client code will only need to query the snap grid that is
+ * adjusted appropriately by the view when interactions take
+ * place.
+ */
+ bool isFineGrain() const { return m_fineGrain; }
+
+ /**
+ * Find out whether the user is requesting to draw over an existing segment
+ * with the pencil, by holding the Ctrl key. This is used by the segment
+ * pencil to decide whether to abort or not if a user attempts to draw over
+ * an existing segment, and this is all necessary in order to avoid breaking
+ * the double-click-to-open behavior.
+ */
+ bool pencilOverExisting() const { return m_pencilOverExisting; }
+
+ /**
+ * Set whether the segment items contain previews or not
+ */
+ void setShowPreviews(bool previews) { m_showPreviews = previews; }
+
+ /**
+ * Return whether the segment items contain previews or not
+ */
+ bool isShowingPreviews() { return m_showPreviews; }
+
+ /**
+ * clear all seg rect cache
+ */
+ void clearSegmentRectsCache(bool clearPreviews = false);
+
+ /// Return the selected Segments if we're currently using a "Selector"
+ SegmentSelection getSelectedSegments();
+
+ bool haveSelection() const { return m_model->haveSelection(); }
+
+ void updateSelectionContents();
+
+ /**
+ * Set and hide a text float on this canvas - it can contain
+ * anything and can be left to timeout or you can hide it
+ * explicitly.
+ *
+ */
+ void setTextFloat(int x, int y, const QString &text);
+ void hideTextFloat() { m_drawTextFloat = false; }
+
+ void setShowSegmentLabels(bool b) { m_showSegmentLabels = b; }
+
+ void setBackgroundPixmap(const QPixmap &m);
+
+ void endAudioPreviewGeneration();
+
+public slots:
+ void scrollRight();
+ void scrollLeft();
+ void slotContentsMoving(int x, int y);
+
+ /// Set the current segment editing tool
+ void slotSetTool(const QString& toolName);
+
+ // This method only operates if we're of the "Selector"
+ // tool type - it's called from the View to enable it
+ // to automatically set the selection of Segments (say
+ // by Track).
+ //
+ void slotSelectSegments(const SegmentSelection &segment);
+
+ // These are sent from the top level app when it gets key
+ // depresses relating to selection add (usually SHIFT) and
+ // selection copy (usually CONTROL)
+ //
+ void slotSetSelectAdd(bool value);
+ void slotSetSelectCopy(bool value);
+
+ void slotSetFineGrain(bool value);
+ void slotSetPencilOverExisting(bool value);
+
+ // Show and hige the splitting line on a Segment
+ //
+ void slotShowSplitLine(int x, int y);
+ void slotHideSplitLine();
+
+ void slotExternalWheelEvent(QWheelEvent*);
+
+ // TextFloat timer
+ void slotTextFloatTimeout();
+
+ void slotUpdateSegmentsDrawBuffer();
+ void slotUpdateSegmentsDrawBuffer(const QRect&);
+
+ void slotRefreshColourCache();
+
+ void slotNewMIDIRecordingSegment(Segment*);
+ void slotNewAudioRecordingSegment(Segment*);
+ // no longer used, see RosegardenGUIDoc::insertRecordedMidi
+// void slotRecordMIDISegmentUpdated(Segment*, timeT updatedFrom);
+ void slotStoppedRecording();
+
+ void slotUpdateSize();
+
+signals:
+ void editSegment(Segment*); // use default editor
+ void editSegmentNotation(Segment*);
+ void editSegmentMatrix(Segment*);
+ void editSegmentAudio(Segment*);
+ void editSegmentEventList(Segment*);
+ void audioSegmentAutoSplit(Segment*);
+ void editRepeat(Segment*, timeT);
+
+ void setPointerPosition(timeT);
+
+ void showContextHelp(const QString &);
+
+protected:
+ virtual bool event(QEvent *);
+
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ virtual void viewportPaintEvent(QPaintEvent*);
+ virtual void resizeEvent(QResizeEvent*);
+
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ virtual void viewportPaintRect(QRect);
+
+ /**
+ * if something changed, returns true and sets rect accordingly
+ * sets 'scroll' if some scrolling occurred
+ */
+ bool checkScrollAndRefreshDrawBuffer(QRect &, bool& scroll);
+ void refreshSegmentsDrawBuffer(const QRect&);
+ void refreshArtifactsDrawBuffer(const QRect&);
+ void drawArea(QPainter * p, const QRect& rect);
+ void drawAreaAudioPreviews(QPainter * p, const QRect& rect);
+ void drawAreaArtifacts(QPainter * p, const QRect& rect);
+ void drawRect(const QRect& rect, QPainter * p, const QRect& clipRect,
+ bool isSelected = false, int intersectLvl = 0, bool fill = true);
+ void drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl = 0, bool fill = true);
+ void drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect);
+ void drawIntersections(const CompositionModel::rectcontainer&, QPainter * p, const QRect& clipRect);
+
+ void drawPointer(QPainter * p, const QRect& clipRect);
+ void drawGuides(QPainter * p, const QRect& clipRect);
+ void drawTextFloat(QPainter * p, const QRect& clipRect);
+
+ void initStepSize();
+ void releaseCurrentItem();
+
+ static QColor mixBrushes(QBrush a, QBrush b);
+
+ SegmentSelector* getSegmentSelectorTool();
+
+protected slots:
+ void slotSegmentsDrawBufferNeedsRefresh() {
+ m_segmentsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ }
+
+ void slotSegmentsDrawBufferNeedsRefresh(QRect r) {
+ m_segmentsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh() {
+ m_artifactsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ updateContents();
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh(QRect r) {
+ m_artifactsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ updateContents(r);
+ }
+
+ void slotAllDrawBuffersNeedRefresh() {
+ slotSegmentsDrawBufferNeedsRefresh();
+ slotArtifactsDrawBufferNeedsRefresh();
+ }
+
+ void slotAllDrawBuffersNeedRefresh(QRect r) {
+ slotSegmentsDrawBufferNeedsRefresh(r);
+ slotArtifactsDrawBufferNeedsRefresh(r);
+ }
+
+ void slotToolHelpChanged(const QString &);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ CompositionModel* m_model;
+ CompositionItem m_currentItem;
+
+ SegmentTool* m_tool;
+ SegmentToolBox* m_toolBox;
+
+ bool m_showPreviews;
+ bool m_showSegmentLabels;
+ bool m_fineGrain;
+ bool m_pencilOverExisting;
+
+ int m_minWidth;
+
+ int m_stepSize;
+ QColor m_rectFill;
+ QColor m_selectedRectFill;
+
+ int m_pointerPos;
+ QColor m_pointerColor;
+ int m_pointerWidth;
+ QPen m_pointerPen;
+
+ QRect m_tmpRect;
+ QColor m_tmpRectFill;
+ QPoint m_splitLinePos;
+
+ QColor m_trackDividerColor;
+
+ bool m_drawGuides;
+ QColor m_guideColor;
+ int m_topGuidePos;
+ int m_foreGuidePos;
+
+ bool m_drawSelectionRect;
+ QRect m_selectionRect;
+
+ bool m_drawTextFloat;
+ QString m_textFloatText;
+ QPoint m_textFloatPos;
+
+ QPixmap m_segmentsDrawBuffer;
+ QPixmap m_artifactsDrawBuffer;
+ QRect m_segmentsDrawBufferRefresh;
+ QRect m_artifactsDrawBufferRefresh;
+ int m_lastBufferRefreshX;
+ int m_lastBufferRefreshY;
+ int m_lastPointerRefreshX;
+ QPixmap m_backgroundPixmap;
+
+ QString m_toolContextHelp;
+ bool m_contextHelpShown;
+
+ mutable CompositionModel::AudioPreviewDrawData m_audioPreviewRects;
+ mutable CompositionModel::RectRanges m_notationPreviewRects;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
new file mode 100644
index 0000000..fa09644
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
@@ -0,0 +1,34 @@
+/* -*- 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 "PreviewRect.h"
+
+#include <qcolor.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.h b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
new file mode 100644
index 0000000..59f3113
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
@@ -0,0 +1,62 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_PREVIEWRECT_H_
+#define _RG_PREVIEWRECT_H_
+
+#include <qcolor.h>
+#include <qrect.h>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+typedef std::vector<QImage> PixmapArray;
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
new file mode 100644
index 0000000..3d1e26f
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
@@ -0,0 +1,88 @@
+/* -*- 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 "SegmentEraser.h"
+
+#include "misc/Debug.h"
+#include "commands/segment/SegmentEraseCommand.h"
+#include "CompositionView.h"
+#include "CompositionItemImpl.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentEraser::SegmentEraser(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentEraser()\n";
+}
+
+void SegmentEraser::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::pointingHandCursor);
+ setBasicContextHelp();
+}
+
+void SegmentEraser::handleMouseButtonPress(QMouseEvent *e)
+{
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+}
+
+void SegmentEraser::handleMouseButtonRelease(QMouseEvent*)
+{
+ if (m_currentItem) {
+ // no need to test the result, we know it's good (see handleMouseButtonPress)
+ CompositionItemImpl* item = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ addCommandToHistory(new SegmentEraseCommand(item->getSegment()));
+ }
+
+ setCurrentItem(CompositionItem());
+}
+
+int SegmentEraser::handleMouseMove(QMouseEvent*)
+{
+ setBasicContextHelp();
+ return RosegardenCanvasView::NoFollow;
+}
+
+void SegmentEraser::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click on a segment to delete it"));
+}
+
+const QString SegmentEraser::ToolName = "segmenteraser";
+
+}
+#include "SegmentEraser.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.h b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
new file mode 100644
index 0000000..f428c28
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
@@ -0,0 +1,67 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTERASER_H_
+#define _RG_SEGMENTERASER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentEraser : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentEraser(CompositionView*, RosegardenGUIDoc*);
+ void setBasicContextHelp();
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
new file mode 100644
index 0000000..f0c4598
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
@@ -0,0 +1,37 @@
+/* -*- 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 "SegmentItemPreview.h"
+
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include <qpainter.h>
+#include <qrect.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
new file mode 100644
index 0000000..e190a5c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
@@ -0,0 +1,91 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTITEMPREVIEW_H_
+#define _RG_SEGMENTITEMPREVIEW_H_
+
+#include <qrect.h>
+
+
+class QWMatrix;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+//////////////////////////////////////////////////////////////////////
+class SegmentItemPreview
+{
+public:
+ SegmentItemPreview(Segment& parent,
+ RulerScale* scale);
+ virtual ~SegmentItemPreview();
+
+ enum PreviewState {
+ PreviewChanged,
+ PreviewCalculating,
+ PreviewCurrent
+ };
+
+ virtual void drawShape(QPainter&) = 0;
+
+ PreviewState getPreviewState() const { return m_previewState; }
+
+ /**
+ * Sets whether the preview shape shown in the segment needs
+ * to be refreshed
+ */
+ void setPreviewCurrent(bool c)
+ { m_previewState = (c ? PreviewCurrent : PreviewChanged); }
+
+ /**
+ * Clears out the preview entirely so that it will be regenerated
+ * next time
+ */
+ virtual void clearPreview() = 0;
+
+ QRect rect();
+
+protected:
+ virtual void updatePreview(const QWMatrix &matrix) = 0;
+
+ //--------------- Data members ---------------------------------
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ PreviewState m_previewState;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
new file mode 100644
index 0000000..5129202
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
@@ -0,0 +1,73 @@
+/* -*- 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 "SegmentJoiner.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentJoiner::SegmentJoiner(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentJoiner() - not implemented\n";
+}
+
+SegmentJoiner::~SegmentJoiner()
+{}
+
+void
+SegmentJoiner::handleMouseButtonPress(QMouseEvent*)
+{}
+
+void
+SegmentJoiner::handleMouseButtonRelease(QMouseEvent*)
+{}
+
+int
+SegmentJoiner::handleMouseMove(QMouseEvent*)
+{
+ return RosegardenCanvasView::NoFollow;
+}
+
+void
+SegmentJoiner::contentsMouseDoubleClickEvent(QMouseEvent*)
+{}
+
+const QString SegmentJoiner::ToolName = "segmentjoiner";
+
+}
+#include "SegmentJoiner.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
new file mode 100644
index 0000000..2c83a26
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
@@ -0,0 +1,70 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTJOINER_H_
+#define _RG_SEGMENTJOINER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentJoiner : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentJoiner();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentJoiner(CompositionView*, RosegardenGUIDoc*);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
new file mode 100644
index 0000000..a3d2a59
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
@@ -0,0 +1,348 @@
+/* -*- 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 "SegmentMover.h"
+
+#include "base/Event.h"
+#include <klocale.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 <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentMover::SegmentMover(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentMover()\n";
+}
+
+void SegmentMover::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp();
+}
+
+void SegmentMover::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentMover::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentMover::handleMouseButtonPress(QMouseEvent *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(QMouseEvent *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(QMouseEvent *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;
+ QRect 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);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%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 QString SegmentMover::ToolName = "segmentmover";
+
+}
+#include "SegmentMover.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.h b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
new file mode 100644
index 0000000..776189e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
@@ -0,0 +1,78 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTMOVER_H_
+#define _RG_SEGMENTMOVER_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentMover : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentMover(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ //--------------- Data members ---------------------------------
+
+ QPoint m_clickPoint;
+ bool m_passedInertiaEdge;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
new file mode 100644
index 0000000..4262eb9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
@@ -0,0 +1,48 @@
+/* -*- 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 "SegmentOrderer.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+void SegmentOrderer::segmentClicked(const Segment* s)
+{
+ m_segmentZs[s] = ++m_currentMaxZ;
+ RG_DEBUG << "SegmentOrderer::segmentClicked() s = " << s << " - max Z = " << m_currentMaxZ << endl;
+}
+
+unsigned int SegmentOrderer::getZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentZs[s];
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
new file mode 100644
index 0000000..f4b3d9a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
@@ -0,0 +1,59 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTORDERER_H_
+#define _RG_SEGMENTORDERER_H_
+
+#include "base/Composition.h"
+#include <map>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class SegmentOrderer : public CompositionObserver {
+public:
+ SegmentOrderer() : m_currentMaxZ(0) {};
+
+ unsigned int getZForSegment(const Segment*);
+
+ void segmentClicked(const Segment *);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ std::map<const Segment*, unsigned int> m_segmentZs;
+ unsigned int m_currentMaxZ;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
new file mode 100644
index 0000000..68ca020
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
@@ -0,0 +1,295 @@
+/* -*- 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 "SegmentPencil.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentInsertCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <klocale.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentPencil::SegmentPencil(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_newRect(false),
+ m_track(0),
+ m_startTime(0),
+ m_endTime(0)
+{
+ RG_DEBUG << "SegmentPencil()\n";
+}
+
+void SegmentPencil::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::ibeamCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelpFor(QPoint(0, 0));
+}
+
+void SegmentPencil::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentPencil::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentPencil::handleMouseButtonPress(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ return;
+
+ // is user holding Ctrl+Alt? (ugly, but we are running short on available
+ // modifiers; Alt is grabbed by the window manager, and right clicking, my
+ // (dmm) original idea, is grabbed by the context menu, so let's see how
+ // this goes over
+ bool pencilAnyway = (m_canvas->pencilOverExisting());
+
+ m_newRect = false;
+
+ // Check if mouse click was on a rect
+ //
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If user clicked a rect, and pencilAnyway is false, then there's nothing
+ // left to do here
+ if (item) {
+ delete item;
+ if (!pencilAnyway) return ;
+ }
+
+ // make new item
+ //
+ m_canvas->setSnapGrain(false);
+
+ int trackPosition = m_canvas->grid().getYBin(e->pos().y());
+
+ // Don't do anything if the user clicked beyond the track buttons
+ //
+ if (trackPosition >= m_doc->getComposition().getNbTracks())
+ return ;
+
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (!t)
+ return ;
+
+ TrackId trackId = t->getId();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), SnapGrid::SnapLeft)));
+ timeT duration = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (duration == 0)
+ duration = Note(Note::Shortest).getDuration();
+
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(trackId);
+ if (multiple < 1) multiple = 1;
+
+ QRect tmpRect;
+ tmpRect.setX(int(nearbyint(m_canvas->grid().getRulerScale()->getXForTime(time))));
+ tmpRect.setY(m_canvas->grid().getYBinCoordinate(trackPosition) + 1);
+ tmpRect.setHeight(m_canvas->grid().getYSnap() * multiple - 2);
+ tmpRect.setWidth(int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(time, duration))));
+
+ m_canvas->setTmpRect(tmpRect,
+ GUIPalette::convertColour
+ (m_doc->getComposition().getSegmentColourMap().
+ getColourByIndex(t->getColor())));
+
+ m_newRect = true;
+ m_origPos = e->pos();
+
+ m_canvas->updateContents(tmpRect);
+}
+
+void SegmentPencil::handleMouseButtonRelease(QMouseEvent* e)
+{
+ if (e->button() == RightButton)
+ return ;
+
+ setContextHelpFor(e->pos());
+
+ if (m_newRect) {
+
+ QRect tmpRect = m_canvas->getTmpRect();
+
+ int trackPosition = m_canvas->grid().getYBin(tmpRect.y());
+ Track *track = m_doc->getComposition().getTrackByPosition(trackPosition);
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x()))),
+ endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ // RG_DEBUG << "SegmentPencil::handleMouseButtonRelease() : new segment with track id "
+ // << track->getId() << endl;
+
+ SegmentInsertCommand *command =
+ new SegmentInsertCommand(m_doc, track->getId(),
+ startTime, endTime);
+
+ m_newRect = false;
+
+ addCommandToHistory(command);
+
+ // add the SegmentItem by hand, instead of allowing the usual
+ // update mechanism to spot it. This way we can select the
+ // segment as we add it; otherwise we'd have no way to know
+ // that the segment was created by this tool rather than by
+ // e.g. a simple file load
+
+ Segment *segment = command->getSegment();
+
+ // add a clef to the start of the segment (tracks initialize to a
+ // default of 0 for this property, so treble will be the default if it
+ // is not specified elsewhere)
+ segment->insert(clefIndexToClef(track->getClef()).getAsEvent
+ (segment->getStartTime()));
+ segment->setTranspose(track->getTranspose());
+ segment->setColourIndex(track->getColor());
+ segment->setLowestPlayable(track->getLowestPlayable());
+ segment->setHighestPlayable(track->getHighestPlayable());
+
+ std::string label = qstrtostr(track->getPresetLabel());
+ if (label != "") {
+ segment->setLabel(qstrtostr(track->getPresetLabel()));
+ }
+
+ CompositionItem item = CompositionItemHelper::makeCompositionItem(segment);
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(item);
+ m_canvas->getModel()->signalSelection();
+ m_canvas->setTmpRect(QRect());
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+
+ } else {
+
+ m_newRect = false;
+ }
+}
+
+int SegmentPencil::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_newRect) {
+ setContextHelpFor(e->pos());
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to bar lines"));
+ } else {
+ clearContextHelp();
+ }
+
+ QRect tmpRect = m_canvas->getTmpRect();
+ QRect oldTmpRect = tmpRect;
+
+ m_canvas->setSnapGrain(false);
+
+ SnapGrid::SnapDirection direction = SnapGrid::SnapRight;
+ if (e->pos().x() <= m_origPos.x())
+ direction = SnapGrid::SnapLeft;
+
+ timeT snap = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (snap == 0)
+ snap = Note(Note::Shortest).getDuration();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), direction)));
+
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x())));
+ timeT endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ if (direction == SnapGrid::SnapRight) {
+
+ if (time >= startTime) {
+ if ((time - startTime) < snap) {
+ time = startTime + snap;
+ }
+ } else {
+ if ((startTime - time) < snap) {
+ time = startTime - snap;
+ }
+ }
+
+ int w = int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(startTime, time - startTime)));
+ tmpRect.setWidth(w);
+
+ } else { // SnapGrid::SnapLeft
+
+ // time += std::max(endTime - startTime, timeT(0));
+ tmpRect.setX(int(m_canvas->grid().getRulerScale()->getXForTime(time)));
+
+ }
+
+ m_canvas->setTmpRect(tmpRect);
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+void SegmentPencil::setContextHelpFor(QPoint p)
+{
+ int trackPosition = m_canvas->grid().getYBin(p.y());
+
+ if (trackPosition < m_doc->getComposition().getNbTracks()) {
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (t) {
+ InstrumentId id = t->getInstrument();
+ if (id >= AudioInstrumentBase && id < MidiInstrumentBase) {
+ setContextHelp(i18n("Record or drop audio here"));
+ return;
+ }
+ }
+ }
+
+ setContextHelp(i18n("Click and drag to draw an empty segment. Control+Alt click and drag to draw in overlap mode."));
+}
+
+const QString SegmentPencil::ToolName = "segmentpencil";
+
+}
+#include "SegmentPencil.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.h b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
new file mode 100644
index 0000000..8b55917
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
@@ -0,0 +1,83 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTPENCIL_H_
+#define _RG_SEGMENTPENCIL_H_
+
+#include "base/Track.h"
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////
+
+class SegmentPencil : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentPencil(CompositionView*, RosegardenGUIDoc*);
+ void setContextHelpFor(QPoint p);
+
+ //--------------- Data members ---------------------------------
+
+ bool m_newRect;
+ TrackId m_track;
+ timeT m_startTime;
+ timeT m_endTime;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
new file mode 100644
index 0000000..6ae7433
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
@@ -0,0 +1,393 @@
+/* -*- 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 "SegmentResizer.h"
+
+#include "base/Event.h"
+#include <klocale.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 <kmessagebox.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.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(Qt::sizeHorCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp(false);
+}
+
+void SegmentResizer::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentResizer::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentResizer::handleMouseButtonPress(QMouseEvent *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(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentResizer::handleMouseButtonRelease" << endl;
+
+ bool rescale = (e->state() & Qt::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(), SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &m_doc->getAudioFileManager(), 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(QMouseEvent *e)
+{
+ // RG_DEBUG << "SegmentResizer::handleMouseMove" << endl;
+
+ bool rescale = (e->state() & Qt::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;
+ }
+ */
+
+ QRect 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 QPoint &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 QString SegmentResizer::ToolName = "segmentresizer";
+
+}
+#include "SegmentResizer.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.h b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
new file mode 100644
index 0000000..9d54573
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
@@ -0,0 +1,87 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTRESIZER_H_
+#define _RG_SEGMENTRESIZER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QPoint;
+class QMouseEvent;
+class CompositionItem;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+/**
+ * Segment Resizer tool. Allows resizing only at the end of the segment part
+ */
+class SegmentResizer : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static bool cursorIsCloseEnoughToEdge(const CompositionItem&, const QPoint&, int, bool &);
+
+ void setEdgeThreshold(int e) { m_edgeThreshold = e; }
+ int getEdgeThreshold() { return m_edgeThreshold; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentResizer(CompositionView*, RosegardenGUIDoc*, int edgeThreshold = 10);
+ void setBasicContextHelp(bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ int m_edgeThreshold;
+ bool m_resizeStart;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
new file mode 100644
index 0000000..35ec639
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
@@ -0,0 +1,532 @@
+/* -*- 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 "SegmentSelector.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/SnapGrid.h"
+#include "base/Selection.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentQuickCopyCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentTool.h"
+#include "SegmentToolBox.h"
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSelector::SegmentSelector(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_segmentAddMode(false),
+ m_segmentCopyMode(false),
+ m_segmentQuickCopyDone(false),
+ m_buttonPressed(false),
+ m_selectionMoveStarted(false),
+ m_dispatchTool(0)
+{
+ RG_DEBUG << "SegmentSelector()\n";
+}
+
+SegmentSelector::~SegmentSelector()
+{}
+
+void SegmentSelector::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelp(i18n("Click and drag to select segments"));
+}
+
+void SegmentSelector::stow()
+{}
+
+void SegmentSelector::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void
+SegmentSelector::handleMouseButtonPress(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentSelector::handleMouseButtonPress\n";
+ m_buttonPressed = true;
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If we're in segmentAddMode or not clicking on an item then we don't
+ // clear the selection vector. If we're clicking on an item and it's
+ // not in the selection - then also clear the selection.
+ //
+ if ((!m_segmentAddMode && !item) ||
+ (!m_segmentAddMode && !(m_canvas->getModel()->isSelected(item)))) {
+ m_canvas->getModel()->clearSelected();
+ }
+
+ if (item) {
+
+ // Fifteen percent of the width of the SegmentItem, up to 10px
+ //
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+
+ bool start = false;
+
+ // Resize if we're dragging from the edge, provided we aren't
+ // in segment-add mode with at least one segment already
+ // selected -- as we aren't able to resize multiple segments
+ // at once, we should assume the segment-add aspect takes
+ // priority
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, e->pos(), threshold, start)) {
+
+ SegmentResizer* resizer =
+ dynamic_cast<SegmentResizer*>(getToolBox()->getTool(SegmentResizer::ToolName));
+
+ resizer->setEdgeThreshold(threshold);
+
+ // For the moment we only allow resizing of a single segment
+ // at a time.
+ //
+ m_canvas->getModel()->clearSelected();
+
+ m_dispatchTool = resizer;
+
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ return ;
+ }
+
+ bool selecting = true;
+
+ if (m_segmentAddMode && m_canvas->getModel()->isSelected(item)) {
+ selecting = false;
+ } else {
+ // put the segment in 'move' mode only if it's being selected
+ m_canvas->getModel()->startChange(item, CompositionModel::ChangeMove);
+ }
+
+ m_canvas->getModel()->setSelected(item, selecting);
+
+ // Moving
+ //
+ // RG_DEBUG << "SegmentSelector::handleMouseButtonPress - m_currentItem = " << item << endl;
+ m_currentItem = item;
+ m_clickPoint = e->pos();
+
+ int guideX = item->rect().x();
+ int guideY = item->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ m_canvas->setDrawGuides(true);
+
+ } else {
+
+ // Add on middle button or ctrl+left - bounding box on rest
+ //
+ if (e->button() == MidButton ||
+ (e->button() == LeftButton && (e->state() & Qt::ControlButton))) {
+
+ m_dispatchTool = getToolBox()->getTool(SegmentPencil::ToolName);
+
+ if (m_dispatchTool) {
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ }
+
+ return ;
+
+ } else {
+
+ m_canvas->setSelectionRectPos(e->pos());
+ m_canvas->setDrawSelectionRect(true);
+ if (!m_segmentAddMode)
+ m_canvas->getModel()->clearSelected();
+
+ }
+ }
+
+ // Tell the RosegardenGUIView that we've selected some new Segments -
+ // when the list is empty we're just unselecting.
+ //
+ m_canvas->getModel()->signalSelection();
+
+ m_passedInertiaEdge = false;
+}
+
+void
+SegmentSelector::handleMouseButtonRelease(QMouseEvent *e)
+{
+ m_buttonPressed = false;
+
+ // Hide guides and stuff
+ //
+ m_canvas->setDrawGuides(false);
+ m_canvas->hideTextFloat();
+
+ if (m_dispatchTool) {
+ m_dispatchTool->handleMouseButtonRelease(e);
+ m_dispatchTool = 0;
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ 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) {
+ m_canvas->setDrawSelectionRect(false);
+ m_canvas->getModel()->finalizeSelectionRect();
+ m_canvas->getModel()->signalSelection();
+ return ;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+
+ Composition &comp = m_doc->getComposition();
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ CompositionModel::itemcontainer::iterator it;
+
+ if (changeMade()) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand
+ (m_selectedItems.size() == 1 ? i18n("Move Segment") :
+ i18n("Move Segments"));
+
+ 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 itemStartTime = 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 itemEndTime = itemStartTime + segment->getEndMarkerTime()
+ - segment->getStartTime();
+
+// std::cerr << "releasing segment " << segment << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", diff is " << trackDiff << ", moving from track pos " << comp.getTrackPositionById(origTrackId) << " to " << trackPos << ", id " << origTrackId << " to " << newTrackId << std::endl;
+
+ command->addSegment(segment,
+ itemStartTime,
+ itemEndTime,
+ newTrackId);
+ }
+
+ addCommandToHistory(command);
+ }
+
+ m_canvas->getModel()->endChange();
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+ }
+
+ // if we've just finished a quick copy then drop the Z level back
+ if (m_segmentQuickCopyDone) {
+ m_segmentQuickCopyDone = false;
+ // m_currentItem->setZ(2); // see SegmentItem::setSelected --??
+ }
+
+ setChangeMade(false);
+
+ m_selectionMoveStarted = false;
+
+ m_currentItem = CompositionItem();
+
+ setContextHelpFor(e->pos());
+}
+
+int
+SegmentSelector::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_buttonPressed) {
+ setContextHelpFor(e->pos(), (e->state() & Qt::ControlButton));
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (m_dispatchTool) {
+ return m_dispatchTool->handleMouseMove(e);
+ }
+
+ Composition &comp = m_doc->getComposition();
+
+ if (!m_currentItem) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: no current item\n";
+
+ // do a bounding box
+ QRect selectionRect = m_canvas->getSelectionRect();
+
+ m_canvas->setDrawSelectionRect(true);
+
+ // same as for notation view
+ int w = int(e->pos().x() - selectionRect.x());
+ int h = int(e->pos().y() - selectionRect.y());
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ // Translate these points
+ //
+ m_canvas->setSelectionRectSize(w, h);
+
+ m_canvas->getModel()->signalSelection();
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+
+ if (m_segmentCopyMode && !m_segmentQuickCopyDone) {
+ KMacroCommand *mcommand = new KMacroCommand
+ (SegmentQuickCopyCommand::getGlobalName());
+
+ SegmentSelection selectedItems = m_canvas->getSelectedSegments();
+ SegmentSelection::iterator it;
+ for (it = selectedItems.begin();
+ it != selectedItems.end();
+ it++) {
+ SegmentQuickCopyCommand *command =
+ new SegmentQuickCopyCommand(*it);
+
+ mcommand->addCommand(command);
+ }
+
+ addCommandToHistory(mcommand);
+
+ // generate SegmentItem
+ //
+ m_canvas->updateContents();
+ m_segmentQuickCopyDone = true;
+ }
+
+ m_canvas->setSnapGrain(true);
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item is selected\n";
+
+ if (!m_selectionMoveStarted) { // start move on selected items only once
+ m_canvas->getModel()->startChangeSelection(CompositionModel::ChangeMove);
+ m_selectionMoveStarted = true;
+ }
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ setCurrentItem(CompositionItemHelper::findSiblingCompositionItem(changingItems, m_currentItem));
+
+ CompositionModel::itemcontainer::iterator it;
+ int guideX = 0;
+ int guideY = 0;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ ++it) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove() : movingItem at "
+ // << (*it)->rect().x() << "," << (*it)->rect().y() << endl;
+
+ 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 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().getYBinCoordinate(trackPos);
+
+ (*it)->moveTo(newX, newY);
+ 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);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%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();
+
+ } else {
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item not selected\n";
+ }
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void SegmentSelector::setContextHelpFor(QPoint p, bool ctrlPressed)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ CompositionItem item = m_canvas->getFirstItemAt(p);
+
+ if (!item) {
+ setContextHelp(i18n("Click and drag to select segments; middle-click and drag to draw an empty segment"));
+
+ } else {
+
+ // Same logic as in handleMouseButtonPress to establish
+ // whether we'd be moving or resizing
+
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+ bool start = false;
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, p,
+ threshold, start)) {
+ 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"));
+ }
+ } else {
+ if (m_canvas->getModel()->haveMultipleSelection()) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segments; hold Ctrl as well to copy them"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segments"));
+ }
+ } else {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segment; hold Ctrl as well to copy it; double-click to edit"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segment"));
+ }
+ }
+ }
+ }
+}
+
+const QString SegmentSelector::ToolName = "segmentselector";
+
+}
+#include "SegmentSelector.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.h b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
new file mode 100644
index 0000000..a6d8d9c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
@@ -0,0 +1,109 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTSELECTOR_H_
+#define _RG_SEGMENTSELECTOR_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSelector : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentTool;
+
+public:
+
+ virtual ~SegmentSelector();
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // These two alter the behaviour of the selection mode
+ //
+ // - SegmentAdd (usually when SHIFT is held down) allows
+ // multiple selections of Segments.
+ //
+ // - SegmentCopy (usually CONTROL) allows draw and drop
+ // copying of Segments - it's a quick shortcut
+ //
+ void setSegmentAdd(const bool &value) { m_segmentAddMode = value; }
+ void setSegmentCopy(const bool &value) { m_segmentCopyMode = value; }
+
+ bool isSegmentAdding() const { return m_segmentAddMode; }
+ bool isSegmentCopying() const { return m_segmentCopyMode; }
+
+ // Return the SegmentItem list for other tools to use
+ //
+ SegmentItemList* getSegmentItemList() { return &m_selectedItems; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentSelector(CompositionView*, RosegardenGUIDoc*);
+
+ void setContextHelpFor(QPoint p, bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ SegmentItemList m_selectedItems;
+
+ bool m_segmentAddMode;
+ bool m_segmentCopyMode;
+ QPoint m_clickPoint;
+ bool m_segmentQuickCopyDone;
+ bool m_passedInertiaEdge;
+ bool m_buttonPressed;
+ bool m_selectionMoveStarted;
+
+ SegmentTool *m_dispatchTool;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
new file mode 100644
index 0000000..4fd48c3
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
@@ -0,0 +1,175 @@
+/* -*- 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 "SegmentSplitter.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/AudioSegmentSplitCommand.h"
+#include "commands/segment/SegmentSplitCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSplitter::SegmentSplitter(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_prevX(0),
+ m_prevY(0)
+{
+ RG_DEBUG << "SegmentSplitter()\n";
+}
+
+SegmentSplitter::~SegmentSplitter()
+{}
+
+void SegmentSplitter::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ setBasicContextHelp();
+}
+
+void
+SegmentSplitter::handleMouseButtonPress(QMouseEvent *e)
+{
+ // Remove cursor and replace with line on a SegmentItem
+ // at where the cut will be made
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->viewport()->setCursor(Qt::blankCursor);
+ m_prevX = item->rect().x();
+ m_prevX = item->rect().y();
+ drawSplitLine(e);
+ delete item;
+ }
+
+}
+
+void
+SegmentSplitter::handleMouseButtonRelease(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->setSnapGrain(true);
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ if (segment->getType() == Segment::Audio) {
+ AudioSegmentSplitCommand *command =
+ new AudioSegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ } else {
+ SegmentSplitCommand *command =
+ new SegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ }
+
+ m_canvas->updateContents(item->rect());
+ delete item;
+ }
+
+ // Reinstate the cursor
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+}
+
+int
+SegmentSplitter::handleMouseMove(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+// m_canvas->viewport()->setCursor(Qt::blankCursor);
+ drawSplitLine(e);
+ delete item;
+ return RosegardenCanvasView::FollowHorizontal;
+ } else {
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+ return RosegardenCanvasView::NoFollow;
+ }
+}
+
+void
+SegmentSplitter::drawSplitLine(QMouseEvent *e)
+{
+ m_canvas->setSnapGrain(true);
+
+ // Turn the real X into a snapped X
+ //
+ timeT xT = m_canvas->grid().snapX(e->pos().x());
+ int x = (int)(m_canvas->grid().getRulerScale()->getXForTime(xT));
+
+ // Need to watch y doesn't leak over the edges of the
+ // current Segment.
+ //
+ int y = m_canvas->grid().snapY(e->pos().y());
+
+ m_canvas->slotShowSplitLine(x, y);
+
+ QRect updateRect(std::max(0, std::min(x, m_prevX) - 5), y,
+ std::max(m_prevX, x) + 5, m_prevY + m_canvas->grid().getYSnap());
+ m_canvas->updateContents(updateRect);
+ m_prevX = x;
+ m_prevY = y;
+}
+
+void
+SegmentSplitter::contentsMouseDoubleClickEvent(QMouseEvent*)
+{
+ // DO NOTHING
+}
+
+void
+SegmentSplitter::setBasicContextHelp()
+{
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Click on a segment to split it in two; hold Shift to avoid snapping to beat grid"));
+ } else {
+ setContextHelp(i18n("Click on a segment to split it in two"));
+ }
+}
+
+const QString SegmentSplitter::ToolName = "segmentsplitter";
+
+}
+#include "SegmentSplitter.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
new file mode 100644
index 0000000..06201ec
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
@@ -0,0 +1,83 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTSPLITTER_H_
+#define _RG_SEGMENTSPLITTER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSplitter : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentSplitter();
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentSplitter(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ void drawSplitLine(QMouseEvent*);
+ void splitSegment(Segment *segment,
+ timeT &splitTime);
+
+ //--------------- Data members ---------------------------------
+ int m_prevX;
+ int m_prevY;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
new file mode 100644
index 0000000..9bd7e69
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
@@ -0,0 +1,115 @@
+/* -*- 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 "SegmentTool.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/BaseTool.h"
+#include "SegmentToolBox.h"
+#include <kcommand.h>
+#include <kmainwindow.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+
+
+namespace Rosegarden
+{
+
+SegmentTool::SegmentTool(CompositionView* canvas, RosegardenGUIDoc *doc)
+ : BaseTool("segment_tool_menu", dynamic_cast<KMainWindow*>(doc->parent())->factory(), canvas),
+ m_canvas(canvas),
+ m_doc(doc),
+ m_changeMade(false)
+{}
+
+SegmentTool::~SegmentTool()
+{}
+
+
+void SegmentTool::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+}
+
+void
+SegmentTool::handleRightButtonPress(QMouseEvent *e)
+{
+ if (m_currentItem) // mouse button is pressed for some tool
+ return ;
+
+ RG_DEBUG << "SegmentTool::handleRightButtonPress()\n";
+
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+
+ if (m_currentItem) {
+ if (!m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(m_currentItem);
+ m_canvas->getModel()->signalSelection();
+ }
+ }
+
+ showMenu();
+ setCurrentItem(CompositionItem());
+}
+
+void
+SegmentTool::createMenu()
+{
+ RG_DEBUG << "SegmentTool::createMenu()\n";
+
+ RosegardenGUIApp *app =
+ dynamic_cast<RosegardenGUIApp*>(m_doc->parent());
+
+ if (app) {
+ m_menu = static_cast<QPopupMenu*>
+ //(app->factory()->container("segment_tool_menu", app));
+ (m_parentFactory->container("segment_tool_menu", app));
+
+ if (!m_menu) {
+ RG_DEBUG << "SegmentTool::createMenu() failed\n";
+ }
+ } else {
+ RG_DEBUG << "SegmentTool::createMenu() failed: !app\n";
+ }
+}
+
+void
+SegmentTool::addCommandToHistory(KCommand *command)
+{
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+SegmentToolBox* SegmentTool::getToolBox()
+{
+ return m_canvas->getToolBox();
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.h b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
new file mode 100644
index 0000000..90ed961
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
@@ -0,0 +1,105 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTTOOL_H_
+#define _RG_SEGMENTTOOL_H_
+
+#include "gui/general/BaseTool.h"
+#include "CompositionItem.h"
+#include <qpoint.h>
+#include <utility>
+#include <vector>
+
+
+class QMouseEvent;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class SegmentToolBox;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////////////////////////////////////////////
+
+class SegmentToolBox;
+class SegmentSelector;
+
+// Allow the tools to share the Selector tool's selection
+// through these.
+//
+typedef std::pair<QPoint, CompositionItem> SegmentItemPair;
+typedef std::vector<SegmentItemPair> SegmentItemList;
+
+class SegmentTool : public BaseTool
+{
+public:
+ friend class SegmentToolBox;
+
+ virtual ~SegmentTool();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) when
+ * the tool is set as current.
+ * Add any setup here
+ */
+ virtual void ready();
+
+ virtual void handleRightButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonPress(QMouseEvent*) = 0;
+ virtual void handleMouseButtonRelease(QMouseEvent*) = 0;
+ virtual int handleMouseMove(QMouseEvent*) = 0;
+
+ void addCommandToHistory(KCommand *command);
+
+protected:
+ SegmentTool(CompositionView*, RosegardenGUIDoc *doc);
+
+ virtual void createMenu();
+ virtual bool hasMenu() { return true; }
+
+ void setCurrentItem(CompositionItem item) { if (item != m_currentItem) { delete m_currentItem; m_currentItem = item; } }
+
+ SegmentToolBox* getToolBox();
+
+ bool changeMade() { return m_changeMade; }
+ void setChangeMade(bool c) { m_changeMade = c; }
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ CompositionItem m_currentItem;
+ RosegardenGUIDoc* m_doc;
+ QPoint m_origPos;
+ bool m_changeMade;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
new file mode 100644
index 0000000..cb0dec9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
@@ -0,0 +1,102 @@
+/* -*- 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 "SegmentToolBox.h"
+
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+#include "SegmentSelector.h"
+#include "SegmentEraser.h"
+#include "SegmentJoiner.h"
+#include "SegmentMover.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentSplitter.h"
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+SegmentToolBox::SegmentToolBox(CompositionView* parent, RosegardenGUIDoc* doc)
+ : BaseToolBox(parent),
+ m_canvas(parent),
+ m_doc(doc)
+{}
+
+SegmentTool* SegmentToolBox::createTool(const QString& toolName)
+{
+ SegmentTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == SegmentPencil::ToolName)
+
+ tool = new SegmentPencil(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentEraser::ToolName)
+
+ tool = new SegmentEraser(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentMover::ToolName)
+
+ tool = new SegmentMover(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentResizer::ToolName)
+
+ tool = new SegmentResizer(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSelector::ToolName)
+
+ tool = new SegmentSelector(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSplitter::ToolName)
+
+ tool = new SegmentSplitter(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentJoiner::ToolName)
+
+ tool = new SegmentJoiner(m_canvas, m_doc);
+
+ else {
+ KMessageBox::error(0, QString("SegmentToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+}
+
+SegmentTool* SegmentToolBox::getTool(const QString& toolName)
+{
+ return dynamic_cast<SegmentTool*>(BaseToolBox::getTool(toolName));
+}
+
+}
+#include "SegmentToolBox.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
new file mode 100644
index 0000000..7a46fb6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
@@ -0,0 +1,63 @@
+
+/* -*- 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.
+*/
+
+#ifndef _RG_SEGMENTTOOLBOX_H_
+#define _RG_SEGMENTTOOLBOX_H_
+
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentToolBox : public BaseToolBox
+{
+ Q_OBJECT
+public:
+ SegmentToolBox(CompositionView* parent, RosegardenGUIDoc*);
+
+ virtual SegmentTool* getTool(const QString& toolName);
+
+protected:
+ virtual SegmentTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ RosegardenGUIDoc* m_doc;
+};
+
+
+}
+
+#endif