/* -*- 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 , Chris Cannam , Richard Bown 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Rosegarden { class PreviewRect : public TQRect { public: PreviewRect(int left, int top, int width, int height) : TQRect(left, top, width, height) {}; PreviewRect(const TQRect& r) : TQRect(r) {}; const TQColor& getColor() const { return m_color; } void setColor(TQColor c) { m_color = c; } protected: TQColor m_color; }; CompositionView::CompositionView(RosegardenGUIDoc* doc, CompositionModel* model, TQWidget * 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(TQPen(m_pointerColor, m_pointerWidth)), m_tmpRect(TQRect(TQPoint(0, 0), TQPoint( -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, TQT_SIGNAL(showContextHelp(const TQString &)), this, TQT_SLOT(slotToolHelpChanged(const TQString &))); } setDragAutoScroll(true); setBackgroundMode(NoBackground); viewport()->setBackgroundMode(NoBackground); viewport()->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas)); slotUpdateSize(); TQScrollBar* hsb = horizontalScrollBar(); // dynamically adjust content size when scrolling past current composition's end connect(hsb, TQT_SIGNAL(nextLine()), this, TQT_SLOT(scrollRight())); connect(hsb, TQT_SIGNAL(prevLine()), this, TQT_SLOT(scrollLeft())); // connect(this, TQT_SIGNAL(contentsMoving(int, int)), // this, TQT_SLOT(slotAllDrawBuffersNeedRefresh())); // connect(this, TQT_SIGNAL(contentsMoving(int, int)), // this, TQT_SLOT(slotContentsMoving(int, int))); connect(model, TQT_SIGNAL(needContentUpdate()), this, TQT_SLOT(slotUpdateSegmentsDrawBuffer())); connect(model, TQT_SIGNAL(needContentUpdate(const TQRect&)), this, TQT_SLOT(slotUpdateSegmentsDrawBuffer(const TQRect&))); connect(model, TQT_SIGNAL(needArtifactsUpdate()), this, TQT_SLOT(slotArtifactsDrawBufferNeedsRefresh())); connect(model, TQT_SIGNAL(needSizeUpdate()), this, TQT_SLOT(slotUpdateSize())); if (doc) { connect(doc, TQT_SIGNAL(docColoursChanged()), this, TQT_SLOT(slotRefreshColourCache())); // recording-related signals connect(doc, TQT_SIGNAL(newMIDIRecordingSegment(Segment*)), this, TQT_SLOT(slotNewMIDIRecordingSegment(Segment*))); connect(doc, TQT_SIGNAL(newAudioRecordingSegment(Segment*)), this, TQT_SLOT(slotNewAudioRecordingSegment(Segment*))); // connect(doc, TQT_SIGNAL(recordMIDISegmentUpdated(Segment*, timeT)), // this, TQT_SLOT(slotRecordMIDISegmentUpdated(Segment*, timeT))); connect(doc, TQT_SIGNAL(stoppedAudioRecording()), this, TQT_SLOT(slotStoppedRecording())); connect(doc, TQT_SIGNAL(stoppedMIDIRecording()), this, TQT_SLOT(slotStoppedRecording())); connect(doc, TQT_SIGNAL(audioFileFinalized(Segment*)), getModel(), TQT_SLOT(slotAudioFileFinalized(Segment*))); } CompositionModelImpl* cmi = dynamic_cast(model); if (cmi) { cmi->setAudioPreviewThread(&doc->getAudioPreviewThread()); } if (doc) { doc->getAudioPreviewThread().setEmptyQueueListener(this); } m_segmentsDrawBuffer.setOptimization(TQPixmap::BestOptim); m_artifactsDrawBuffer.setOptimization(TQPixmap::BestOptim); viewport()->setMouseTracking(true); } void CompositionView::endAudioPreviewGeneration() { CompositionModelImpl* cmi = dynamic_cast(m_model); if (cmi) { cmi->setAudioPreviewThread(0); } } void CompositionView::setBackgroundPixmap(const TQPixmap &m) { m_backgroundPixmap = m; // viewport()->setErasePixmap(m_backgroundPixmap); } void CompositionView::initStepSize() { TQScrollBar* 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 TQPoint& pos) { m_selectionRect.setRect(pos.x(), pos.y(), 0, 0); getModel()->setSelectionRect(m_selectionRect); } void CompositionView::setSelectionRectSize(int w, int h) { m_selectionRect.setSize(TQSize(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(getModel())->clearSegmentRectsCache(clearPreviews); } SegmentSelection CompositionView::getSelectedSegments() { return (dynamic_cast(m_model))->getSelectedSegments(); } void CompositionView::updateSelectionContents() { if (!haveSelection()) return ; TQRect selectionRect = getModel()->getSelectionContentsRect(); updateContents(selectionRect); } void CompositionView::slotContentsMoving(int x, int y) { // qDebug("contents moving : x=%d", x); } void CompositionView::slotSetTool(const TQString& 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, TQString("CompositionView::slotSetTool() : unknown tool name %1").arg(toolName)); } } void CompositionView::slotSelectSegments(const SegmentSelection &segments) { RG_DEBUG << "CompositionView::slotSelectSegments\n"; static TQRect 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(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(TQWheelEvent* e) { e->accept(); wheelEvent(e); } CompositionItem CompositionView::getFirstItemAt(TQPoint 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 TQRect& 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(TQResizeEvent* e) { TQScrollView::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(TQPaintEvent* e) { TQMemArray rects = e->region().rects(); for (unsigned int i = 0; i < rects.size(); ++i) { viewportPaintRect(rects[i]); } } void CompositionView::viewportPaintRect(TQRect r) { TQRect 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 TQRect 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 = TQRect(); } 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 // TQPainter pdebug(viewport()); // static TQPen framePen(Qt::red, 1); // pdebug.setPen(framePen); // pdebug.drawRect(updateRect); } bool CompositionView::checkScrollAndRefreshDrawBuffer(TQRect &rect, bool& scroll) { bool all = false; TQRect 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) { TQPainter cp(&m_segmentsDrawBuffer); cp.drawPixmap(dx, 0, m_segmentsDrawBuffer); if (dx < 0) { refreshRect |= TQRect(cx + w + dx, cy, -dx, h); } else { refreshRect |= TQRect(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) { TQPainter cp(&m_segmentsDrawBuffer); cp.drawPixmap(0, dy, m_segmentsDrawBuffer); if (dy < 0) { refreshRect |= TQRect(cx, cy + h + dy, w, -dy); } else { refreshRect |= TQRect(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 = TQRect(); m_lastBufferRefreshX = cx; m_lastBufferRefreshY = cy; rect |= refreshRect; if (scroll) rect.setRect(cx, cy, w, h); return needRefresh; } void CompositionView::refreshSegmentsDrawBuffer(const TQRect& rect) { // Profiler profiler("CompositionView::refreshDrawBuffer", true); // RG_DEBUG << "CompositionView::refreshSegmentsDrawBuffer() r = " // << rect << endl; TQPainter p(&m_segmentsDrawBuffer, viewport()); p.translate( -contentsX(), -contentsY()); if (!m_backgroundPixmap.isNull()) { TQPoint 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 // TQPen framePen(Qt::red, 1); // p.setPen(framePen); // p.drawRect(rect); // m_segmentsDrawBufferNeedsRefresh = false; } void CompositionView::refreshArtifactsDrawBuffer(const TQRect& rect) { // RG_DEBUG << "CompositionView::refreshArtifactsDrawBuffer() r = " // << rect << endl; TQPainter p; p.begin(&m_artifactsDrawBuffer, viewport()); p.translate( -contentsX(), -contentsY()); // TQRect r(contentsX(), contentsY(), m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height()); drawAreaArtifacts(&p, rect); p.end(); // m_artifactsDrawBufferNeedsRefresh = false; } void CompositionView::drawArea(TQPainter *p, const TQRect& 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(); TQColor 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); TQColor defaultCol = CompositionColourCache::getInstance()->SegmentInternalPreview; TQColor 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(TQPainter * p, const TQRect& clipRect) { CompositionModel::AudioPreviewDrawData::const_iterator api = m_audioPreviewRects.begin(); CompositionModel::AudioPreviewDrawData::const_iterator apEnd = m_audioPreviewRects.end(); TQRect rectToFill, // rect to fill on canvas localRect; // the rect of the tile to draw on the canvas TQPoint basePoint, // origin of segment rect drawBasePoint; // origin of rect to fill on canvas TQRect 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(TQPainter * p, const TQRect& 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(TQPainter * p, const TQRect& /*clipRect*/) { // no need to check for clipping, these guides are meant to follow the mouse cursor TQPoint 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, TQPainter *p, const TQRect& clipRect, int intersectLvl, bool fill) { p->save(); TQBrush brush = r.getBrush(); if (r.isRepeating()) { TQColor 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 // TQRect 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()) { TQPoint 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, TQPainter *p, const TQRect& clipRect) { // draw segment label // #ifdef NOT_DEFINED if (!r.getLabel().isEmpty() /* && !r.isSelected() */) { p->save(); p->setPen(GUIPalette::getColour(GUIPalette::SegmentLabel)); p->setBrush(white); TQRect textRect(r); textRect.setX(textRect.x() + 3); TQString label = " " + r.getLabel() + " "; TQRect 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(); TQFont font; font.setPixelSize(r.height() / 2.2); font.setWeight(TQFont::Bold); font.setItalic(false); p->setFont(font); TQRect 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(); TQBrush brush = r.getBrush(); TQColor 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 TQRect& r, TQPainter *p, const TQRect& 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(); TQRect rect = r; if (fill) { if (isSelected) { TQColor fillColor = p->brush().color(); fillColor = fillColor.dark(200); TQBrush b = p->brush(); b.setColor(fillColor); p->setBrush(b); // RG_DEBUG << "CompositionView::drawRect : selected color : " << fillColor << endl; } if (intersectLvl > 0) { TQColor fillColor = p->brush().color(); fillColor = fillColor.dark((intersectLvl) * 105); TQBrush 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... TQRect 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(); } TQColor CompositionView::mixBrushes(TQBrush a, TQBrush b) { TQColor 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, TQPainter * p, const TQRect& 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(TQPainter *p, const TQRect& 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(TQPainter *p, const TQRect& clipRect) { TQFontMetrics metrics(p->fontMetrics()); TQRect 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); TQPoint 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(TQEvent* e) { if (e->type() == AudioPreviewThread::AudioPreviewQueueEmpty) { RG_DEBUG << "CompositionView::event - AudioPreviewQueueEmpty\n"; slotSegmentsDrawBufferNeedsRefresh(); viewport()->update(); return true; } return RosegardenScrollView::event(e); } void CompositionView::enterEvent(TQEvent *e) { kapp->config()->setGroup(GeneralOptionsConfigGroup); if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return; emit showContextHelp(m_toolContextHelp); m_contextHelpShown = true; } void CompositionView::leaveEvent(TQEvent *e) { emit showContextHelp(""); m_contextHelpShown = false; } void CompositionView::slotToolHelpChanged(const TQString &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(TQMouseEvent* 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(TQMouseEvent* 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(TQMouseEvent* 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((_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(TQMouseEvent* 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 TQRect updateRect (std::min(m_pointerPos, oldPos) - m_pointerPen.width(), 0, deltaW + m_pointerPen.width() * 2, contentsHeight()); slotArtifactsDrawBufferNeedsRefresh(updateRect); } else { slotArtifactsDrawBufferNeedsRefresh (TQRect(m_pointerPos - m_pointerPen.width(), 0, m_pointerPen.width() * 2, contentsHeight())); slotArtifactsDrawBufferNeedsRefresh (TQRect(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 TQPoint& p) { m_topGuidePos = p.x(); m_foreGuidePos = p.y(); slotArtifactsDrawBufferNeedsRefresh(); } void CompositionView::setDrawGuides(bool d) { m_drawGuides = d; slotArtifactsDrawBufferNeedsRefresh(); } void CompositionView::setTmpRect(const TQRect& r) { setTmpRect(r, m_tmpRectFill); } void CompositionView::setTmpRect(const TQRect& r, const TQColor &c) { TQRect pRect = m_tmpRect; m_tmpRect = r; m_tmpRectFill = c; slotUpdateSegmentsDrawBuffer(m_tmpRect | pRect); } void CompositionView::setTextFloat(int x, int y, const TQString &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 // TQRect 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(TQString::null); } } #include "CompositionView.moc"