You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1327 lines
44 KiB
1327 lines
44 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
#include <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 <tqbrush.h>
|
|
#include <tqcolor.h>
|
|
#include <tqpen.h>
|
|
#include <tqpoint.h>
|
|
#include <tqrect.h>
|
|
#include <tqregexp.h>
|
|
#include <tqsize.h>
|
|
#include <tqstring.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 TQRect &r1, const TQRect &r2) const {
|
|
return r1.x() < r2.x();
|
|
}
|
|
};
|
|
|
|
void CompositionModelImpl::makeNotationPreviewRects(RectRanges* npRects, TQPoint basePoint,
|
|
const Segment* segment, const TQRect& 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, TQPoint basePoint,
|
|
const Segment* segment, const TQRect& 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 TQRect& clipRect)
|
|
{
|
|
Profiler profiler("CompositionModelImpl::makeAudioPreviewRects", true);
|
|
RG_DEBUG << "CompositionModelImpl::makeAudioPreviewRects - segRect = " << segRect << endl;
|
|
|
|
PixmapArray previewImage = getAudioPreviewPixmap(segment);
|
|
|
|
TQPoint basePoint = segRect.topLeft();
|
|
|
|
AudioPreviewDrawDataItem previewItem(previewImage, basePoint, static_cast<const TQRect&>(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;
|
|
|
|
TQRect r(x, (int)y, width, height);
|
|
|
|
// RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForNotationSegment() : npData = "
|
|
// << npData << ", preview rect = "
|
|
// << r << endl;
|
|
npData->push_back(r);
|
|
}
|
|
|
|
}
|
|
|
|
TQColor CompositionModelImpl::computeSegmentPreviewColor(const Segment* segment)
|
|
{
|
|
// compute the preview color so it's as visible as possible over the segment's color
|
|
TQColor 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(TQPoint(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, TQ_SIGNAL(audioPreviewComplete(AudioPreviewUpdater*)),
|
|
this, TQ_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());
|
|
TQRect 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);
|
|
}
|
|
|
|
TQRect 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 static_cast<const TQRect&>(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();
|
|
|
|
TQRect 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 TQRect& 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();
|
|
|
|
TQRect 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 = TQRect();
|
|
m_tmpSelectedSegments.clear();
|
|
}
|
|
|
|
TQRect CompositionModelImpl::getSelectionContentsRect()
|
|
{
|
|
TQRect 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 TQPoint& 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 TQPoint& 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;
|
|
}
|
|
|
|
TQPoint CompositionModelImpl::computeSegmentOrigin(const Segment& s)
|
|
{
|
|
// Profiler profiler("CompositionModelImpl::computeSegmentOrigin", true);
|
|
|
|
int trackPosition = m_composition.getTrackPositionById(s.getTrack());
|
|
timeT startTime = s.getStartTime();
|
|
|
|
TQPoint 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, TQPoint 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);
|
|
|
|
TQPoint 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, TQSize(w, h));
|
|
TQString label = strtoqstr(s.getLabel());
|
|
if (s.getType() == Segment::Audio) {
|
|
static TQRegExp re1("( *\\([^)]*\\))*$"); // (inserted) (copied) (etc)
|
|
static TQRegExp 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 TQRect& 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)) {
|
|
TQColor 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, TQPoint(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);
|
|
TQColor 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 TQRect& 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"
|