/* 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 "MarkerRuler.h" #include "misc/Debug.h" #include "misc/Strings.h" #include "base/Composition.h" #include "base/RulerScale.h" #include "document/RosegardenGUIDoc.h" #include "gui/general/GUIPalette.h" #include "gui/general/HZoomable.h" #include "gui/dialogs/MarkerModifyDialog.h" #include "commands/edit/ModifyMarkerCommand.h" #include "document/MultiViewCommandHistory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Rosegarden { MarkerRuler::MarkerRuler(RosegardenGUIDoc *doc, RulerScale *rulerScale, int barHeight, double xorigin, TQWidget* parent, const char* name, WFlags f) : TQWidget(parent, name, f), m_barHeight(barHeight), m_xorigin(xorigin), m_currentXOffset(0), m_width(-1), m_clickX(0), m_menu(0), m_doc(doc), m_rulerScale(rulerScale), m_parentMainWindow(dynamic_cast(doc->parent())) { // If the parent window has a main window above it, we need to use // that as the parent main window, not the document's parent. // Otherwise we'll end up adding all actions to the same // (document-level) action collection regardless of which window // we're in. TQObject *probe = parent; while (probe && !dynamic_cast(probe)) probe = probe->parent(); if (probe) m_parentMainWindow = dynamic_cast(probe); // m_barFont = new TQFont("helvetica", 12); // m_barFont->setPixelSize(12); m_barFont = new TQFont(); m_barFont->setPointSize(10); TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/"); TQIconSet icon; // Use the event insert, delete, edit icons because they are // actually generic enough to serve for anything. Let's hope they // don't become more event-specific in future... icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-insert.png")); new TDEAction(i18n("Insert Marker"), icon, 0, this, TQ_SLOT(slotInsertMarkerHere()), actionCollection(), "insert_marker_here"); new TDEAction(i18n("Insert Marker at Playback Position"), 0, this, TQ_SLOT(slotInsertMarkerAtPointer()), actionCollection(), "insert_marker_at_pointer"); icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-delete.png")); new TDEAction(i18n("Delete Marker"), icon, 0, this, TQ_SLOT(slotDeleteMarker()), actionCollection(), "delete_marker"); icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-edit.png")); new TDEAction(i18n("Edit Marker..."), icon, 0, this, TQ_SLOT(slotEditMarker()), actionCollection(), "edit_marker"); TQToolTip::add (this, i18n("Click on a marker to move the playback pointer.\nShift-click to set a range between markers.\nDouble-click to open the marker editor.")); } MarkerRuler::~MarkerRuler() { delete m_barFont; // we have to do this so that the menu is re-created properly // when the main window is itself recreated (on a File->New for instance) KXMLGUIFactory* factory = m_parentMainWindow->factory(); if (factory) factory->removeClient(this); } void MarkerRuler::createMenu() { setXMLFile("markerruler.rc"); KXMLGUIFactory* factory = m_parentMainWindow->factory(); factory->addClient(this); TQWidget* tmp = factory->container("marker_ruler_menu", this); // if (!tmp) { // RG_DEBUG << "MarkerRuler::createMenu() menu not found\n" // << domDocument().toString(4) << endl; // } m_menu = dynamic_cast(tmp); if (!m_menu) { RG_DEBUG << "MarkerRuler::createMenu() failed\n"; } } void MarkerRuler::scrollHoriz(int x) { m_currentXOffset = static_cast( -x / getHScaleFactor()); repaint(); } TQSize MarkerRuler::sizeHint() const { int lastBar = m_rulerScale->getLastVisibleBar(); double width = m_rulerScale->getBarPosition(lastBar) + m_rulerScale->getBarWidth(lastBar) + m_xorigin; return TQSize(std::max(int(width), m_width), m_barHeight); } TQSize MarkerRuler::minimumSizeHint() const { double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin; return TQSize(static_cast(firstBarWidth), m_barHeight); } void MarkerRuler::slotInsertMarkerHere() { emit addMarker(getClickPosition()); } void MarkerRuler::slotInsertMarkerAtPointer() { emit addMarker(m_doc->getComposition().getPosition()); } void MarkerRuler::slotDeleteMarker() { RG_DEBUG << "MarkerRuler::slotDeleteMarker()\n"; Rosegarden::Marker* marker = getMarkerAtClickPosition(); if (marker) emit deleteMarker(marker->getID(), marker->getTime(), marker->getName().c_str(), marker->getDescription().c_str()); } void MarkerRuler::slotEditMarker() { Rosegarden::Marker* marker = getMarkerAtClickPosition(); if (!marker) return; // I think the ruler should be doing all this stuff itself, or // emitting signals connected to a dedicated marker model object, // not just relying on the app object. Same goes for practically // everything else we do. Hey ho. Having this here is // inconsistent with the other methods, so if anyone wants to move // it, be my guest. MarkerModifyDialog dialog(this, &m_doc->getComposition(), marker); if (dialog.exec() == TQDialog::Accepted) { ModifyMarkerCommand *command = new ModifyMarkerCommand(&m_doc->getComposition(), marker->getID(), dialog.getOriginalTime(), dialog.getTime(), qstrtostr(dialog.getName()), qstrtostr(dialog.getDescription())); m_doc->getCommandHistory()->addCommand(command); } } timeT MarkerRuler::getClickPosition() { timeT t = m_rulerScale->getTimeForX (m_clickX - m_xorigin - m_currentXOffset); return t; } Rosegarden::Marker* MarkerRuler::getMarkerAtClickPosition() { TQRect clipRect = visibleRect(); int firstBar = m_rulerScale->getBarForX(clipRect.x() - m_currentXOffset - m_xorigin); int lastBar = m_rulerScale->getLastVisibleBar(); if (firstBar < m_rulerScale->getFirstVisibleBar()) { firstBar = m_rulerScale->getFirstVisibleBar(); } Composition &comp = m_doc->getComposition(); Composition::markercontainer markers = comp.getMarkers(); timeT start = comp.getBarStart(firstBar); timeT end = comp.getBarEnd(lastBar); // need these to calculate the visible extents of a marker tag TQPainter painter(this); painter.setFont(*m_barFont); TQFontMetrics metrics = painter.fontMetrics(); for (Composition::markerconstiterator i = markers.begin(); i != markers.end(); ++i) { if ((*i)->getTime() >= start && (*i)->getTime() < end) { TQString name(strtoqstr((*i)->getName())); int x = m_rulerScale->getXForTime((*i)->getTime()) + m_xorigin + m_currentXOffset; int width = metrics.width(name) + 5; int nextX = -1; Composition::markerconstiterator j = i; ++j; if (j != markers.end()) { nextX = m_rulerScale->getXForTime((*j)->getTime()) + m_xorigin + m_currentXOffset; } if (m_clickX >= x && m_clickX <= x + width) { if (nextX < x || m_clickX <= nextX) { return *i; } } } } return 0L; } void MarkerRuler::paintEvent(TQPaintEvent*) { TQPainter painter(this); painter.setFont(*m_barFont); if (getHScaleFactor() != 1.0) painter.scale(getHScaleFactor(), 1.0); TQRect clipRect = visibleRect(); int firstBar = m_rulerScale->getBarForX(clipRect.x() - m_currentXOffset - m_xorigin); int lastBar = m_rulerScale->getLastVisibleBar(); if (firstBar < m_rulerScale->getFirstVisibleBar()) { firstBar = m_rulerScale->getFirstVisibleBar(); } painter.drawLine(m_currentXOffset, 0, static_cast(visibleRect().width() / getHScaleFactor()), 0); float minimumWidth = 25.0; float testSize = ((float)(m_rulerScale->getBarPosition(firstBar + 1) - m_rulerScale->getBarPosition(firstBar))) / minimumWidth; int every = 0; int count = 0; if (testSize < 1.0) { every = (int(1.0 / testSize)); if (every % 2 == 0) every++; } for (int i = firstBar; i <= lastBar; ++i) { double x = m_rulerScale->getBarPosition(i) + m_xorigin + m_currentXOffset; if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width()) break; // always the first bar number if (every && i != firstBar) { if (count < every) { count++; continue; } // reset count if we passed count = 0; } // adjust count for first bar line if (every == firstBar) count++; if (i != lastBar) { painter.drawLine(static_cast(x), 0, static_cast(x), m_barHeight); // disable worldXForm for text TQPoint textDrawPoint = painter.xForm(TQPoint(static_cast(x + 4), 12)); bool enableXForm = painter.hasWorldXForm(); painter.setWorldXForm(false); if (i >= 0) painter.drawText(textDrawPoint, TQString("%1").arg(i + 1)); painter.setWorldXForm(enableXForm); } else { const TQPen normalPen = painter.pen(); ; TQPen endPen(black, 2); painter.setPen(endPen); painter.drawLine(static_cast(x), 0, static_cast(x), m_barHeight); painter.setPen(normalPen); } } if (m_doc) { Composition &comp = m_doc->getComposition(); Composition::markercontainer markers = comp.getMarkers(); Composition::markerconstiterator it; timeT start = comp.getBarStart(firstBar); timeT end = comp.getBarEnd(lastBar); TQFontMetrics metrics = painter.fontMetrics(); for (it = markers.begin(); it != markers.end(); ++it) { if ((*it)->getTime() >= start && (*it)->getTime() < end) { TQString name(strtoqstr((*it)->getName())); double x = m_rulerScale->getXForTime((*it)->getTime()) + m_xorigin + m_currentXOffset; painter.fillRect(static_cast(x), 1, static_cast(metrics.width(name) + 5), m_barHeight - 2, TQBrush(GUIPalette::getColour(GUIPalette::MarkerBackground))); painter.drawLine(int(x), 1, int(x), m_barHeight - 2); painter.drawLine(int(x) + 1, 1, int(x) + 1, m_barHeight - 2); TQPoint textDrawPoint = painter.xForm (TQPoint(static_cast(x + 3), m_barHeight - 4)); // disable worldXForm for text bool enableXForm = painter.hasWorldXForm(); painter.setWorldXForm(false); painter.drawText(textDrawPoint, name); painter.setWorldXForm(enableXForm); } } } } void MarkerRuler::mousePressEvent(TQMouseEvent *e) { RG_DEBUG << "MarkerRuler::mousePressEvent: x = " << e->x() << endl; if (!m_doc || !e) return; m_clickX = e->x(); Rosegarden::Marker* clickedMarker = getMarkerAtClickPosition(); // if right-click, show popup menu // if (e->button() == TQt::RightButton) { if (!m_menu) createMenu(); if (m_menu) { actionCollection()->action("delete_marker")->setEnabled(clickedMarker != 0); actionCollection()->action("edit_marker")->setEnabled(clickedMarker != 0); m_menu->exec(TQCursor::pos()); } return; } bool shiftPressed = ((e->state() & TQt::ShiftButton) != 0); Composition &comp = m_doc->getComposition(); Composition::markercontainer markers = comp.getMarkers(); if (shiftPressed) { // set loop timeT t = m_rulerScale->getTimeForX (e->x() - m_xorigin - m_currentXOffset); timeT prev = 0; for (Composition::markerconstiterator i = markers.begin(); i != markers.end(); ++i) { timeT cur = (*i)->getTime(); if (cur >= t) { emit setLoop(prev, cur); return ; } prev = cur; } if (prev > 0) emit setLoop(prev, comp.getEndMarker()); } else { // set pointer to clicked marker if (clickedMarker) emit setPointerPosition(clickedMarker->getTime()); } } void MarkerRuler::mouseDoubleClickEvent(TQMouseEvent *) { RG_DEBUG << "MarkerRuler::mouseDoubleClickEvent" << endl; emit editMarkers(); } } #include "MarkerRuler.moc"