summaryrefslogtreecommitdiffstats
path: root/src/gui/editors/segment
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/editors/segment')
-rw-r--r--src/gui/editors/segment/ControlEditorDialog.cpp446
-rw-r--r--src/gui/editors/segment/ControlEditorDialog.h122
-rw-r--r--src/gui/editors/segment/ControlParameterEditDialog.cpp325
-rw-r--r--src/gui/editors/segment/ControlParameterEditDialog.h92
-rw-r--r--src/gui/editors/segment/ControlParameterItem.cpp34
-rw-r--r--src/gui/editors/segment/ControlParameterItem.h65
-rw-r--r--src/gui/editors/segment/MarkerEditor.cpp594
-rw-r--r--src/gui/editors/segment/MarkerEditor.h124
-rw-r--r--src/gui/editors/segment/MarkerEditorViewItem.cpp51
-rw-r--r--src/gui/editors/segment/MarkerEditorViewItem.h70
-rw-r--r--src/gui/editors/segment/PlayList.cpp254
-rw-r--r--src/gui/editors/segment/PlayList.h93
-rw-r--r--src/gui/editors/segment/PlayListDialog.cpp76
-rw-r--r--src/gui/editors/segment/PlayListDialog.h71
-rw-r--r--src/gui/editors/segment/PlayListView.cpp66
-rw-r--r--src/gui/editors/segment/PlayListView.h52
-rw-r--r--src/gui/editors/segment/PlayListViewItem.cpp42
-rw-r--r--src/gui/editors/segment/PlayListViewItem.h47
-rw-r--r--src/gui/editors/segment/TrackButtons.cpp1149
-rw-r--r--src/gui/editors/segment/TrackButtons.h228
-rw-r--r--src/gui/editors/segment/TrackEditor.cpp827
-rw-r--r--src/gui/editors/segment/TrackEditor.h248
-rw-r--r--src/gui/editors/segment/TrackEditorIface.cpp33
-rw-r--r--src/gui/editors/segment/TrackEditorIface.h55
-rw-r--r--src/gui/editors/segment/TrackHeader.cpp64
-rw-r--r--src/gui/editors/segment/TrackHeader.h65
-rw-r--r--src/gui/editors/segment/TrackLabel.cpp203
-rw-r--r--src/gui/editors/segment/TrackLabel.h122
-rw-r--r--src/gui/editors/segment/TrackVUMeter.cpp77
-rw-r--r--src/gui/editors/segment/TrackVUMeter.h65
-rw-r--r--src/gui/editors/segment/TriggerManagerItem.cpp60
-rw-r--r--src/gui/editors/segment/TriggerManagerItem.h72
-rw-r--r--src/gui/editors/segment/TriggerSegmentManager.cpp576
-rw-r--r--src/gui/editors/segment/TriggerSegmentManager.h116
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp316
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h79
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp267
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h99
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp149
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h90
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp62
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.h69
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp150
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h61
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h74
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.cpp43
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.h179
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp1328
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h239
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.cpp42
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.h108
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.cpp1591
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.h366
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.h62
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp88
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp37
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h91
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp73
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.h70
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.cpp348
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.h78
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp48
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.h59
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp295
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp393
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.h87
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp532
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.h109
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp175
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.cpp115
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.h105
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp102
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.h63
80 files changed, 15261 insertions, 0 deletions
diff --git a/src/gui/editors/segment/ControlEditorDialog.cpp b/src/gui/editors/segment/ControlEditorDialog.cpp
new file mode 100644
index 0000000..3c4cc47
--- /dev/null
+++ b/src/gui/editors/segment/ControlEditorDialog.cpp
@@ -0,0 +1,446 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlEditorDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/Composition.h"
+#include "base/ControlParameter.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/AddControlParameterCommand.h"
+#include "commands/studio/ModifyControlParameterCommand.h"
+#include "commands/studio/RemoveControlParameterCommand.h"
+#include "ControlParameterEditDialog.h"
+#include "ControlParameterItem.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qcolor.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const QString notShowing(i18n("<not showing>"));
+
+ControlEditorDialog::ControlEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId device):
+ KMainWindow(parent, "controleditordialog"),
+ m_studio(&doc->getStudio()),
+ m_doc(doc),
+ m_device(device),
+ m_modified(false)
+{
+ RG_DEBUG << "ControlEditorDialog::ControlEditorDialog: device is " << m_device << endl;
+
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Control Events"));
+
+ QString deviceName(i18n("<no device>"));
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+ if (md)
+ deviceName = strtoqstr(md->getName());
+
+ // spacing hack!
+ new QLabel("", mainFrame);
+ new QLabel(i18n(" Control Events for %1 (device %2)").arg(deviceName).
+ arg(device), mainFrame);
+ new QLabel("", mainFrame);
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn(i18n("Control Event name "));
+ m_listView->addColumn(i18n("Control Event type "));
+ m_listView->addColumn(i18n("Control Event value "));
+ m_listView->addColumn(i18n("Description "));
+ m_listView->addColumn(i18n("Min "));
+ m_listView->addColumn(i18n("Max "));
+ m_listView->addColumn(i18n("Default "));
+ m_listView->addColumn(i18n("Color "));
+ m_listView->addColumn(i18n("Position on instrument panel"));
+
+ m_listView->setColumnAlignment(0, Qt::AlignLeft);
+
+ // Align remaining columns centrally
+ for (int i = 1; i < 9; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ m_listView->restoreLayout(kapp->config(), ControlEditorConfigGroup);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Control Parameter to the Studio"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Control Parameter from the Studio"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Control Parameter editor"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+
+ initDialog();
+
+ setAutoSaveSettings(ControlEditorConfigGroup, true);
+}
+
+ControlEditorDialog::~ControlEditorDialog()
+{
+ RG_DEBUG << "\n*** ControlEditorDialog::~ControlEditorDialog\n" << endl;
+
+ m_listView->saveLayout(kapp->config(), ControlEditorConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+ControlEditorDialog::initDialog()
+{
+ RG_DEBUG << "ControlEditorDialog::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+ControlEditorDialog::slotUpdate()
+{
+ RG_DEBUG << "ControlEditorDialog::slotUpdate" << endl;
+
+ //QPtrList<QListViewItem> selection = m_listView->selectedItems();
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+ if (!md)
+ return ;
+
+ ControlList::const_iterator it = md->beginControllers();
+ QListViewItem *item;
+ int i = 0;
+
+ m_listView->clear();
+
+ for (; it != md->endControllers(); ++it) {
+ Composition &comp = m_doc->getComposition();
+
+ QString colour =
+ strtoqstr(comp.getGeneralColourMap().getNameByIndex(it->getColourIndex()));
+
+ if (colour == "")
+ colour = i18n("<default>");
+
+ QString position = QString("%1").arg(it->getIPBPosition());
+ if (position.toInt() == -1)
+ position = notShowing;
+
+ QString value;
+ value.sprintf("%d (0x%x)", it->getControllerValue(),
+ it->getControllerValue());
+
+ if (it->getType() == PitchBend::EventType) {
+ item = new ControlParameterItem(i++,
+ m_listView,
+ strtoqstr(it->getName()),
+ strtoqstr(it->getType()),
+ QString("-"),
+ strtoqstr(it->getDescription()),
+ QString("%1").arg(it->getMin()),
+ QString("%1").arg(it->getMax()),
+ QString("%1").arg(it->getDefault()),
+ colour,
+ position);
+ } else {
+ item = new ControlParameterItem(i++,
+ m_listView,
+ strtoqstr(it->getName()),
+ strtoqstr(it->getType()),
+ value,
+ strtoqstr(it->getDescription()),
+ QString("%1").arg(it->getMin()),
+ QString("%1").arg(it->getMax()),
+ QString("%1").arg(it->getDefault()),
+ colour,
+ position);
+ }
+
+
+ // create and set a colour pixmap
+ //
+ QPixmap colourPixmap(16, 16);
+ Colour c = comp.getGeneralColourMap().getColourByIndex(it->getColourIndex());
+ colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
+ item->setPixmap(7, colourPixmap);
+
+ m_listView->insertItem(item);
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item = new QListViewItem(m_listView, i18n("<none>"));
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+
+
+}
+
+/*
+void
+ControlEditorDialog::slotEditCopy()
+{
+ RG_DEBUG << "ControlEditorDialog::slotEditCopy" << endl;
+}
+
+void
+ControlEditorDialog::slotEditPaste()
+{
+ RG_DEBUG << "ControlEditorDialog::slotEditPaste" << endl;
+}
+*/
+
+void
+ControlEditorDialog::slotAdd()
+{
+ RG_DEBUG << "ControlEditorDialog::slotAdd to device " << m_device << endl;
+
+ AddControlParameterCommand *command =
+ new AddControlParameterCommand(m_studio, m_device,
+ ControlParameter());
+
+ addCommandToHistory(command);
+}
+
+void
+ControlEditorDialog::slotDelete()
+{
+ RG_DEBUG << "ControlEditorDialog::slotDelete" << endl;
+
+ if (!m_listView->currentItem())
+ return ;
+
+ ControlParameterItem *item =
+ dynamic_cast<ControlParameterItem*>(m_listView->currentItem());
+
+ if (item) {
+ RemoveControlParameterCommand *command =
+ new RemoveControlParameterCommand(m_studio, m_device, item->getId());
+
+ addCommandToHistory(command);
+ }
+}
+
+void
+ControlEditorDialog::slotClose()
+{
+ RG_DEBUG << "ControlEditorDialog::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+ControlEditorDialog::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ createGUI("controleditor.rc");
+}
+
+void
+ControlEditorDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+ControlEditorDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+ControlEditorDialog::setModified(bool modified)
+{
+ RG_DEBUG << "ControlEditorDialog::setModified(" << modified << ")" << endl;
+
+ if (modified) {}
+ else {}
+
+ m_modified = modified;
+}
+
+void
+ControlEditorDialog::checkModified()
+{
+ RG_DEBUG << "ControlEditorDialog::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+ControlEditorDialog::slotEdit()
+{}
+
+void
+ControlEditorDialog::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "ControlEditorDialog::slotEdit" << endl;
+
+ ControlParameterItem *item =
+ dynamic_cast<ControlParameterItem*>(i);
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+
+ if (item && md) {
+ ControlParameterEditDialog dialog
+ (this,
+ md->getControlParameter(item->getId()), m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyControlParameterCommand *command =
+ new ModifyControlParameterCommand(m_studio,
+ m_device,
+ dialog.getControl(),
+ item->getId());
+
+ addCommandToHistory(command);
+ }
+ }
+}
+
+void
+ControlEditorDialog::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+ControlEditorDialog::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_studio = &doc->getStudio();
+ m_modified = false;
+
+ slotUpdate();
+}
+
+}
+#include "ControlEditorDialog.moc"
diff --git a/src/gui/editors/segment/ControlEditorDialog.h b/src/gui/editors/segment/ControlEditorDialog.h
new file mode 100644
index 0000000..9270d2c
--- /dev/null
+++ b/src/gui/editors/segment/ControlEditorDialog.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLEDITORDIALOG_H_
+#define _RG_CONTROLEDITORDIALOG_H_
+
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include <kmainwindow.h>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QCloseEvent;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class ControlEditorDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ ControlEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId device);
+
+ ~ControlEditorDialog();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ DeviceId getDevice() { return m_device; }
+
+public slots:
+ void slotUpdate();
+
+/*
+ void slotEditCopy();
+ void slotEditPaste();
+*/
+
+ void slotAdd();
+ void slotDelete();
+ void slotClose();
+
+ void slotEdit();
+ void slotEdit(QListViewItem *);
+
+signals:
+ void closing();
+
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+
+ //--------------- Data members ---------------------------------
+ Studio *m_studio;
+ RosegardenGUIDoc *m_doc;
+ DeviceId m_device;
+
+ QPushButton *m_closeButton;
+
+ QPushButton *m_copyButton;
+ QPushButton *m_pasteButton;
+
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ ControlList m_clipboard; // local clipboard only
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/ControlParameterEditDialog.cpp b/src/gui/editors/segment/ControlParameterEditDialog.cpp
new file mode 100644
index 0000000..bc779f5
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterEditDialog.cpp
@@ -0,0 +1,325 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlParameterEditDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/ControlParameter.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcolor.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qpixmap.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const QString notShowing(i18n("<not showing>"));
+
+ControlParameterEditDialog::ControlParameterEditDialog(
+ QWidget *parent,
+ ControlParameter *control,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, 0, true,
+ i18n("Edit Control Parameter"), Ok | Cancel),
+ m_doc(doc),
+ m_control(control)
+{
+ m_dialogControl = *control; // copy in the ControlParameter
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Control Event Properties"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);
+
+ layout->addWidget(new QLabel(i18n("Name:"), frame), 0, 0);
+ m_nameEdit = new QLineEdit(frame);
+ layout->addWidget(m_nameEdit, 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Type:"), frame), 1, 0);
+ m_typeCombo = new KComboBox(frame);
+ layout->addMultiCellWidget(m_typeCombo, 1, 1, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Description:"), frame), 2, 0);
+ m_description = new QLineEdit(frame);
+ layout->addMultiCellWidget(m_description, 2, 2, 1, 2);
+
+ // hex value alongside decimal value
+ m_hexValue = new QLabel(frame);
+ layout->addWidget(m_hexValue, 3, 1);
+
+ layout->addWidget(new QLabel(i18n("Control Event value:"), frame), 3, 0);
+ m_controllerBox = new QSpinBox(frame);
+ layout->addWidget(m_controllerBox, 3, 2);
+
+ layout->addWidget(new QLabel(i18n("Minimum value:"), frame), 4, 0);
+ m_minBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_minBox, 4, 4, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Maximum value:"), frame), 5, 0);
+ m_maxBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_maxBox, 5, 5, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Default value:"), frame), 6, 0);
+ m_defaultBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_defaultBox, 6, 6, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Color:"), frame), 7, 0);
+ m_colourCombo = new KComboBox(frame);
+ layout->addMultiCellWidget(m_colourCombo, 7, 7, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Instrument Parameter Box position:"), frame), 8, 0);
+ m_ipbPosition = new KComboBox(frame);
+ layout->addMultiCellWidget(m_ipbPosition, 8, 8, 1, 2);
+
+ connect(m_nameEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotNameChanged(const QString&)));
+
+ connect(m_typeCombo, SIGNAL(activated(int)),
+ SLOT(slotTypeChanged(int)));
+
+ connect(m_description, SIGNAL(textChanged(const QString&)),
+ SLOT(slotDescriptionChanged(const QString &)));
+
+ connect(m_controllerBox, SIGNAL(valueChanged(int)),
+ SLOT(slotControllerChanged(int)));
+
+ connect(m_minBox, SIGNAL(valueChanged(int)),
+ SLOT(slotMinChanged(int)));
+
+ connect(m_maxBox, SIGNAL(valueChanged(int)),
+ SLOT(slotMaxChanged(int)));
+
+ connect(m_defaultBox, SIGNAL(valueChanged(int)),
+ SLOT(slotDefaultChanged(int)));
+
+ connect(m_colourCombo, SIGNAL(activated(int)),
+ SLOT(slotColourChanged(int)));
+
+ connect(m_ipbPosition, SIGNAL(activated(int)),
+ SLOT(slotIPBPositionChanged(int)));
+
+ //m_nameEdit->selectAll();
+ //m_description->selectAll();
+
+ // set limits
+ m_controllerBox->setMinValue(0);
+ m_controllerBox->setMaxValue(127);
+
+ m_minBox->setMinValue(INT_MIN);
+ m_minBox->setMaxValue(INT_MAX);
+
+ m_maxBox->setMinValue(INT_MIN);
+ m_maxBox->setMaxValue(INT_MAX);
+
+ m_defaultBox->setMinValue(INT_MIN);
+ m_defaultBox->setMaxValue(INT_MAX);
+
+ // populate combos
+ m_typeCombo->insertItem(strtoqstr(Controller::EventType));
+ m_typeCombo->insertItem(strtoqstr(PitchBend::EventType));
+ /*
+ m_typeCombo->insertItem(strtoqstr(KeyPressure::EventType));
+ m_typeCombo->insertItem(strtoqstr(ChannelPressure::EventType));
+ */
+
+ // Populate colour combo
+ //
+ //
+ ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
+ RCMap::const_iterator it;
+ QPixmap colourPixmap(16, 16);
+
+ for (it = colourMap.begin(); it != colourMap.end(); ++it) {
+ Colour c = it->second.first;
+ colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
+ m_colourCombo->insertItem(colourPixmap, strtoqstr(it->second.second));
+ }
+
+ // Populate IPB position combo
+ //
+ m_ipbPosition->insertItem(notShowing);
+ for (unsigned int i = 0; i < 32; i++)
+ m_ipbPosition->insertItem(QString("%1").arg(i));
+
+ if (m_control->getType() == Controller::EventType)
+ m_typeCombo->setCurrentItem(0);
+ else if (m_control->getType() == PitchBend::EventType)
+ m_typeCombo->setCurrentItem(1);
+ /*
+ else if (m_control->getType() == KeyPressure::EventType)
+ m_typeCombo->setCurrentItem(2);
+ else if (m_control->getType() == ChannelPressure::EventType)
+ m_typeCombo->setCurrentItem(3);
+ */
+
+ populate();
+}
+
+void
+ControlParameterEditDialog::populate()
+{
+ m_nameEdit->setText(strtoqstr(m_control->getName()));
+
+ m_description->setText(strtoqstr(m_control->getDescription()));
+ m_controllerBox->setValue(int(m_control->getControllerValue()));
+
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", m_control->getControllerValue());
+ m_hexValue->setText(hexValue);
+
+ m_minBox->setValue(m_control->getMin());
+ m_maxBox->setValue(m_control->getMax());
+ m_defaultBox->setValue(m_control->getDefault());
+
+ int pos = 0, setItem = 0;
+ ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
+ RCMap::const_iterator it;
+ for (it = colourMap.begin(); it != colourMap.end(); ++it)
+ if (m_control->getColourIndex() == it->first)
+ setItem = pos++;
+
+ m_colourCombo->setCurrentItem(setItem);
+
+ // set combo position
+ m_ipbPosition->setCurrentItem(m_control->getIPBPosition() + 1);
+
+ // If the type has changed and there are no defaults then we have to
+ // supply some.
+ //
+ if (qstrtostr(m_typeCombo->currentText()) == PitchBend::EventType ||
+ qstrtostr(m_typeCombo->currentText()) == KeyPressure::EventType ||
+ qstrtostr(m_typeCombo->currentText()) == ChannelPressure::EventType) {
+ m_controllerBox->setEnabled(false);
+ m_ipbPosition->setEnabled(false);
+ m_colourCombo->setEnabled(false);
+ m_hexValue->setEnabled(false);
+ m_minBox->setEnabled(false);
+ m_maxBox->setEnabled(false);
+ m_defaultBox->setEnabled(false);
+ } else if (qstrtostr(m_typeCombo->currentText()) == Controller::EventType) {
+ m_controllerBox->setEnabled(true);
+ m_ipbPosition->setEnabled(true);
+ m_colourCombo->setEnabled(true);
+ m_hexValue->setEnabled(true);
+ m_minBox->setEnabled(true);
+ m_maxBox->setEnabled(true);
+ m_defaultBox->setEnabled(true);
+ }
+
+}
+
+void
+ControlParameterEditDialog::slotNameChanged(const QString &str)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotNameChanged" << endl;
+ m_dialogControl.setName(qstrtostr(str));
+}
+
+void
+ControlParameterEditDialog::slotTypeChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotTypeChanged" << endl;
+ m_dialogControl.setType(qstrtostr(m_typeCombo->text(value)));
+
+ populate();
+}
+
+void
+ControlParameterEditDialog::slotDescriptionChanged(const QString &str)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotDescriptionChanged" << endl;
+ m_dialogControl.setDescription(qstrtostr(str));
+}
+
+void
+ControlParameterEditDialog::slotControllerChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotControllerChanged" << endl;
+ m_dialogControl.setControllerValue(value);
+
+ // set hex value
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", value);
+ m_hexValue->setText(hexValue);
+}
+
+void
+ControlParameterEditDialog::slotMinChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotMinChanged" << endl;
+ m_dialogControl.setMin(value);
+}
+
+void
+ControlParameterEditDialog::slotMaxChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotMaxChanged" << endl;
+ m_dialogControl.setMax(value);
+}
+
+void
+ControlParameterEditDialog::slotDefaultChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotDefaultChanged" << endl;
+ m_dialogControl.setDefault(value);
+}
+
+void
+ControlParameterEditDialog::slotColourChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotColourChanged" << endl;
+ m_dialogControl.setColourIndex(value);
+}
+
+void
+ControlParameterEditDialog::slotIPBPositionChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotIPBPositionChanged" << endl;
+ m_dialogControl.setIPBPosition(value - 1);
+}
+
+}
+#include "ControlParameterEditDialog.moc"
diff --git a/src/gui/editors/segment/ControlParameterEditDialog.h b/src/gui/editors/segment/ControlParameterEditDialog.h
new file mode 100644
index 0000000..b9f4606
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterEditDialog.h
@@ -0,0 +1,92 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLPARAMETEREDITDIALOG_H_
+#define _RG_CONTROLPARAMETEREDITDIALOG_H_
+
+#include "base/ControlParameter.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QString;
+class QSpinBox;
+class QLineEdit;
+class QLabel;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class ControlParameterEditDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ ControlParameterEditDialog(QWidget *parent,
+ ControlParameter *control,
+ RosegardenGUIDoc *doc);
+
+ ControlParameter& getControl() { return m_dialogControl; }
+
+public slots:
+
+ void slotNameChanged(const QString &);
+ void slotTypeChanged(int);
+ void slotDescriptionChanged(const QString &);
+ void slotControllerChanged(int);
+ void slotMinChanged(int);
+ void slotMaxChanged(int);
+ void slotDefaultChanged(int);
+ void slotColourChanged(int);
+ void slotIPBPositionChanged(int);
+
+protected:
+ void populate(); // populate the dialog
+
+ RosegardenGUIDoc *m_doc;
+ ControlParameter *m_control;
+ ControlParameter m_dialogControl;
+
+ QLineEdit *m_nameEdit;
+ KComboBox *m_typeCombo;
+ QLineEdit *m_description;
+ QSpinBox *m_controllerBox;
+ QSpinBox *m_minBox;
+ QSpinBox *m_maxBox;
+ QSpinBox *m_defaultBox;
+ KComboBox *m_colourCombo;
+ KComboBox *m_ipbPosition;
+ QLabel *m_hexValue;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/ControlParameterItem.cpp b/src/gui/editors/segment/ControlParameterItem.cpp
new file mode 100644
index 0000000..beb0922
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterItem.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlParameterItem.h"
+
+#include <qlistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/ControlParameterItem.h b/src/gui/editors/segment/ControlParameterItem.h
new file mode 100644
index 0000000..6746ca2
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterItem.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLPARAMETERITEM_H_
+#define _RG_CONTROLPARAMETERITEM_H_
+
+#include <qstring.h>
+#include <klistview.h>
+
+
+namespace Rosegarden
+{
+
+
+class ControlParameterItem : public KListViewItem
+{
+public:
+ ControlParameterItem(int id,
+ QListView *parent,
+ QString str1,
+ QString str2,
+ QString str3,
+ QString str4,
+ QString str5,
+ QString str6,
+ QString str7,
+ QString str8,
+ QString str9):
+ KListViewItem(parent, str1, str2, str3, str4, str5, str6, str7, str8),
+ m_id(id) { setText(8, str9); }
+
+ int getId() const { return m_id; }
+
+protected:
+
+ int m_id;
+ QString m_string9;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/MarkerEditor.cpp b/src/gui/editors/segment/MarkerEditor.cpp
new file mode 100644
index 0000000..61caaa7
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditor.cpp
@@ -0,0 +1,594 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MarkerEditor.h"
+#include "MarkerEditorViewItem.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kstdaccel.h>
+#include <kconfig.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/Marker.h"
+#include "base/RealTime.h"
+#include "commands/edit/AddMarkerCommand.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "commands/edit/RemoveMarkerCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/MarkerModifyDialog.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+MarkerEditor::MarkerEditor(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KMainWindow(parent, "markereditordialog"),
+ m_doc(doc),
+ m_modified(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Markers"));
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn(i18n("Marker time "));
+ m_listView->addColumn(i18n("Marker text "));
+ m_listView->addColumn(i18n("Marker description "));
+
+ // Align centrally
+ for (int i = 0; i < 3; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ QGroupBox *posGroup = new QGroupBox(2, Horizontal,
+ i18n("Pointer position"), mainFrame);
+
+ new QLabel(i18n("Absolute time:"), posGroup);
+ m_absoluteTime = new QLabel(posGroup);
+
+ new QLabel(i18n("Real time:"), posGroup);
+ m_realTime = new QLabel(posGroup);
+
+ new QLabel(i18n("In measure:"), posGroup);
+ m_barTime = new QLabel(posGroup);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+ m_deleteAllButton = new QPushButton(i18n("Delete All"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Marker"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Marker"));
+
+ QToolTip::add
+ (m_deleteAllButton,
+ i18n("Delete All Markers"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Marker Editor"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addWidget(m_deleteAllButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ connect(m_closeButton, SIGNAL(released()),
+ SLOT(slotClose()));
+
+ connect(m_deleteAllButton, SIGNAL(released()),
+ SLOT(slotDeleteAll()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ connect(m_listView, SIGNAL(pressed(QListViewItem *)),
+ this, SLOT(slotItemClicked(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->setItemsRenameable(true);
+
+ initDialog();
+
+ setAutoSaveSettings(MarkerEditorConfigGroup, true);
+
+ m_accelerators = new QAccel(this);
+}
+
+void
+MarkerEditor::updatePosition()
+{
+ timeT pos = m_doc->getComposition().getPosition();
+ m_absoluteTime->setText(QString("%1").arg(pos));
+
+ RealTime rT = m_doc->getComposition().getElapsedRealTime(pos);
+ long hours = rT.sec / (60 * 60);
+ long mins = rT.sec / 60;
+ long secs = rT.sec;
+ long msecs = rT.msec();
+
+ QString realTime, secsStr;
+ if (hours)
+ realTime += QString("%1h ").arg(hours);
+ if (mins)
+ realTime += QString("%1m ").arg(mins);
+ secsStr.sprintf("%ld.%03lds", secs, msecs);
+ realTime += secsStr;
+
+ // only update if we need to to try and avoid flickering
+ if (m_realTime->text() != realTime)
+ m_realTime->setText(realTime);
+
+ QString barTime =
+ QString("%1").arg(m_doc->getComposition().getBarNumber(pos) + 1);
+
+ // again only update if needed
+ if (m_barTime->text() != barTime)
+ m_barTime->setText(barTime);
+
+ /*
+ // Don't allow us to add another marker if there's already one
+ // at the current position.
+ //
+ if (m_doc->getComposition().
+ isMarkerAtPosition(m_doc->getComposition().getPosition()))
+ m_addButton->setEnabled(false);
+ else
+ m_addButton->setEnabled(true);
+ */
+}
+
+MarkerEditor::~MarkerEditor()
+{
+ RG_DEBUG << "MarkerEditor::~MarkerEditor" << endl;
+
+ m_listView->saveLayout(kapp->config(), MarkerEditorConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+MarkerEditor::initDialog()
+{
+ RG_DEBUG << "MarkerEditor::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotUpdate()
+{
+ RG_DEBUG << "MarkerEditor::slotUpdate" << endl;
+
+ //QPtrList<QListViewItem> selection = m_listView->selectedItems();
+
+ MarkerEditorViewItem *item;
+
+ m_listView->clear();
+
+ Composition::markercontainer markers =
+ m_doc->getComposition().getMarkers();
+
+ Composition::markerconstiterator it;
+
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ for (it = markers.begin(); it != markers.end(); ++it) {
+ QString timeString = makeTimeString((*it)->getTime(), timeMode);
+
+ item = new
+ MarkerEditorViewItem(m_listView,
+ (*it)->getID(),
+ timeString,
+ strtoqstr((*it)->getName()),
+ strtoqstr((*it)->getDescription()));
+
+ // Set this for the MarkerEditor
+ //
+ item->setRawTime((*it)->getTime());
+
+ m_listView->insertItem(item);
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item =
+ new MarkerEditorViewItem(m_listView, 0, i18n("<none>"));
+ ((MarkerEditorViewItem *)item)->setFake(true);
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+
+ updatePosition();
+
+}
+
+void
+MarkerEditor::slotDeleteAll()
+{
+ RG_DEBUG << "MarkerEditor::slotDeleteAll" << endl;
+ KMacroCommand *command = new KMacroCommand(i18n("Remove all markers"));
+
+ QListViewItem *item = m_listView->firstChild();
+
+ do {
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+ if (!ei || ei->isFake())
+ continue;
+
+ RemoveMarkerCommand *rc =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ ei->getID(),
+ ei->getRawTime(),
+ qstrtostr(item->text(1)),
+ qstrtostr(item->text(2)));
+ command->addCommand(rc);
+ } while ((item = item->nextSibling()));
+
+ addCommandToHistory(command);
+}
+
+void
+MarkerEditor::slotAdd()
+{
+ RG_DEBUG << "MarkerEditor::slotAdd" << endl;
+
+ AddMarkerCommand *command =
+ new AddMarkerCommand(&m_doc->getComposition(),
+ m_doc->getComposition().getPosition(),
+ std::string("new marker"),
+ std::string("no description"));
+
+ addCommandToHistory(command);
+}
+
+void
+MarkerEditor::slotDelete()
+{
+ RG_DEBUG << "MarkerEditor::slotDelete" << endl;
+ QListViewItem *item = m_listView->currentItem();
+
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+
+ if (!ei || ei->isFake())
+ return ;
+
+ RemoveMarkerCommand *command =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ ei->getID(),
+ ei->getRawTime(),
+ qstrtostr(item->text(1)),
+ qstrtostr(item->text(2)));
+
+ addCommandToHistory(command);
+
+}
+
+void
+MarkerEditor::slotClose()
+{
+ RG_DEBUG << "MarkerEditor::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+MarkerEditor::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/time-musical.png");
+ QIconSet icon(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ createGUI("markereditor.rc");
+}
+
+void
+MarkerEditor::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+MarkerEditor::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+MarkerEditor::setModified(bool modified)
+{
+ RG_DEBUG << "MarkerEditor::setModified(" << modified << ")" << endl;
+
+ if (modified) {}
+ else {}
+
+ m_modified = modified;
+}
+
+void
+MarkerEditor::checkModified()
+{
+ RG_DEBUG << "MarkerEditor::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+MarkerEditor::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "MarkerEditor::slotEdit" << endl;
+
+ if (m_listView->selectionMode() == QListView::NoSelection) {
+ // The marker list is empty, so we shouldn't allow editing the
+ // <none> placeholder
+ return ;
+ }
+
+ // Need to get the raw time from the ListViewItem
+ //
+ MarkerEditorViewItem *item =
+ dynamic_cast<MarkerEditorViewItem*>(i);
+
+ if (!item || item->isFake())
+ return ;
+
+ MarkerModifyDialog dialog(this,
+ &m_doc->getComposition(),
+ item->getRawTime(),
+ item->text(1),
+ item->text(2));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyMarkerCommand *command =
+ new ModifyMarkerCommand(&m_doc->getComposition(),
+ item->getID(),
+ dialog.getOriginalTime(),
+ dialog.getTime(),
+ qstrtostr(dialog.getName()),
+ qstrtostr(dialog.getDescription()));
+
+ addCommandToHistory(command);
+ }
+
+
+}
+
+void
+MarkerEditor::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+MarkerEditor::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_modified = false;
+
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotItemClicked(QListViewItem *item)
+{
+ RG_DEBUG << "MarkerEditor::slotItemClicked" << endl;
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+
+ if (ei && !ei->isFake()) {
+ RG_DEBUG << "MarkerEditor::slotItemClicked - "
+ << "jump to marker at " << ei->getRawTime() << endl;
+
+ emit jumpToMarker(timeT(ei->getRawTime()));
+ }
+}
+
+QString
+MarkerEditor::makeTimeString(timeT time, int timeMode)
+{
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ m_doc->getComposition().getMusicalTimeForAbsoluteTime
+ (time, bar, beat, fraction, remainder);
+ ++bar;
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ m_doc->getComposition().getElapsedRealTime(time);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(time);
+ }
+}
+
+void
+MarkerEditor::slotMusicalTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 0);
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotRealTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 1);
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotRawTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 2);
+ slotUpdate();
+}
+
+}
+#include "MarkerEditor.moc"
diff --git a/src/gui/editors/segment/MarkerEditor.h b/src/gui/editors/segment/MarkerEditor.h
new file mode 100644
index 0000000..d3c9ac7
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditor.h
@@ -0,0 +1,124 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MARKEREDITOR_H_
+#define _RG_MARKEREDITOR_H_
+
+#include <kmainwindow.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QLabel;
+class QCloseEvent;
+class QAccel;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class MarkerEditor : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ MarkerEditor(QWidget *parent,
+ RosegardenGUIDoc *doc);
+ ~MarkerEditor();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ // update pointer position
+ void updatePosition();
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+public slots:
+ void slotUpdate();
+
+ void slotAdd();
+ void slotDelete();
+ void slotDeleteAll();
+ void slotClose();
+ void slotEdit(QListViewItem *);
+ void slotItemClicked(QListViewItem *);
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+signals:
+ void closing();
+ void jumpToMarker(timeT);
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+ QString makeTimeString(timeT time, int timeMode);
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc *m_doc;
+
+ QLabel *m_absoluteTime;
+ QLabel *m_realTime;
+ QLabel *m_barTime;
+
+ QPushButton *m_closeButton;
+
+
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+ QPushButton *m_deleteAllButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ QAccel *m_accelerators;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/MarkerEditorViewItem.cpp b/src/gui/editors/segment/MarkerEditorViewItem.cpp
new file mode 100644
index 0000000..9ff2bda
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditorViewItem.cpp
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "MarkerEditorViewItem.h"
+
+namespace Rosegarden {
+
+int
+MarkerEditorViewItem::compare(QListViewItem * i, int col, bool ascending) const
+{
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(i);
+
+ if (!ei) return KListViewItem::compare(i, col, ascending);
+
+ // Raw time sorting on time column
+ //
+ if (col == 0) {
+
+ if (m_rawTime < ei->getRawTime()) return -1;
+ else if (ei->getRawTime() < m_rawTime) return 1;
+ else return 0;
+
+ } else {
+ return KListViewItem::compare(i, col, ascending);
+ }
+}
+
+}
+
diff --git a/src/gui/editors/segment/MarkerEditorViewItem.h b/src/gui/editors/segment/MarkerEditorViewItem.h
new file mode 100644
index 0000000..010d227
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditorViewItem.h
@@ -0,0 +1,70 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MARKEREDITORVIEWITEM_H_
+#define _RG_MARKEREDITORVIEWITEM_H_
+
+#include <klistview.h>
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+
+class MarkerEditorViewItem : public KListViewItem
+{
+public:
+ MarkerEditorViewItem(QListView * parent, int id,
+ QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null):
+ KListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8),
+ m_rawTime(0), m_fake(false), m_id(id) { ; }
+
+ virtual int compare(QListViewItem * i, int col, bool ascending) const;
+
+ void setRawTime(Rosegarden::timeT rawTime) { m_rawTime = rawTime; }
+ Rosegarden::timeT getRawTime() const { return m_rawTime; }
+
+ void setFake(bool fake) { m_fake = true; }
+ bool isFake() const { return m_fake; }
+
+ int getID() const { return m_id; }
+
+protected:
+ Rosegarden::timeT m_rawTime;
+ bool m_fake;
+ int m_id;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayList.cpp b/src/gui/editors/segment/PlayList.cpp
new file mode 100644
index 0000000..bfc795c
--- /dev/null
+++ b/src/gui/editors/segment/PlayList.cpp
@@ -0,0 +1,254 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PlayList.h"
+#include "PlayListView.h"
+#include "PlayListViewItem.h"
+#include "document/ConfigGroups.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kurl.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qdragobject.h>
+
+
+namespace Rosegarden
+{
+
+PlayList::PlayList(QWidget *parent, const char *name)
+ : QVBox(parent, name),
+ m_listView(new PlayListView(this)),
+ m_buttonBar(new QFrame(this)),
+ m_barLayout(new QHBoxLayout(m_buttonBar)),
+ m_playButton(0),
+ m_moveUpButton(0),
+ m_moveDownButton(0),
+ m_deleteButton(0),
+ m_clearButton(0)
+{
+ m_openButton = new QPushButton(m_buttonBar);
+ m_playButton = new QPushButton(m_buttonBar);
+ m_moveUpButton = new QPushButton(m_buttonBar);
+ m_moveDownButton = new QPushButton(m_buttonBar);
+ m_deleteButton = new QPushButton(m_buttonBar);
+ m_clearButton = new QPushButton(m_buttonBar);
+ m_barLayout->addWidget(m_openButton);
+ m_barLayout->addWidget(m_playButton);
+ m_barLayout->addWidget(m_moveUpButton);
+ m_barLayout->addWidget(m_moveDownButton);
+ m_barLayout->addWidget(m_deleteButton);
+ m_barLayout->addWidget(m_clearButton);
+ m_barLayout->addStretch();
+
+
+ m_openButton ->setText(i18n("Add..."));
+ m_playButton ->setText(i18n("Play"));
+ m_moveUpButton ->setText(i18n("Move Up"));
+ m_moveDownButton->setText(i18n("Move Down"));
+ m_deleteButton ->setText(i18n("Delete"));
+ m_clearButton ->setText(i18n("Clear"));
+
+ connect(m_openButton, SIGNAL(clicked()),
+ SLOT(slotOpenFiles()));
+
+ connect(m_playButton, SIGNAL(clicked()),
+ SLOT(slotPlay()));
+
+ connect(m_deleteButton, SIGNAL(clicked()),
+ SLOT(slotDeleteCurrent()));
+
+ connect(m_clearButton, SIGNAL(clicked()),
+ SLOT(slotClear()));
+
+ connect(m_moveUpButton, SIGNAL(clicked()),
+ SLOT(slotMoveUp()));
+
+ connect(m_moveDownButton, SIGNAL(clicked()),
+ SLOT(slotMoveDown()));
+
+ connect(m_listView, SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(slotCurrentItemChanged(QListViewItem*)));
+
+ connect(m_listView, SIGNAL(dropped(QDropEvent*, QListViewItem*)),
+ SLOT(slotDropped(QDropEvent*, QListViewItem*)));
+
+ restore();
+
+ enableButtons(0);
+
+}
+
+PlayList::~PlayList()
+{
+ save();
+}
+
+void PlayList::slotOpenFiles()
+{
+ KURL::List kurlList =
+ KFileDialog::getOpenURLs(":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21",
+ this,
+ i18n("Select one or more Rosegarden files"));
+
+ KURL::List::iterator it;
+
+ for (it = kurlList.begin(); it != kurlList.end(); ++it) {
+ new PlayListViewItem(m_listView, *it);
+ }
+
+ enableButtons(m_listView->currentItem());
+}
+
+void
+PlayList::slotDropped(QDropEvent *event, QListViewItem* after)
+{
+ QStrList uri;
+
+ // see if we can decode a URI.. if not, just ignore it
+ if (QUriDrag::decode(event, uri)) {
+
+ // okay, we have a URI.. process it
+ // weed out non-rg files
+ //
+ for (QString url = uri.first(); url; url = uri.next()) {
+ if (url.right(3).lower() == ".rg")
+ new PlayListViewItem(m_listView, after, KURL(url));
+
+ }
+ }
+
+ enableButtons(m_listView->currentItem());
+}
+
+void PlayList::slotPlay()
+{
+ PlayListViewItem *currentItem = dynamic_cast<PlayListViewItem*>(m_listView->currentItem());
+
+ if (currentItem)
+ emit play(currentItem->getURL().url());
+}
+
+void PlayList::slotMoveUp()
+{
+ QListViewItem *currentItem = m_listView->currentItem();
+ QListViewItem *previousItem = m_listView->previousSibling(currentItem);
+
+ if (previousItem)
+ previousItem->moveItem(currentItem);
+
+ enableButtons(currentItem);
+}
+
+void PlayList::slotMoveDown()
+{
+ QListViewItem *currentItem = m_listView->currentItem();
+ QListViewItem *nextItem = currentItem->nextSibling();
+
+ if (nextItem)
+ currentItem->moveItem(nextItem);
+
+ enableButtons(currentItem);
+}
+
+void PlayList::slotClear()
+{
+ m_listView->clear();
+ enableButtons(0);
+}
+
+void PlayList::slotDeleteCurrent()
+{
+ QListViewItem* currentItem = m_listView->currentItem();
+ if (currentItem)
+ delete currentItem;
+}
+
+void PlayList::slotCurrentItemChanged(QListViewItem* currentItem)
+{
+ enableButtons(currentItem);
+}
+
+void PlayList::enableButtons(QListViewItem* currentItem)
+{
+ bool enable = (currentItem != 0);
+
+ m_playButton->setEnabled(enable);
+ m_deleteButton->setEnabled(enable);
+
+ if (currentItem) {
+ m_moveUpButton->setEnabled(currentItem != m_listView->firstChild());
+ m_moveDownButton->setEnabled(currentItem != m_listView->lastItem());
+ } else {
+ m_moveUpButton->setEnabled(false);
+ m_moveDownButton->setEnabled(false);
+ }
+
+ m_clearButton->setEnabled(m_listView->childCount() > 0);
+}
+
+void PlayList::save()
+{
+ QStringList urlList;
+ PlayListViewItem* item = dynamic_cast<PlayListViewItem*>(getListView()->firstChild());
+
+ while (item) {
+ urlList << item->getURL().url();
+ item = dynamic_cast<PlayListViewItem*>(item->nextSibling());
+ }
+
+ KConfig *kc = KGlobal::config();
+ KConfigGroupSaver cs(kc, PlayListConfigGroup);
+ kc->writeEntry("Playlist Files", urlList);
+
+ getListView()->saveLayout(kc, PlayListConfigGroup);
+}
+
+void PlayList::restore()
+{
+ KConfig *kc = KGlobal::config();
+ getListView()->restoreLayout(kc, PlayListConfigGroup);
+
+ KConfigGroupSaver cs(kc, PlayListConfigGroup);
+ QStringList urlList = kc->readListEntry("Playlist Files");
+
+ for (QStringList::Iterator it = urlList.begin();
+ it != urlList.end(); ++it) {
+ new PlayListViewItem(getListView(), KURL(*it));
+ }
+}
+
+}
+#include "PlayList.moc"
diff --git a/src/gui/editors/segment/PlayList.h b/src/gui/editors/segment/PlayList.h
new file mode 100644
index 0000000..8e40c8c
--- /dev/null
+++ b/src/gui/editors/segment/PlayList.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLIST_H_
+#define _RG_PLAYLIST_H_
+
+#include <qvbox.h>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QHBoxLayout;
+class QFrame;
+class QDropEvent;
+
+
+namespace Rosegarden
+{
+
+class PlayListView;
+
+
+class PlayList : public QVBox
+{
+ Q_OBJECT
+
+public:
+ PlayList(QWidget *parent = 0, const char *name = 0);
+ ~PlayList();
+
+ PlayListView* getListView() { return m_listView; }
+
+ void enableButtons(QListViewItem*);
+
+
+signals:
+ void play(QString);
+
+protected slots:
+ void slotOpenFiles();
+ void slotPlay();
+ void slotMoveUp();
+ void slotMoveDown();
+ void slotDeleteCurrent();
+ void slotClear();
+ void slotCurrentItemChanged(QListViewItem*);
+ void slotDropped(QDropEvent*, QListViewItem*);
+
+protected:
+ void save();
+ void restore();
+
+ //--------------- Data members ---------------------------------
+ PlayListView* m_listView;
+ QFrame* m_buttonBar;
+ QHBoxLayout* m_barLayout;
+
+ QPushButton* m_openButton;
+ QPushButton* m_playButton;
+ QPushButton* m_moveUpButton;
+ QPushButton* m_moveDownButton;
+ QPushButton* m_deleteButton;
+ QPushButton* m_clearButton;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayListDialog.cpp b/src/gui/editors/segment/PlayListDialog.cpp
new file mode 100644
index 0000000..7aa03a5
--- /dev/null
+++ b/src/gui/editors/segment/PlayListDialog.cpp
@@ -0,0 +1,76 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PlayListDialog.h"
+
+#include "document/ConfigGroups.h"
+#include "PlayList.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PlayListDialog::PlayListDialog(QString caption,
+ QWidget* parent, const char* name)
+ : KDialogBase(parent, name, false, caption,
+ KDialogBase::Close, // standard buttons
+ KDialogBase::Close, // default button
+ true),
+ m_playList(new PlayList(this))
+{
+ setWFlags(WDestructiveClose);
+ setMainWidget(m_playList);
+ restore();
+}
+
+void PlayListDialog::save()
+{
+ saveDialogSize(PlayListConfigGroup);
+}
+
+void PlayListDialog::restore()
+{
+ setInitialSize(configDialogSize(PlayListConfigGroup));
+}
+
+void PlayListDialog::closeEvent(QCloseEvent *e)
+{
+ save();
+ emit closing();
+ KDialogBase::closeEvent(e);
+}
+
+void PlayListDialog::slotClose()
+{
+ save();
+ emit closing();
+ KDialogBase::slotClose();
+}
+
+}
+#include "PlayListDialog.moc"
diff --git a/src/gui/editors/segment/PlayListDialog.h b/src/gui/editors/segment/PlayListDialog.h
new file mode 100644
index 0000000..51db8ca
--- /dev/null
+++ b/src/gui/editors/segment/PlayListDialog.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTDIALOG_H_
+#define _RG_PLAYLISTDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QCloseEvent;
+
+
+namespace Rosegarden
+{
+
+class PlayList;
+
+
+class PlayListDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ PlayListDialog(QString caption, QWidget* parent = 0, const char* name = 0);
+
+ PlayList* getPlayList() { return m_playList; }
+
+public slots:
+ void slotClose();
+
+signals:
+ void closing();
+
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+
+ void save();
+ void restore();
+
+ //--------------- Data members ---------------------------------
+ PlayList* m_playList;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayListView.cpp b/src/gui/editors/segment/PlayListView.cpp
new file mode 100644
index 0000000..8c17076
--- /dev/null
+++ b/src/gui/editors/segment/PlayListView.cpp
@@ -0,0 +1,66 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PlayListView.h"
+
+#include <klocale.h>
+#include <qdragobject.h>
+
+namespace Rosegarden {
+
+PlayListView::PlayListView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ addColumn(i18n("Title"));
+ addColumn(i18n("File name"));
+
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropVisualizer(true);
+
+ setShowToolTips(true);
+ setShowSortIndicator(true);
+ setAllColumnsShowFocus(true);
+ setItemsMovable(true);
+ setSorting(-1);
+}
+
+bool PlayListView::acceptDrag(QDropEvent* e) const
+{
+ return QUriDrag::canDecode(e) || KListView::acceptDrag(e);
+}
+
+
+QListViewItem* PlayListView::previousSibling(QListViewItem* item)
+{
+ QListViewItem* prevSib = firstChild();
+
+ while(prevSib && prevSib->nextSibling() != item)
+ prevSib = prevSib->nextSibling();
+
+ return prevSib;
+}
+
+}
+
diff --git a/src/gui/editors/segment/PlayListView.h b/src/gui/editors/segment/PlayListView.h
new file mode 100644
index 0000000..a18b8e8
--- /dev/null
+++ b/src/gui/editors/segment/PlayListView.h
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTVIEW_H_
+#define _RG_PLAYLISTVIEW_H_
+
+#include <klistview.h>
+
+namespace Rosegarden {
+
+class PlayListView : public KListView
+{
+public:
+ PlayListView(QWidget *parent=0, const char *name=0);
+
+ QListViewItem* previousSibling(QListViewItem*);
+
+protected:
+// virtual void dragEnterEvent(QDragEnterEvent *event);
+// virtual void dropEvent(QDropEvent*);
+
+// virtual void dragEnterEvent(QDragEnterEvent*);
+ virtual bool acceptDrag(QDropEvent*) const;
+
+
+};
+
+}
+
+#endif
+
diff --git a/src/gui/editors/segment/PlayListViewItem.cpp b/src/gui/editors/segment/PlayListViewItem.cpp
new file mode 100644
index 0000000..df04a2e
--- /dev/null
+++ b/src/gui/editors/segment/PlayListViewItem.cpp
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PlayListViewItem.h"
+
+namespace Rosegarden {
+
+PlayListViewItem::PlayListViewItem(KListView* parent, KURL kurl)
+ : KListViewItem(parent, kurl.fileName(), kurl.prettyURL()),
+ m_kurl(kurl)
+{
+}
+
+PlayListViewItem::PlayListViewItem(KListView* parent, QListViewItem* after, KURL kurl)
+ : KListViewItem(parent, after, kurl.fileName(), kurl.prettyURL()),
+ m_kurl(kurl)
+{
+}
+
+}
+
diff --git a/src/gui/editors/segment/PlayListViewItem.h b/src/gui/editors/segment/PlayListViewItem.h
new file mode 100644
index 0000000..b88de0f
--- /dev/null
+++ b/src/gui/editors/segment/PlayListViewItem.h
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTVIEWITEM_H_
+#define _RG_PLAYLISTVIEWITEM_H_
+
+#include <klistview.h>
+#include <kurl.h>
+
+namespace Rosegarden {
+
+class PlayListViewItem : public KListViewItem
+{
+public:
+ PlayListViewItem(KListView* parent, KURL);
+ PlayListViewItem(KListView* parent, QListViewItem*, KURL);
+
+ const KURL& getURL() { return m_kurl; }
+
+protected:
+ KURL m_kurl;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackButtons.cpp b/src/gui/editors/segment/TrackButtons.cpp
new file mode 100644
index 0000000..5cf9908
--- /dev/null
+++ b/src/gui/editors/segment/TrackButtons.cpp
@@ -0,0 +1,1149 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackButtons.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/segment/RenameTrackCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/kdeext/KLedButton.h"
+#include "sound/AudioFileManager.h"
+#include "sound/PluginIdentifier.h"
+#include "TrackLabel.h"
+#include "TrackVUMeter.h"
+#include <kglobal.h>
+#include <kled.h>
+#include <kmessagebox.h>
+#include <qcursor.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpopupmenu.h>
+#include <qsignalmapper.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <qtooltip.h>
+
+namespace Rosegarden
+{
+
+TrackButtons::TrackButtons(RosegardenGUIDoc* doc,
+ unsigned int trackCellHeight,
+ unsigned int trackLabelWidth,
+ bool showTrackLabels,
+ int overallHeight,
+ QWidget* parent,
+ const char* name,
+ WFlags f)
+ : QFrame(parent, name, f),
+ m_doc(doc),
+ m_layout(new QVBoxLayout(this)),
+ m_recordSigMapper(new QSignalMapper(this)),
+ m_muteSigMapper(new QSignalMapper(this)),
+ m_clickedSigMapper(new QSignalMapper(this)),
+ m_instListSigMapper(new QSignalMapper(this)),
+ m_tracks(doc->getComposition().getNbTracks()),
+ m_offset(4),
+ m_cellSize(trackCellHeight),
+ m_borderGap(1),
+ m_trackLabelWidth(trackLabelWidth),
+ m_popupItem(0),
+ m_lastSelected( -1)
+{
+ setFrameStyle(Plain);
+
+ // when we create the widget, what are we looking at?
+ if (showTrackLabels)
+ m_trackInstrumentLabels = TrackLabel::ShowTrack;
+ else
+ m_trackInstrumentLabels = TrackLabel::ShowInstrument;
+
+ // Set the spacing between vertical elements
+ //
+ m_layout->setSpacing(m_borderGap);
+
+ // Now draw the buttons and labels and meters
+ //
+ makeButtons();
+
+ m_layout->addStretch(20);
+
+ connect(m_recordSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotToggleRecordTrack(int)));
+
+ connect(m_muteSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotToggleMutedTrack(int)));
+
+ // connect signal mappers
+ connect(m_instListSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotInstrumentSelection(int)));
+
+ connect(m_clickedSigMapper, SIGNAL(mapped(int)),
+ this, SIGNAL(trackSelected(int)));
+
+ // // Populate instrument popup menu just once at start-up
+ // //
+ // populateInstrumentPopup();
+
+ // We have to force the height for the moment
+ //
+ setMinimumHeight(overallHeight);
+
+}
+
+TrackButtons::~TrackButtons()
+{}
+
+void
+TrackButtons::makeButtons()
+{
+ if (!m_doc)
+ return ;
+
+ // Create a horizontal box for each track
+ // plus the two buttons
+ //
+ unsigned int nbTracks = m_doc->getComposition().getNbTracks();
+
+ for (unsigned int i = 0; i < nbTracks; ++i) {
+ Track *track = m_doc->getComposition().getTrackByPosition(i);
+
+ if (track) {
+ QFrame *trackHBox = makeButton(track->getId());
+
+ if (trackHBox) {
+ m_layout->addWidget(trackHBox);
+ m_trackHBoxes.push_back(trackHBox);
+ }
+ }
+ }
+
+ populateButtons();
+}
+
+void TrackButtons::setButtonMapping(QObject* obj, TrackId trackId)
+{
+ m_clickedSigMapper->setMapping(obj, trackId);
+ m_instListSigMapper->setMapping(obj, trackId);
+}
+
+void
+TrackButtons::populateButtons()
+{
+ Instrument *ins = 0;
+ Track *track;
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ track = m_doc->getComposition().getTrackByPosition(i);
+
+ if (track) {
+ ins = m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ // Set mute button from track
+ //
+ if (track->isMuted())
+ m_muteLeds[i]->off();
+ else
+ m_muteLeds[i]->on();
+
+ // Set record button from track
+ //
+ bool recording =
+ m_doc->getComposition().isTrackRecording(track->getId());
+ setRecordTrack(track->getPosition(), recording);
+
+ // reset track tokens
+ m_trackLabels[i]->setId(track->getId());
+ setButtonMapping(m_trackLabels[i], track->getId());
+ m_trackLabels[i]->setPosition(i);
+ }
+
+ if (ins) {
+ m_trackLabels[i]->getInstrumentLabel()->setText
+ (strtoqstr(ins->getPresentationName()));
+ if (ins->sendsProgramChange()) {
+ m_trackLabels[i]->setAlternativeLabel(strtoqstr(ins->getProgramName()));
+ }
+
+ } else {
+ m_trackLabels[i]->getInstrumentLabel()->setText(i18n("<no instrument>"));
+ }
+
+ m_trackLabels[i]->update();
+ }
+
+}
+
+std::vector<int>
+TrackButtons::mutedTracks()
+{
+ std::vector<int> mutedTracks;
+
+ for (TrackId i = 0; i < m_tracks; i++) {
+ if (m_muteLeds[i]->state() == KLed::Off)
+ mutedTracks.push_back(i);
+ }
+
+ return mutedTracks;
+}
+
+void
+TrackButtons::slotToggleMutedTrack(int mutedTrackPos)
+{
+ RG_DEBUG << "TrackButtons::slotToggleMutedTrack(" << mutedTrackPos << ")\n";
+
+ if (mutedTrackPos < 0 || mutedTrackPos > (int)m_tracks )
+ return ;
+
+ Track *track =
+ m_doc->getComposition().getTrackByPosition(mutedTrackPos);
+
+ emit muteButton(track->getId(), !track->isMuted()); // will set the value
+}
+
+void
+TrackButtons::removeButtons(unsigned int position)
+{
+ RG_DEBUG << "TrackButtons::removeButtons - "
+ << "deleting track button at position "
+ << position << endl;
+
+ if (position >= m_trackHBoxes.size()) {
+ RG_DEBUG << "%%%%%%%%% BIG PROBLEM : TrackButtons::removeButtons() was passed a non-existing index\n";
+ return ;
+ }
+
+ std::vector<TrackLabel*>::iterator tit = m_trackLabels.begin();
+ tit += position;
+ m_trackLabels.erase(tit);
+
+ std::vector<TrackVUMeter*>::iterator vit = m_trackMeters.begin();
+ vit += position;
+ m_trackMeters.erase(vit);
+
+ std::vector<KLedButton*>::iterator mit = m_muteLeds.begin();
+ mit += position;
+ m_muteLeds.erase(mit);
+
+ mit = m_recordLeds.begin();
+ mit += position;
+ m_recordLeds.erase(mit);
+
+ delete m_trackHBoxes[position]; // deletes all child widgets (button, led, label...)
+
+ std::vector<QFrame*>::iterator it = m_trackHBoxes.begin();
+ it += position;
+ m_trackHBoxes.erase(it);
+
+}
+
+void
+TrackButtons::slotUpdateTracks()
+{
+ Composition &comp = m_doc->getComposition();
+ unsigned int newNbTracks = comp.getNbTracks();
+ Track *track = 0;
+
+ std::cerr << "TrackButtons::slotUpdateTracks" << std::endl;
+
+ if (newNbTracks < m_tracks) {
+ for (unsigned int i = m_tracks; i > newNbTracks; --i)
+ removeButtons(i - 1);
+ } else if (newNbTracks > m_tracks) {
+ for (unsigned int i = m_tracks; i < newNbTracks; ++i) {
+ track = m_doc->getComposition().getTrackByPosition(i);
+ if (track) {
+ QFrame *trackHBox = makeButton(track->getId());
+
+ if (trackHBox) {
+ trackHBox->show();
+ m_layout->insertWidget(i, trackHBox);
+ m_trackHBoxes.push_back(trackHBox);
+ }
+ } else
+ RG_DEBUG << "TrackButtons::slotUpdateTracks - can't find TrackId for position " << i << endl;
+ }
+ }
+
+ // Set height
+ //
+ for (unsigned int i = 0; i < m_trackHBoxes.size(); ++i) {
+
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(track->getId());
+ if (multiple == 0) multiple = 1;
+
+ // nasty dupe from makeButton
+
+ int buttonGap = 8;
+ int vuWidth = 20;
+ int vuSpacing = 2;
+
+ int labelWidth = m_trackLabelWidth -
+ ((m_cellSize - buttonGap) * 2 +
+ vuSpacing * 2 + vuWidth);
+
+ m_trackHBoxes[i]->setMinimumSize
+ (labelWidth, m_cellSize * multiple - m_borderGap);
+
+ m_trackHBoxes[i]->setFixedHeight
+ (m_cellSize * multiple - m_borderGap);
+ }
+ }
+
+ // Renumber all the labels
+ //
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+ m_trackLabels[i]->setId(track->getId());
+
+ QLabel *trackLabel = m_trackLabels[i]->getTrackLabel();
+
+ if (track->getLabel() == std::string("")) {
+ Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+ if (ins && ins->getType() == Instrument::Audio) {
+ trackLabel->setText(i18n("<untitled audio>"));
+ } else {
+ trackLabel->setText(i18n("<untitled>"));
+ }
+ } else {
+ trackLabel->setText(strtoqstr(track->getLabel()));
+ }
+
+ // RG_DEBUG << "TrackButtons::slotUpdateTracks - set button mapping at pos "
+ // << i << " to track id " << track->getId() << endl;
+ setButtonMapping(m_trackLabels[i], track->getId());
+ }
+ }
+ m_tracks = newNbTracks;
+
+ // Set record status and colour
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+
+ setRecordTrack(i, comp.isTrackRecording(track->getId()));
+
+ Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ if (ins &&
+ ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+
+ // repopulate the buttons
+ populateButtons();
+}
+
+void
+TrackButtons::slotToggleRecordTrack(int position)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackByPosition(position);
+
+ bool state = !comp.isTrackRecording(track->getId());
+
+ Instrument *instrument = m_doc->getStudio().getInstrumentById
+ (track->getInstrument());
+
+ bool audio = (instrument &&
+ instrument->getType() == Instrument::Audio);
+
+ if (audio && state) {
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException e) {
+ if (KMessageBox::warningContinueCancel
+ (this,
+ i18n("The audio file path does not exist or is not writable.\nPlease set the audio file path to a valid directory in Document Properties before recording audio.\nWould you like to set it now?"),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ RosegardenGUIApp::self()->slotOpenAudioPathSettings();
+ }
+ }
+ }
+
+ // can have any number of audio instruments armed, but only one
+ // track armed per instrument.
+
+ // Need to copy this container, as we're implicitly modifying it
+ // through calls to comp.setTrackRecording
+
+ Composition::recordtrackcontainer oldRecordTracks =
+ comp.getRecordTracks();
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ oldRecordTracks.begin();
+ i != oldRecordTracks.end(); ++i) {
+
+ if (!comp.isTrackRecording(*i)) {
+ // We've already reset this one
+ continue;
+ }
+
+ Track *otherTrack = comp.getTrackById(*i);
+
+ if (otherTrack &&
+ otherTrack != track) {
+
+ /* Obsolete code: audio, MIDI and plugin tracks behave the same now.
+ plcl, 06/2006 - Multitrack MIDI recording
+
+ bool unselect;
+
+ if (audio) {
+ unselect = (otherTrack->getInstrument() == track->getInstrument());
+ } else {
+ // our track is not an audio track, check that the
+ // other isn't either
+ Instrument *otherInstrument =
+ m_doc->getStudio().getInstrumentById(otherTrack->getInstrument());
+ bool otherAudio = (otherInstrument &&
+ otherInstrument->getType() ==
+ Instrument::Audio);
+
+ unselect = !otherAudio;
+ }
+
+ if (unselect) { */
+
+ if (otherTrack->getInstrument() == track->getInstrument()) {
+ // found another record track of the same type (and
+ // with the same instrument, if audio): unselect that
+
+ //!!! should we tell the user, particularly for the
+ //audio case? might seem odd otherwise
+
+ int otherPos = otherTrack->getPosition();
+ setRecordTrack(otherPos, false);
+ }
+ }
+ }
+
+ setRecordTrack(position, state);
+
+ emit recordButton(track->getId(), state);
+}
+
+void
+TrackButtons::setRecordTrack(int position, bool state)
+{
+ setRecordButton(position, state);
+ m_doc->getComposition().setTrackRecording
+ (m_trackLabels[position]->getId(), state);
+}
+
+void
+TrackButtons::setRecordButton(int position, bool state)
+{
+ if (position < 0 || position >= (int)m_tracks)
+ return ;
+
+ KLedButton* led = m_recordLeds[position];
+
+ led->setState(state ? KLed::On : KLed::Off);
+}
+
+void
+TrackButtons::selectLabel(int position)
+{
+ if (m_lastSelected >= 0 && m_lastSelected < (int)m_trackLabels.size()) {
+ m_trackLabels[m_lastSelected]->setSelected(false);
+ }
+
+ if (position >= 0 && position < (int)m_trackLabels.size()) {
+ m_trackLabels[position]->setSelected(true);
+ m_lastSelected = position;
+ }
+}
+
+std::vector<int>
+TrackButtons::getHighlightedTracks()
+{
+ std::vector<int> retList;
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ if (m_trackLabels[i]->isSelected())
+ retList.push_back(i);
+ }
+
+ return retList;
+}
+
+void
+TrackButtons::slotRenameTrack(QString newName, TrackId trackId)
+{
+ m_doc->getCommandHistory()->addCommand
+ (new RenameTrackCommand(&m_doc->getComposition(),
+ trackId,
+ qstrtostr(newName)));
+
+ changeTrackLabel(trackId, newName);
+}
+
+void
+TrackButtons::slotSetTrackMeter(float value, int position)
+{
+ //Composition &comp = m_doc->getComposition();
+ //Studio &studio = m_doc->getStudio();
+ //Track *track;
+
+ for (unsigned int i = 0; i < m_trackMeters.size(); ++i) {
+ if (i == ((unsigned int)position)) {
+ m_trackMeters[i]->setLevel(value);
+ return ;
+ }
+ }
+}
+
+void
+TrackButtons::slotSetMetersByInstrument(float value,
+ InstrumentId id)
+{
+ Composition &comp = m_doc->getComposition();
+ //Studio &studio = m_doc->getStudio();
+ Track *track;
+
+ for (unsigned int i = 0; i < m_trackMeters.size(); ++i) {
+ track = comp.getTrackByPosition(i);
+
+ if (track != 0 && track->getInstrument() == id) {
+ m_trackMeters[i]->setLevel(value);
+ }
+ }
+}
+
+void
+TrackButtons::slotInstrumentSelection(int trackId)
+{
+ RG_DEBUG << "TrackButtons::slotInstrumentSelection(" << trackId << ")\n";
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ int position = comp.getTrackById(trackId)->getPosition();
+
+ QString instrumentName = i18n("<no instrument>");
+ Track *track = comp.getTrackByPosition(position);
+
+ Instrument *instrument = 0;
+ if (track != 0) {
+ instrument = studio.getInstrumentById(track->getInstrument());
+ if (instrument)
+ instrumentName = strtoqstr(instrument->getPresentationName());
+ }
+
+ //
+ // populate this instrument widget
+ m_trackLabels[position]->getInstrumentLabel()->setText(instrumentName);
+
+ // Ensure the instrument name is shown
+ m_trackLabels[position]->showLabel(TrackLabel::ShowInstrument);
+
+ // Yes, well as we might've changed the Device name in the
+ // Device/Bank dialog then we reload the whole menu here.
+ //
+
+ QPopupMenu instrumentPopup(this);
+
+ populateInstrumentPopup(instrument, &instrumentPopup);
+
+ // Store the popup item position
+ //
+ m_popupItem = position;
+
+ instrumentPopup.exec(QCursor::pos());
+
+ // Restore the label back to what it was showing
+ m_trackLabels[position]->showLabel(m_trackInstrumentLabels);
+
+ // Do this here as well as in slotInstrumentPopupActivated, so as
+ // to restore the correct alternative label even if no other
+ // program was selected from the menu
+ if (track != 0) {
+ instrument = studio.getInstrumentById(track->getInstrument());
+ if (instrument) {
+ m_trackLabels[position]->getInstrumentLabel()->
+ setText(strtoqstr(instrument->getPresentationName()));
+ m_trackLabels[position]->clearAlternativeLabel();
+ if (instrument->sendsProgramChange()) {
+ m_trackLabels[position]->setAlternativeLabel
+ (strtoqstr(instrument->getProgramName()));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::populateInstrumentPopup(Instrument *thisTrackInstr, QPopupMenu* instrumentPopup)
+{
+ static QPixmap connectedPixmap, unconnectedPixmap,
+ connectedUsedPixmap, unconnectedUsedPixmap,
+ connectedSelectedPixmap, unconnectedSelectedPixmap;
+ static bool havePixmaps = false;
+
+ if (!havePixmaps) {
+
+ QString pixmapDir =
+ KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ connectedPixmap.load
+ (QString("%1/misc/connected.xpm").arg(pixmapDir));
+ connectedUsedPixmap.load
+ (QString("%1/misc/connected-used.xpm").arg(pixmapDir));
+ connectedSelectedPixmap.load
+ (QString("%1/misc/connected-selected.xpm").arg(pixmapDir));
+ unconnectedPixmap.load
+ (QString("%1/misc/unconnected.xpm").arg(pixmapDir));
+ unconnectedUsedPixmap.load
+ (QString("%1/misc/unconnected-used.xpm").arg(pixmapDir));
+ unconnectedSelectedPixmap.load
+ (QString("%1/misc/unconnected-selected.xpm").arg(pixmapDir));
+
+ havePixmaps = true;
+ }
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ // clear the popup
+ instrumentPopup->clear();
+
+ std::vector<QPopupMenu*> instrumentSubMenus;
+
+ // position index
+ int i = 0;
+
+ // Get the list
+ InstrumentList list = studio.getPresentationInstruments();
+ InstrumentList::iterator it;
+ int currentDevId = -1;
+ bool deviceUsedByAnyone = false;
+
+ for (it = list.begin(); it != list.end(); it++) {
+
+ if (! (*it))
+ continue; // sanity check
+
+ QString iname(strtoqstr((*it)->getPresentationName()));
+ QString pname(strtoqstr((*it)->getProgramName()));
+ Device *device = (*it)->getDevice();
+ DeviceId devId = device->getId();
+ bool connected = false;
+
+ if ((*it)->getType() == Instrument::SoftSynth) {
+ pname = "";
+ AudioPluginInstance *plugin = (*it)->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+ if (plugin) {
+ pname = strtoqstr(plugin->getProgram());
+ QString identifier = strtoqstr(plugin->getIdentifier());
+ if (identifier != "") {
+ connected = true;
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier
+ (identifier, type, soName, label);
+ if (pname == "") {
+ pname = strtoqstr(plugin->getDistinctiveConfigurationText());
+ }
+ if (pname != "") {
+ pname = QString("%1: %2").arg(label).arg(pname);
+ } else {
+ pname = label;
+ }
+ } else {
+ connected = false;
+ }
+ }
+ } else if ((*it)->getType() == Instrument::Audio) {
+ connected = true;
+ } else {
+ connected = (device->getConnection() != "");
+ }
+
+ bool instrUsedByMe = false;
+ bool instrUsedByAnyone = false;
+
+ if (thisTrackInstr && thisTrackInstr->getId() == (*it)->getId()) {
+ instrUsedByMe = true;
+ instrUsedByAnyone = true;
+ }
+
+ if (devId != (DeviceId)(currentDevId)) {
+
+ deviceUsedByAnyone = false;
+
+ if (instrUsedByMe)
+ deviceUsedByAnyone = true;
+ else {
+ for (Composition::trackcontainer::iterator tit =
+ comp.getTracks().begin();
+ tit != comp.getTracks().end(); ++tit) {
+
+ if (tit->second->getInstrument() == (*it)->getId()) {
+ instrUsedByAnyone = true;
+ deviceUsedByAnyone = true;
+ break;
+ }
+
+ Instrument *instr =
+ studio.getInstrumentById(tit->second->getInstrument());
+ if (instr && (instr->getDevice()->getId() == devId)) {
+ deviceUsedByAnyone = true;
+ }
+ }
+ }
+
+ QIconSet iconSet
+ (connected ?
+ (deviceUsedByAnyone ?
+ connectedUsedPixmap : connectedPixmap) :
+ (deviceUsedByAnyone ?
+ unconnectedUsedPixmap : unconnectedPixmap));
+
+ currentDevId = int(devId);
+
+ QPopupMenu *subMenu = new QPopupMenu(instrumentPopup);
+ QString deviceName = strtoqstr(device->getName());
+ instrumentPopup->insertItem(iconSet, deviceName, subMenu);
+ instrumentSubMenus.push_back(subMenu);
+
+ // Connect up the submenu
+ //
+ connect(subMenu, SIGNAL(activated(int)),
+ SLOT(slotInstrumentPopupActivated(int)));
+
+ } else if (!instrUsedByMe) {
+
+ for (Composition::trackcontainer::iterator tit =
+ comp.getTracks().begin();
+ tit != comp.getTracks().end(); ++tit) {
+
+ if (tit->second->getInstrument() == (*it)->getId()) {
+ instrUsedByAnyone = true;
+ break;
+ }
+ }
+ }
+
+ QIconSet iconSet
+ (connected ?
+ (instrUsedByAnyone ?
+ instrUsedByMe ?
+ connectedSelectedPixmap :
+ connectedUsedPixmap : connectedPixmap) :
+ (instrUsedByAnyone ?
+ instrUsedByMe ?
+ unconnectedSelectedPixmap :
+ unconnectedUsedPixmap : unconnectedPixmap));
+
+ if (pname != "")
+ iname += " (" + pname + ")";
+
+ instrumentSubMenus[instrumentSubMenus.size() - 1]->insertItem(iconSet, iname, i++);
+ }
+
+}
+
+void
+TrackButtons::slotInstrumentPopupActivated(int item)
+{
+ RG_DEBUG << "TrackButtons::slotInstrumentPopupActivated " << item << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ Instrument *inst = studio.getInstrumentFromList(item);
+
+ RG_DEBUG << "TrackButtons::slotInstrumentPopupActivated: instrument " << inst << endl;
+
+ if (inst != 0) {
+ Track *track = comp.getTrackByPosition(m_popupItem);
+
+ if (track != 0) {
+ track->setInstrument(inst->getId());
+
+ // select instrument
+ emit instrumentSelected((int)inst->getId());
+
+ m_trackLabels[m_popupItem]->getInstrumentLabel()->
+ setText(strtoqstr(inst->getPresentationName()));
+
+ // reset the alternative label
+ m_trackLabels[m_popupItem]->clearAlternativeLabel();
+
+ // Now see if the program is being shown for this instrument
+ // and if so reset the label
+ //
+ if (inst->sendsProgramChange())
+ m_trackLabels[m_popupItem]->setAlternativeLabel(strtoqstr(inst->getProgramName()));
+
+ if (inst->getType() == Instrument::Audio) {
+ m_recordLeds[m_popupItem]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[m_popupItem]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ } else
+ RG_DEBUG << "slotInstrumentPopupActivated() - can't find item!\n";
+ } else
+ RG_DEBUG << "slotInstrumentPopupActivated() - can't find item!\n";
+
+}
+
+void
+TrackButtons::changeTrackInstrumentLabels(TrackLabel::InstrumentTrackLabels label)
+{
+ // Set new label
+ m_trackInstrumentLabels = label;
+
+ // update and reconnect with new value
+ for (int i = 0; i < (int)m_tracks; i++) {
+ m_trackLabels[i]->showLabel(label);
+ }
+}
+
+void
+TrackButtons::changeInstrumentLabel(InstrumentId id, QString label)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+
+ if (track && track->getInstrument() == id) {
+
+ m_trackLabels[i]->setAlternativeLabel(label);
+
+ Instrument *ins = m_doc->getStudio().
+ getInstrumentById(track->getInstrument());
+
+ if (ins && ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::changeTrackLabel(TrackId id, QString label)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+ if (track && track->getId() == id) {
+ if (m_trackLabels[i]->getTrackLabel()->text() != label) {
+ m_trackLabels[i]->getTrackLabel()->setText(label);
+ emit widthChanged();
+ emit nameChanged();
+ }
+ return ;
+ }
+ }
+}
+
+void
+TrackButtons::slotSynchroniseWithComposition()
+{
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+ if (track->isMuted())
+ m_muteLeds[i]->off();
+ else
+ m_muteLeds[i]->on();
+
+ Instrument *ins = studio.
+ getInstrumentById(track->getInstrument());
+
+ QString instrumentName(i18n("<no instrument>"));
+ if (ins)
+ instrumentName = strtoqstr(ins->getPresentationName());
+
+ m_trackLabels[i]->getInstrumentLabel()->setText(instrumentName);
+
+ setRecordButton(i, comp.isTrackRecording(track->getId()));
+
+ if (ins && ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::slotLabelSelected(int position)
+{
+ Track *track =
+ m_doc->getComposition().getTrackByPosition(position);
+
+ if (track) {
+ emit trackSelected(track->getId());
+ }
+}
+
+void
+TrackButtons::setMuteButton(TrackId track, bool value)
+{
+ Track *trackObj = m_doc->getComposition().getTrackById(track);
+ if (trackObj == 0)
+ return ;
+
+ int pos = trackObj->getPosition();
+
+ RG_DEBUG << "TrackButtons::setMuteButton() trackId = "
+ << track << ", pos = " << pos << endl;
+
+ m_muteLeds[pos]->setState(value ? KLed::Off : KLed::On);
+}
+
+void
+TrackButtons::slotTrackInstrumentSelection(TrackId trackId, int item)
+{
+ RG_DEBUG << "TrackButtons::slotTrackInstrumentSelection(" << trackId << ")\n";
+
+ Composition &comp = m_doc->getComposition();
+ int position = comp.getTrackById(trackId)->getPosition();
+ m_popupItem = position;
+ slotInstrumentPopupActivated( item );
+}
+
+QFrame* TrackButtons::makeButton(Rosegarden::TrackId trackId)
+{
+ // The buttonGap sets up the sizes of the buttons
+ //
+ static const int buttonGap = 8;
+
+ QFrame *trackHBox = 0;
+
+ KLedButton *mute = 0;
+ KLedButton *record = 0;
+
+ TrackVUMeter *vuMeter = 0;
+ TrackLabel *trackLabel = 0;
+
+ int vuWidth = 20;
+ int vuSpacing = 2;
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(trackId);
+ if (multiple == 0) multiple = 1;
+ int labelWidth = m_trackLabelWidth - ( (m_cellSize - buttonGap) * 2 +
+ vuSpacing * 2 + vuWidth );
+
+ // Set the label from the Track object on the Composition
+ //
+ Rosegarden::Track *track = m_doc->getComposition().getTrackById(trackId);
+
+ if (track == 0) return 0;
+
+ // Create a horizontal box for each track
+ //
+ trackHBox = new QFrame(this);
+ QHBoxLayout *hblayout = new QHBoxLayout(trackHBox);
+
+ trackHBox->setMinimumSize(labelWidth, m_cellSize * multiple - m_borderGap);
+ trackHBox->setFixedHeight(m_cellSize * multiple - m_borderGap);
+
+ // Try a style for the box
+ //
+ trackHBox->setFrameStyle(StyledPanel);
+ trackHBox->setFrameShape(StyledPanel);
+ trackHBox->setFrameShadow(Raised);
+
+ // Insert a little gap
+ hblayout->addSpacing(vuSpacing);
+
+ // Create a VU meter
+ vuMeter = new TrackVUMeter(trackHBox,
+ VUMeter::PeakHold,
+ vuWidth,
+ buttonGap,
+ track->getPosition());
+
+ m_trackMeters.push_back(vuMeter);
+
+ hblayout->addWidget(vuMeter);
+
+ // Create another little gap
+ hblayout->addSpacing(vuSpacing);
+
+ //
+ // 'mute' and 'record' leds
+ //
+
+ mute = new KLedButton(Rosegarden::GUIPalette::getColour
+ (Rosegarden::GUIPalette::MuteTrackLED), trackHBox);
+ QToolTip::add(mute, i18n("Mute track"));
+ hblayout->addWidget(mute);
+
+ record = new KLedButton(Rosegarden::GUIPalette::getColour
+ (Rosegarden::GUIPalette::RecordMIDITrackLED), trackHBox);
+ QToolTip::add(record, i18n("Record on this track"));
+ hblayout->addWidget(record);
+
+ record->setLook(KLed::Sunken);
+ mute->setLook(KLed::Sunken);
+ record->off();
+
+ // Connect them to their sigmappers
+ connect(record, SIGNAL(stateChanged(bool)),
+ m_recordSigMapper, SLOT(map()));
+ connect(mute, SIGNAL(stateChanged(bool)),
+ m_muteSigMapper, SLOT(map()));
+ m_recordSigMapper->setMapping(record, track->getPosition());
+ m_muteSigMapper->setMapping(mute, track->getPosition());
+
+ // Store the KLedButton
+ //
+ m_muteLeds.push_back(mute);
+ m_recordLeds.push_back(record);
+
+ //
+ // Track label
+ //
+ trackLabel = new TrackLabel(trackId, track->getPosition(), trackHBox);
+ hblayout->addWidget(trackLabel);
+ hblayout->addSpacing(vuSpacing);
+
+ if (track->getLabel() == std::string("")) {
+ Rosegarden::Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+ if (ins && ins->getType() == Rosegarden::Instrument::Audio) {
+ trackLabel->getTrackLabel()->setText(i18n("<untitled audio>"));
+ } else {
+ trackLabel->getTrackLabel()->setText(i18n("<untitled>"));
+ }
+ }
+ else
+ trackLabel->getTrackLabel()->setText(strtoqstr(track->getLabel()));
+
+ trackLabel->setFixedSize(labelWidth, m_cellSize - buttonGap);
+ trackLabel->setFixedHeight(m_cellSize - buttonGap);
+ trackLabel->setIndent(7);
+
+ connect(trackLabel, SIGNAL(renameTrack(QString, TrackId)),
+ SLOT(slotRenameTrack(QString, TrackId)));
+
+ // Store the TrackLabel pointer
+ //
+ m_trackLabels.push_back(trackLabel);
+
+ // Connect it
+ setButtonMapping(trackLabel, trackId);
+
+ connect(trackLabel, SIGNAL(changeToInstrumentList()),
+ m_instListSigMapper, SLOT(map()));
+ connect(trackLabel, SIGNAL(clicked()),
+ m_clickedSigMapper, SLOT(map()));
+
+ //
+ // instrument label
+ //
+ Rosegarden::Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ QString instrumentName(i18n("<no instrument>"));
+ if (ins) instrumentName = strtoqstr(ins->getPresentationName());
+
+ // Set label to program change if it's being sent
+ //
+ if (ins != 0 && ins->sendsProgramChange())
+ trackLabel->setAlternativeLabel(strtoqstr(ins->getProgramName()));
+
+ trackLabel->showLabel(m_trackInstrumentLabels);
+
+ mute->setFixedSize(m_cellSize - buttonGap, m_cellSize - buttonGap);
+ record->setFixedSize(m_cellSize - buttonGap, m_cellSize - buttonGap);
+
+ // set the mute button
+ //
+ if (track->isMuted())
+ mute->off();
+
+ return trackHBox;
+}
+
+}
+#include "TrackButtons.moc"
diff --git a/src/gui/editors/segment/TrackButtons.h b/src/gui/editors/segment/TrackButtons.h
new file mode 100644
index 0000000..a61601d
--- /dev/null
+++ b/src/gui/editors/segment/TrackButtons.h
@@ -0,0 +1,228 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKBUTTONS_H_
+#define _RG_TRACKBUTTONS_H_
+
+#include "base/MidiProgram.h"
+#include "base/Track.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "TrackLabel.h"
+#include <qframe.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QVBoxLayout;
+class QSignalMapper;
+class QPopupMenu;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class TrackVUMeter;
+class RosegardenGUIDoc;
+class KLedButton;
+class Instrument;
+
+
+class TrackButtons : public QFrame
+{
+ Q_OBJECT
+public:
+
+ TrackButtons(RosegardenGUIDoc* doc,
+ unsigned int trackCellHeight,
+ unsigned int trackLabelWidth,
+ bool showTrackLabels,
+ int overallHeight,
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ ~TrackButtons();
+
+ /// Return a vector of muted tracks
+ std::vector<int> mutedTracks();
+
+ /// Return a vector of highlighted tracks
+ std::vector<int> getHighlightedTracks();
+
+ void changeTrackInstrumentLabels(TrackLabel::InstrumentTrackLabels label);
+
+ /**
+ * Change the instrument label to something else like
+ * an actual program name rather than a meaningless
+ * device number and midi channel
+ */
+ void changeInstrumentLabel(InstrumentId id, QString label);
+
+ void changeTrackLabel(TrackId id, QString label);
+
+ // Select a label from outside this class by position
+ //
+ void selectLabel(int trackId);
+
+ /*
+ * Set the mute button down or up
+ */
+ void setMuteButton(TrackId track, bool value);
+
+ /*
+ * Make this available so that others can set record buttons down
+ */
+ void setRecordTrack(int position, bool value);
+
+ /**
+ * Precalculate the Instrument popup so we don't have to every
+ * time it appears
+ * not protected because also used by the RosegardenGUIApp
+ *
+ * @see RosegardenGUIApp#slotPopulateTrackInstrumentPopup()
+ */
+ void populateInstrumentPopup(Instrument *thisTrackInstr, QPopupMenu* instrumentPopup);
+
+signals:
+ // to emit what Track has been selected
+ //
+ void trackSelected(int);
+ void instrumentSelected(int);
+
+ void widthChanged();
+
+ // to tell the notation canvas &c when a name changes
+ //
+ void nameChanged();
+
+ // document modified (mute button)
+ //
+ void modified();
+
+ // A record button has been pressed - if we're setting to an audio
+ // track we need to tell the sequencer for live monitoring
+ // purposes.
+ //
+ void recordButton(TrackId track, bool state);
+
+ // A mute button has been pressed
+ //
+ void muteButton(TrackId track, bool state);
+
+public slots:
+
+ void slotToggleRecordTrack(int position);
+ void slotToggleMutedTrack(int mutedTrack);
+ void slotUpdateTracks();
+ void slotRenameTrack(QString newName, TrackId trackId);
+ void slotSetTrackMeter(float value, int position);
+ void slotSetMetersByInstrument(float value, InstrumentId id);
+
+ void slotInstrumentSelection(int);
+ void slotInstrumentPopupActivated(int);
+ void slotTrackInstrumentSelection(TrackId, int);
+
+ // ensure track buttons match the Composition
+ //
+ void slotSynchroniseWithComposition();
+
+ // Convert a positional selection into a track selection and re-emit
+ //
+ void slotLabelSelected(int position);
+
+protected:
+
+ /**
+ * Populate the track buttons themselves with Instrument information
+ */
+ void populateButtons();
+
+ /**
+ * Remove buttons and clear iterators for a position
+ */
+ void removeButtons(unsigned int position);
+
+ /**
+ * Set record button - graphically only
+ */
+ void setRecordButton(int position, bool down);
+
+ /**
+ * buttons, starting at the specified index
+ */
+ void makeButtons();
+
+ QFrame* makeButton(TrackId trackId);
+ QString getPresentationName(Instrument *);
+
+ void setButtonMapping(QObject*, TrackId);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+
+ QVBoxLayout *m_layout;
+
+ std::vector<KLedButton *> m_muteLeds;
+ std::vector<KLedButton *> m_recordLeds;
+ std::vector<TrackLabel *> m_trackLabels;
+ std::vector<TrackVUMeter *> m_trackMeters;
+ std::vector<QFrame *> m_trackHBoxes;
+
+ QSignalMapper *m_recordSigMapper;
+ QSignalMapper *m_muteSigMapper;
+ QSignalMapper *m_clickedSigMapper;
+ QSignalMapper *m_instListSigMapper;
+
+ // Number of tracks on our view
+ //
+ unsigned int m_tracks;
+
+ // The pixel offset from the top - just to overcome
+ // the borders
+ int m_offset;
+
+ // The height of the cells
+ //
+ int m_cellSize;
+
+ // gaps between elements
+ //
+ int m_borderGap;
+
+ int m_trackLabelWidth;
+ int m_popupItem;
+
+ TrackLabel::InstrumentTrackLabels m_trackInstrumentLabels;
+ int m_lastSelected;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackEditor.cpp b/src/gui/editors/segment/TrackEditor.cpp
new file mode 100644
index 0000000..32c2b02
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditor.cpp
@@ -0,0 +1,827 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackEditor.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/rulers/StandardRuler.h"
+#include "base/Composition.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "commands/segment/AddTracksCommand.h"
+#include "commands/segment/DeleteTracksCommand.h"
+#include "commands/segment/SegmentEraseCommand.h"
+#include "commands/segment/SegmentInsertCommand.h"
+#include "commands/segment/SegmentRepeatToCopyCommand.h"
+#include "segmentcanvas/CompositionModel.h"
+#include "segmentcanvas/CompositionModelImpl.h"
+#include "segmentcanvas/CompositionView.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "gui/widgets/QDeferScrollView.h"
+#include "sound/AudioFile.h"
+#include "TrackButtons.h"
+#include "TrackEditorIface.h"
+#include <dcopobject.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qfont.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qwidget.h>
+#include <qvalidator.h>
+#include <qdragobject.h>
+#include <qtextstream.h>
+
+
+namespace Rosegarden
+{
+
+TrackEditor::TrackEditor(RosegardenGUIDoc* doc,
+ QWidget* rosegardenguiview,
+ RulerScale *rulerScale,
+ bool showTrackLabels,
+ double initialUnitsPerPixel,
+ QWidget* parent, const char* name,
+ WFlags) :
+ DCOPObject("TrackEditorIface"),
+ QWidget(parent, name),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_topStandardRuler(0),
+ m_bottomStandardRuler(0),
+ m_trackButtons(0),
+ m_segmentCanvas(0),
+ m_trackButtonScroll(0),
+ m_showTrackLabels(showTrackLabels),
+ m_canvasWidth(0),
+ m_compositionRefreshStatusId(doc->getComposition().getNewRefreshStatusId()),
+ m_playTracking(true),
+ m_initialUnitsPerPixel(initialUnitsPerPixel)
+{
+ // accept dnd
+ setAcceptDrops(true);
+
+ init(rosegardenguiview);
+ slotReadjustCanvasSize();
+}
+
+TrackEditor::~TrackEditor()
+{
+ delete m_chordNameRuler;
+ delete m_compositionModel;
+}
+
+void
+TrackEditor::init(QWidget* rosegardenguiview)
+{
+ QGridLayout *grid = new QGridLayout(this, 4, 2);
+
+ int trackLabelWidth = 230;
+ int barButtonsHeight = 25;
+
+ m_chordNameRuler = new ChordNameRuler(m_rulerScale,
+ m_doc,
+ 0.0,
+ 20,
+ this);
+ grid->addWidget(m_chordNameRuler, 0, 1);
+
+ m_tempoRuler = new TempoRuler(m_rulerScale,
+ m_doc,
+ RosegardenGUIApp::self(),
+ 0.0,
+ 24,
+ true,
+ this);
+
+ grid->addWidget(m_tempoRuler, 1, 1);
+
+ m_tempoRuler->connectSignals();
+
+ //
+ // Top Bar Buttons
+ //
+ m_topStandardRuler = new StandardRuler(m_doc,
+ m_rulerScale,
+ 0,
+ barButtonsHeight,
+ false,
+ this, "topbarbuttons");
+ m_topStandardRuler->connectRulerToDocPointer(m_doc);
+
+ grid->addWidget(m_topStandardRuler, 2, 1);
+
+ //
+ // Segment Canvas
+ //
+ m_compositionModel = new CompositionModelImpl(m_doc->getComposition(),
+ m_doc->getStudio(),
+ m_rulerScale, getTrackCellHeight());
+
+ connect(rosegardenguiview, SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_compositionModel, SLOT(slotInstrumentParametersChanged(InstrumentId)));
+ connect(rosegardenguiview->parent(), SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_compositionModel, SLOT(slotInstrumentParametersChanged(InstrumentId)));
+
+ m_segmentCanvas = new CompositionView(m_doc, m_compositionModel, this);
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (kapp->config()->readBoolEntry("backgroundtextures", true)) {
+ QPixmap background;
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ if (background.load(QString("%1/misc/bg-segmentcanvas.xpm").
+ arg(pixmapDir))) {
+ m_segmentCanvas->setBackgroundPixmap(background);
+ m_segmentCanvas->viewport()->setBackgroundPixmap(background);
+ }
+ }
+
+ //
+ // Bottom Bar Buttons
+ //
+ m_bottomStandardRuler = new StandardRuler(m_doc,
+ m_rulerScale,
+ 0,
+ barButtonsHeight,
+ true,
+ m_segmentCanvas, "bottombarbuttons");
+ m_bottomStandardRuler->connectRulerToDocPointer(m_doc);
+
+ m_segmentCanvas->setBottomFixedWidget(m_bottomStandardRuler);
+
+ grid->addWidget(m_segmentCanvas, 3, 1);
+
+ grid->setColStretch(1, 10); // to make sure the seg canvas doesn't leave a "blank" grey space when
+ // loading a file which has a low zoom factor
+
+ // Track Buttons
+ //
+ // (must be put in a QScrollView)
+ //
+ m_trackButtonScroll = new QDeferScrollView(this);
+ grid->addWidget(m_trackButtonScroll, 3, 0);
+
+ int canvasHeight = getTrackCellHeight() *
+ std::max(40u, m_doc->getComposition().getNbTracks());
+
+ m_trackButtons = new TrackButtons(m_doc,
+ getTrackCellHeight(),
+ trackLabelWidth,
+ m_showTrackLabels,
+ canvasHeight,
+ m_trackButtonScroll->viewport());
+ m_trackButtonScroll->addChild(m_trackButtons);
+ m_trackButtonScroll->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_trackButtonScroll->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_trackButtonScroll->setResizePolicy(QScrollView::AutoOneFit);
+ m_trackButtonScroll->setBottomMargin(m_bottomStandardRuler->height() +
+ m_segmentCanvas->horizontalScrollBar()->height());
+
+ connect(m_trackButtons, SIGNAL(widthChanged()),
+ this, SLOT(slotTrackButtonsWidthChanged()));
+
+ connect(m_trackButtons, SIGNAL(trackSelected(int)),
+ rosegardenguiview, SLOT(slotSelectTrackSegments(int)));
+
+ connect(m_trackButtons, SIGNAL(instrumentSelected(int)),
+ rosegardenguiview, SLOT(slotUpdateInstrumentParameterBox(int)));
+
+ connect(this, SIGNAL(stateChange(QString, bool)),
+ rosegardenguiview, SIGNAL(stateChange(QString, bool)));
+
+ connect(m_trackButtons, SIGNAL(modified()),
+ m_doc, SLOT(slotDocumentModified()));
+
+ connect(m_trackButtons, SIGNAL(muteButton(TrackId, bool)),
+ rosegardenguiview, SLOT(slotSetMuteButton(TrackId, bool)));
+
+ // connect loop rulers' follow-scroll signals
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_segmentCanvas, SLOT(startAutoScroll(int)));
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_segmentCanvas, SLOT(stopAutoScroll()));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_segmentCanvas, SLOT(startAutoScroll(int)));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_segmentCanvas, SLOT(stopAutoScroll()));
+
+ connect(m_segmentCanvas, SIGNAL(contentsMoving(int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+
+ // Synchronize bar buttons' scrollview with segment canvas' scrollbar
+ //
+ connect(m_segmentCanvas->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotVerticalScrollTrackButtons(int)));
+
+ connect(m_segmentCanvas->verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ this, SLOT(slotVerticalScrollTrackButtons(int)));
+
+ // scrolling with mouse wheel
+ connect(m_trackButtonScroll, SIGNAL(gotWheelEvent(QWheelEvent*)),
+ m_segmentCanvas, SLOT(slotExternalWheelEvent(QWheelEvent*)));
+
+ // Connect horizontal scrollbar
+ //
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_tempoRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_tempoRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_chordNameRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_chordNameRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(this, SIGNAL(needUpdate()), m_segmentCanvas, SLOT(slotUpdateSegmentsDrawBuffer()));
+
+ connect(m_segmentCanvas->getModel(),
+ SIGNAL(selectedSegments(const SegmentSelection &)),
+ rosegardenguiview,
+ SLOT(slotSelectedSegments(const SegmentSelection &)));
+
+ connect(m_segmentCanvas, SIGNAL(zoomIn()),
+ RosegardenGUIApp::self(), SLOT(slotZoomIn()));
+ connect(m_segmentCanvas, SIGNAL(zoomOut()),
+ RosegardenGUIApp::self(), SLOT(slotZoomOut()));
+
+ connect(getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ connect(m_doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ //
+ // pointer and loop drag signals from top and bottom bar buttons (loop rulers actually)
+ //
+ connect(m_topStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotPointerDraggedToPosition(timeT)));
+ connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotPointerDraggedToPosition(timeT)));
+
+ connect(m_topStandardRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SLOT(slotLoopDraggedToPosition(timeT)));
+ connect(m_bottomStandardRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SLOT(slotLoopDraggedToPosition(timeT)));
+
+ connect(m_doc, SIGNAL(loopChanged(timeT,
+ timeT)),
+ this, SLOT(slotSetLoop(timeT, timeT)));
+}
+
+void TrackEditor::slotReadjustCanvasSize()
+{
+ m_segmentCanvas->slotUpdateSize();
+}
+
+void TrackEditor::slotTrackButtonsWidthChanged()
+{
+ // We need to make sure the trackButtons geometry is fully updated
+ //
+ ProgressDialog::processEvents();
+
+ m_trackButtonScroll->setMinimumWidth(m_trackButtons->width());
+ m_doc->slotDocumentModified();
+}
+
+int TrackEditor::getTrackCellHeight() const
+{
+ int size;
+ static QFont defaultFont;
+
+ // do some scrabbling around for a reasonable size
+ //
+ size = defaultFont.pixelSize();
+
+ if (size < 8) {
+ if (QApplication::font(this).pixelSize() < 8)
+ size = 12;
+ else
+ size = QApplication::font(this).pixelSize();
+ }
+
+ return size + 12;
+}
+
+bool TrackEditor::isCompositionModified()
+{
+ return m_doc->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).needsRefresh();
+}
+
+void TrackEditor::setCompositionModified(bool c)
+{
+ m_doc->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).setNeedsRefresh(c);
+}
+
+void TrackEditor::updateRulers()
+{
+ if (getTempoRuler() != 0)
+ getTempoRuler()->update();
+
+ if (getChordNameRuler() != 0)
+ getChordNameRuler()->update();
+
+ getTopStandardRuler()->update();
+ getBottomStandardRuler()->update();
+}
+
+void TrackEditor::paintEvent(QPaintEvent* e)
+{
+ if (isCompositionModified()) {
+
+ slotReadjustCanvasSize();
+ m_trackButtons->slotUpdateTracks();
+ m_segmentCanvas->clearSegmentRectsCache(true);
+ m_segmentCanvas->updateContents();
+
+ Composition &composition = m_doc->getComposition();
+
+ if (composition.getNbSegments() == 0) {
+ emit stateChange("have_segments", false); // no segments : reverse state
+ emit stateChange("have_selection", false); // no segments : reverse state
+ } else {
+ emit stateChange("have_segments", true);
+ if (m_segmentCanvas->haveSelection())
+ emit stateChange("have_selection", true);
+ else
+ emit stateChange("have_selection", false); // no selection : reverse state
+ }
+
+ setCompositionModified(false);
+ }
+
+ QWidget::paintEvent(e);
+}
+
+void TrackEditor::slotAddTracks(unsigned int nbNewTracks,
+ InstrumentId id,
+ int position)
+{
+ Composition &comp = m_doc->getComposition();
+
+ AddTracksCommand* command = new AddTracksCommand(&comp, nbNewTracks, id,
+ position);
+ addCommandToHistory(command);
+ slotReadjustCanvasSize();
+}
+
+void TrackEditor::slotDeleteTracks(std::vector<TrackId> tracks)
+{
+ Composition &comp = m_doc->getComposition();
+
+ DeleteTracksCommand* command = new DeleteTracksCommand(&comp, tracks);
+ addCommandToHistory(command);
+}
+
+void TrackEditor::addSegment(int track, int time, unsigned int duration)
+{
+ if (!m_doc)
+ return ; // sanity check
+
+ SegmentInsertCommand *command =
+ new SegmentInsertCommand(m_doc, track, time, duration);
+
+ addCommandToHistory(command);
+}
+
+void TrackEditor::slotSegmentOrderChanged(int section, int fromIdx, int toIdx)
+{
+ RG_DEBUG << QString("TrackEditor::segmentOrderChanged(section : %1, from %2, to %3)")
+ .arg(section).arg(fromIdx).arg(toIdx) << endl;
+
+ //!!! how do we get here? need to involve a command
+ emit needUpdate();
+}
+
+void
+TrackEditor::slotCanvasScrolled(int x, int y)
+{
+ // update the pointer position if the user is dragging it from the loop ruler
+ if ((m_topStandardRuler && m_topStandardRuler->getLoopRuler() &&
+ m_topStandardRuler->getLoopRuler()->hasActiveMousePress() &&
+ !m_topStandardRuler->getLoopRuler()->getLoopingMode()) ||
+ (m_bottomStandardRuler && m_bottomStandardRuler->getLoopRuler() &&
+ m_bottomStandardRuler->getLoopRuler()->hasActiveMousePress() &&
+ !m_bottomStandardRuler->getLoopRuler()->getLoopingMode())) {
+
+ int mx = m_segmentCanvas->viewport()->mapFromGlobal(QCursor::pos()).x();
+ m_segmentCanvas->setPointerPos(x + mx);
+
+ // bad idea, creates a feedback loop
+ // timeT t = m_segmentCanvas->grid().getRulerScale()->getTimeForX(x + mx);
+ // slotSetPointerPosition(t);
+ }
+}
+
+void
+TrackEditor::slotSetPointerPosition(timeT position)
+{
+ SimpleRulerScale *ruler =
+ dynamic_cast<SimpleRulerScale*>(m_rulerScale);
+
+ if (!ruler)
+ return ;
+
+ double pos = m_segmentCanvas->grid().getRulerScale()->getXForTime(position);
+
+ int currentPointerPos = m_segmentCanvas->getPointerPos();
+
+ double distance = pos - currentPointerPos;
+ if (distance < 0.0)
+ distance = -distance;
+
+ if (distance >= 1.0) {
+
+ if (m_doc && m_doc->getSequenceManager() &&
+ (m_doc->getSequenceManager()->getTransportStatus() != STOPPED)) {
+
+ if (m_playTracking) {
+ getSegmentCanvas()->slotScrollHoriz(int(double(position) / ruler->getUnitsPerPixel()));
+ }
+ } else if (!getSegmentCanvas()->isAutoScrolling()) {
+ int newpos = int(double(position) / ruler->getUnitsPerPixel());
+ // RG_DEBUG << "TrackEditor::slotSetPointerPosition("
+ // << position
+ // << ") : calling canvas->slotScrollHoriz() "
+ // << newpos << endl;
+ getSegmentCanvas()->slotScrollHoriz(newpos);
+ }
+
+ m_segmentCanvas->setPointerPos(pos);
+ }
+
+}
+
+void
+TrackEditor::slotPointerDraggedToPosition(timeT position)
+{
+ int currentPointerPos = m_segmentCanvas->getPointerPos();
+
+ double newPosition;
+
+ if (handleAutoScroll(currentPointerPos, position, newPosition))
+ m_segmentCanvas->setPointerPos(int(newPosition));
+}
+
+void
+TrackEditor::slotLoopDraggedToPosition(timeT position)
+{
+ if (m_doc) {
+ int currentEndLoopPos = m_doc->getComposition().getLoopEnd();
+ double dummy;
+ handleAutoScroll(currentEndLoopPos, position, dummy);
+ }
+}
+
+bool TrackEditor::handleAutoScroll(int currentPosition, timeT newTimePosition, double &newPosition)
+{
+ SimpleRulerScale *ruler =
+ dynamic_cast<SimpleRulerScale*>(m_rulerScale);
+
+ if (!ruler)
+ return false;
+
+ newPosition = m_segmentCanvas->grid().getRulerScale()->getXForTime(newTimePosition);
+
+ double distance = fabs(newPosition - currentPosition);
+
+ bool moveDetected = distance >= 1.0;
+
+ if (moveDetected) {
+
+ if (m_doc && m_doc->getSequenceManager() &&
+ (m_doc->getSequenceManager()->getTransportStatus() != STOPPED)) {
+
+ if (m_playTracking) {
+ getSegmentCanvas()->slotScrollHoriz(int(double(newTimePosition) / ruler->getUnitsPerPixel()));
+ }
+ } else {
+ int newpos = int(double(newTimePosition) / ruler->getUnitsPerPixel());
+ getSegmentCanvas()->slotScrollHorizSmallSteps(newpos);
+ getSegmentCanvas()->doAutoScroll();
+ }
+
+ }
+
+ return moveDetected;
+}
+
+void
+TrackEditor::slotToggleTracking()
+{
+ m_playTracking = !m_playTracking;
+}
+
+void
+TrackEditor::slotSetLoop(timeT start, timeT end)
+{
+ getTopStandardRuler()->getLoopRuler()->slotSetLoopMarker(start, end);
+ getBottomStandardRuler()->getLoopRuler()->slotSetLoopMarker(start, end);
+}
+
+MultiViewCommandHistory*
+TrackEditor::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+TrackEditor::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+void
+TrackEditor::slotScrollToTrack(int track)
+{
+ // Find the vertical track pos
+ int newY = track * getTrackCellHeight();
+
+ RG_DEBUG << "TrackEditor::scrollToTrack(" << track <<
+ ") scrolling to Y " << newY << endl;
+
+ // Scroll the segment view; it will scroll tracks by connected signals
+ // slotVerticalScrollTrackButtons(newY);
+ m_segmentCanvas->slotScrollVertSmallSteps(newY);
+}
+
+void
+TrackEditor::slotDeleteSelectedSegments()
+{
+ KMacroCommand *macro = new KMacroCommand("Delete Segments");
+
+ SegmentSelection segments =
+ m_segmentCanvas->getSelectedSegments();
+
+ if (segments.size() == 0)
+ return ;
+
+ SegmentSelection::iterator it;
+
+ // Clear the selection before erasing the Segments
+ // the selection points to
+ //
+ m_segmentCanvas->getModel()->clearSelected();
+
+ // Create the compound command
+ //
+ for (it = segments.begin(); it != segments.end(); it++) {
+ macro->addCommand(new SegmentEraseCommand(*it,
+ &m_doc->getAudioFileManager()));
+ }
+
+ addCommandToHistory(macro);
+
+}
+
+void
+TrackEditor::slotTurnRepeatingSegmentToRealCopies()
+{
+ RG_DEBUG << "TrackEditor::slotTurnRepeatingSegmentToRealCopies" << endl;
+
+ SegmentSelection segments =
+ m_segmentCanvas->getSelectedSegments();
+
+ if (segments.size() == 0)
+ return ;
+
+ QString text;
+
+ if (segments.size() == 1)
+ text = i18n("Turn Repeating Segment into Real Copies");
+ else
+ text = i18n("Turn Repeating Segments into Real Copies");
+
+ KMacroCommand *macro = new KMacroCommand(text);
+
+ SegmentSelection::iterator it = segments.begin();
+ for (; it != segments.end(); it++) {
+ if ((*it)->isRepeating()) {
+ macro->addCommand(new SegmentRepeatToCopyCommand(*it));
+ }
+ }
+
+ addCommandToHistory(macro);
+
+}
+
+void
+TrackEditor::slotVerticalScrollTrackButtons(int y)
+{
+ m_trackButtonScroll->setContentsPos(0, y);
+}
+
+void TrackEditor::dragEnterEvent(QDragEnterEvent *event)
+{
+ event->accept(QUriDrag::canDecode(event) ||
+ QTextDrag::canDecode(event));
+}
+
+void TrackEditor::dropEvent(QDropEvent* event)
+{
+ QStrList uri;
+ QString text;
+
+ int heightAdjust = 0;
+ //int widthAdjust = 0;
+
+ // Adjust any drop event height position by visible rulers
+ //
+ if (m_topStandardRuler && m_topStandardRuler->isVisible())
+ heightAdjust += m_topStandardRuler->height();
+
+ if (m_tempoRuler && m_tempoRuler->isVisible())
+ heightAdjust += m_tempoRuler->height();
+
+ if (m_chordNameRuler && m_chordNameRuler->isVisible())
+ heightAdjust += m_chordNameRuler->height();
+
+ QPoint posInSegmentCanvas =
+ m_segmentCanvas->viewportToContents
+ (m_segmentCanvas->
+ viewport()->mapFrom(this, event->pos()));
+
+ int trackPos = m_segmentCanvas->grid().getYBin(posInSegmentCanvas.y());
+
+ timeT time =
+// m_segmentCanvas->grid().getRulerScale()->
+// getTimeForX(posInSegmentCanvas.x());
+ m_segmentCanvas->grid().snapX(posInSegmentCanvas.x());
+
+
+ if (QUriDrag::decode(event, uri)) {
+ RG_DEBUG << "TrackEditor::dropEvent() : got URI :"
+ << uri.first() << endl;
+ QString uriPath = uri.first();
+
+ if (uriPath.endsWith(".rg")) {
+ emit droppedDocument(uriPath);
+ } else {
+
+ QStrList uris;
+ QString uri;
+ if (QUriDrag::decode(event, uris)) uri = uris.first();
+// QUriDrag::decodeLocalFiles(event, files);
+// QString filePath = files.first();
+
+ RG_DEBUG << "TrackEditor::dropEvent() : got URI: "
+ << uri << endl;
+
+ RG_DEBUG << "TrackEditor::dropEvent() : dropping at track pos = "
+ << trackPos
+ << ", time = "
+ << time
+ << ", x = "
+ << event->pos().x()
+ << ", mapped x = "
+ << posInSegmentCanvas.x()
+ << endl;
+
+ Track* track = m_doc->getComposition().getTrackByPosition(trackPos);
+ if (track) {
+ QString audioText;
+ QTextOStream t(&audioText);
+
+ t << uri << "\n";
+ t << track->getId() << "\n";
+ t << time << "\n";
+
+ emit droppedNewAudio(audioText);
+ }
+
+ }
+
+ } else if (QTextDrag::decode(event, text)) {
+ RG_DEBUG << "TrackEditor::dropEvent() : got text info " << endl;
+ //<< text << endl;
+
+ if (text.endsWith(".rg")) {
+ emit droppedDocument(text);
+ //
+ // WARNING
+ //
+ // DO NOT PERFORM ANY OPERATIONS AFTER THAT
+ // EMITTING THIS SIGNAL TRIGGERS THE LOADING OF A NEW DOCUMENT
+ // AND AS A CONSEQUENCE THE DELETION OF THIS TrackEditor OBJECT
+ //
+ } else {
+
+ QTextIStream s(&text);
+
+ QString id;
+ AudioFileId audioFileId;
+ RealTime startTime, endTime;
+
+ // read the audio info checking for end of stream
+ s >> id;
+ s >> audioFileId;
+ s >> startTime.sec;
+ s >> startTime.nsec;
+ s >> endTime.sec;
+ s >> endTime.nsec;
+
+ if (id == "AudioFileManager") { // only create something if this is data from the right client
+
+
+ // Drop this audio segment if we have a valid track number
+ // (could also check for time limits too)
+ //
+ Track* track = m_doc->getComposition().getTrackByPosition(trackPos);
+ if (track) {
+
+ RG_DEBUG << "TrackEditor::dropEvent() : dropping at track pos = "
+ << trackPos
+ << ", time = "
+ << time
+ << ", x = "
+ << event->pos().x()
+ << ", map = "
+ << posInSegmentCanvas.x()
+ << endl;
+
+ QString audioText;
+ QTextOStream t(&audioText);
+ t << audioFileId << "\n";
+ t << track->getId() << "\n";
+ t << time << "\n"; // time on canvas
+ t << startTime.sec << "\n";
+ t << startTime.nsec << "\n";
+ t << endTime.sec << "\n";
+ t << endTime.nsec << "\n";
+
+ emit droppedAudio(audioText);
+ }
+
+ } else {
+
+ KMessageBox::sorry(this, i18n("You can't drop files into Rosegarden from this client. Try using Konqueror instead."));
+
+ }
+
+ }
+
+ // SEE WARNING ABOVE - DON'T DO ANYTHING, THIS OBJECT MAY NOT
+ // EXIST AT THIS POINT.
+
+ }
+}
+
+}
+#include "TrackEditor.moc"
diff --git a/src/gui/editors/segment/TrackEditor.h b/src/gui/editors/segment/TrackEditor.h
new file mode 100644
index 0000000..6670a15
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditor.h
@@ -0,0 +1,248 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKEDITOR_H_
+#define _RG_TRACKEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include <map>
+#include "TrackEditorIface.h"
+#include <qstring.h>
+#include <qwidget.h>
+#include "base/Event.h"
+#include "gui/editors/segment/TrackButtons.h"
+
+
+class QPaintEvent;
+class QDropEvent;
+class QDragEnterEvent;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class TrackButtons;
+class TempoRuler;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class QDeferScrollView;
+class MultiViewCommandHistory;
+class CompositionView;
+class CompositionModel;
+class ChordNameRuler;
+class StandardRuler;
+
+
+/**
+ * Global widget for segment edition.
+ *
+ * Shows a global overview of the composition, and lets the user
+ * manipulate the segments
+ *
+ * @see CompositionView
+ */
+class TrackEditor : public QWidget, virtual public TrackEditorIface
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new TrackEditor representing the document \a doc
+ */
+ TrackEditor(RosegardenGUIDoc* doc,
+ QWidget* rosegardenguiview,
+ RulerScale *rulerScale,
+ bool showTrackLabels,
+ double initialUnitsPerPixel = 0,
+ QWidget* parent = 0, const char* name = 0,
+ WFlags f=0);
+
+ ~TrackEditor();
+
+ CompositionView* getSegmentCanvas() { return m_segmentCanvas; }
+ TempoRuler* getTempoRuler() { return m_tempoRuler; }
+ ChordNameRuler*getChordNameRuler() { return m_chordNameRuler; }
+ StandardRuler* getTopStandardRuler() { return m_topStandardRuler; }
+ StandardRuler* getBottomStandardRuler() { return m_bottomStandardRuler; }
+ TrackButtons* getTrackButtons() { return m_trackButtons; }
+ RulerScale* getRulerScale() { return m_rulerScale; }
+
+ int getTrackCellHeight() const;
+
+ /**
+ * Add a new segment - DCOP interface
+ */
+ virtual void addSegment(int track, int start, unsigned int duration);
+
+ /**
+ * Manage command history
+ */
+ MultiViewCommandHistory *getCommandHistory();
+ void addCommandToHistory(KCommand *command);
+
+ void updateRulers();
+
+ bool isTracking() const { return m_playTracking; }
+
+public slots:
+
+ /**
+ * Scroll the view such that the numbered track is on-screen
+ */
+ void slotScrollToTrack(int trackPosition);
+
+ /**
+ * Set the position pointer during playback
+ */
+ void slotSetPointerPosition(timeT position);
+
+ /**
+ * Update the pointer position as it is being dragged along
+ * This changes how the segment canvas will scroll to follow the pointer
+ */
+ void slotPointerDraggedToPosition(timeT position);
+
+ /**
+ * Update the loop end position as it is being dragged along
+ * This changes how the segment canvas will scroll to follow the pointer
+ */
+ void slotLoopDraggedToPosition(timeT position);
+
+ /**
+ * Act on a canvas scroll event
+ */
+ void slotCanvasScrolled(int, int);
+
+ /**
+ * Adjust the canvas size to that required for the composition
+ */
+ void slotReadjustCanvasSize();
+
+ /**
+ * Show the given loop on the ruler or wherever
+ */
+ void slotSetLoop(timeT start, timeT end);
+
+ /**
+ * Add given number of tracks
+ */
+ void slotAddTracks(unsigned int nbTracks, InstrumentId id, int position);
+
+ /*
+ * Delete a given track
+ */
+ void slotDeleteTracks(std::vector<TrackId> tracks);
+
+ void slotDeleteSelectedSegments();
+ void slotTurnRepeatingSegmentToRealCopies();
+
+ void slotToggleTracking();
+
+protected slots:
+ void slotSegmentOrderChanged(int section, int fromIdx, int toIdx);
+
+ void slotTrackButtonsWidthChanged();
+
+ /// Scroll the track buttons along with the segment canvas
+ void slotVerticalScrollTrackButtons(int y);
+
+signals:
+ /**
+ * Emitted when the represented data changed and the SegmentCanvas
+ * needs to update itself
+ *
+ * @see SegmentCanvas::update()
+ */
+ void needUpdate();
+
+ /**
+ * sent back to RosegardenGUI
+ */
+ void stateChange(QString, bool);
+
+ /**
+ * A URI to a Rosegarden document was dropped on the canvas
+ *
+ * @see RosegardenGUI#slotOpenURL()
+ */
+ void droppedDocument(QString uri);
+
+ /**
+ * An audio file was dropped from the audio manager dialog
+ */
+ void droppedAudio(QString audioDesc);
+
+ /**
+ * And audio file was dropped from konqi say and needs to be
+ * inserted into AudioManagerDialog before adding to the
+ * composition.
+ */
+ void droppedNewAudio(QString audioDesc);
+
+protected:
+
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent*);
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ void init(QWidget *);
+
+ bool isCompositionModified();
+ void setCompositionModified(bool);
+
+ /// return true if an actual move occurred between current and new position, newPosition contains the horiz. pos corresponding to newTimePosition
+ bool handleAutoScroll(int currentPosition, timeT newTimePosition, double& newPosition);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+ TempoRuler *m_tempoRuler;
+ ChordNameRuler *m_chordNameRuler;
+ StandardRuler *m_topStandardRuler;
+ StandardRuler *m_bottomStandardRuler;
+ TrackButtons *m_trackButtons;
+ CompositionView *m_segmentCanvas;
+ CompositionModel *m_compositionModel;
+ QDeferScrollView *m_trackButtonScroll;
+
+ bool m_showTrackLabels;
+ unsigned int m_canvasWidth;
+ unsigned int m_compositionRefreshStatusId;
+ bool m_playTracking;
+
+ typedef std::map<Segment *, unsigned int>
+ SegmentRefreshStatusIdMap;
+ SegmentRefreshStatusIdMap m_segmentsRefreshStatusIds;
+
+ double m_initialUnitsPerPixel;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackEditorIface.cpp b/src/gui/editors/segment/TrackEditorIface.cpp
new file mode 100644
index 0000000..8238c1d
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditorIface.cpp
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackEditorIface.h"
+
+#include <dcopobject.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/TrackEditorIface.h b/src/gui/editors/segment/TrackEditorIface.h
new file mode 100644
index 0000000..1637591
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditorIface.h
@@ -0,0 +1,55 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKEDITORIFACE_H_
+#define _RG_TRACKEDITORIFACE_H_
+
+#include <dcopobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * TrackEditor DCOP Interface
+ *
+ * @see TrackEditor
+ */
+class TrackEditorIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+k_dcop:
+ virtual void addSegment(int instrument, int start, unsigned int length) = 0;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackHeader.cpp b/src/gui/editors/segment/TrackHeader.cpp
new file mode 100644
index 0000000..d7ca6d3
--- /dev/null
+++ b/src/gui/editors/segment/TrackHeader.cpp
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackHeader.h"
+
+#include <qheader.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TrackHeader::~TrackHeader()
+{}
+
+void
+TrackHeader::paintEvent(QPaintEvent *e)
+{
+ QPainter p( this );
+ p.setPen( colorGroup().buttonText() );
+ int pos = (orientation() == Horizontal)
+ ? e->rect().left()
+ : e->rect().top();
+ int id = mapToIndex( sectionAt( pos + offset() ) );
+ if ( id < 0 )
+ if ( pos > 0 )
+ return ;
+ else
+ id = 0;
+ for ( int i = id; i < count(); i++ ) {
+ QRect r = sRect( i );
+ paintSection( &p, i, r );
+ if ( orientation() == Horizontal && r. right() >= e->rect().right() ||
+ orientation() == Vertical && r. bottom() >= e->rect().bottom() )
+ return ;
+ }
+
+}
+
+}
diff --git a/src/gui/editors/segment/TrackHeader.h b/src/gui/editors/segment/TrackHeader.h
new file mode 100644
index 0000000..fe404c3
--- /dev/null
+++ b/src/gui/editors/segment/TrackHeader.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKHEADER_H_
+#define _RG_TRACKHEADER_H_
+
+#include <qheader.h>
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class TrackHeader : public QHeader
+{
+
+public:
+ TrackHeader(int number,
+ QWidget *parent=0,
+ const char *name=0 ):
+ QHeader(number, parent, name) {;}
+ ~TrackHeader();
+
+protected:
+ virtual void paintEvent(QPaintEvent *pe);
+// void paintSection(QPainter * p, int index, QRect fr);
+// void paintSectionLabel (QPainter * p, int index, const QRect & fr);
+// QRect sRect (int index);
+
+private:
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackLabel.cpp b/src/gui/editors/segment/TrackLabel.cpp
new file mode 100644
index 0000000..90561d1
--- /dev/null
+++ b/src/gui/editors/segment/TrackLabel.cpp
@@ -0,0 +1,203 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackLabel.h"
+
+#include <klocale.h>
+#include "base/Track.h"
+#include <klineeditdlg.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <qvalidator.h>
+
+
+namespace Rosegarden
+{
+
+TrackLabel::TrackLabel(TrackId id,
+ int position,
+ QWidget *parent,
+ const char *name):
+ QWidgetStack(parent, name),
+ m_instrumentLabel(new QLabel(this)),
+ m_trackLabel(new QLabel(this)),
+ m_id(id),
+ m_position(position)
+{
+ QFont font;
+ font.setPointSize(font.pointSize() * 95 / 100);
+ if (font.pixelSize() > 14)
+ font.setPixelSize(14);
+ font.setBold(false);
+ m_instrumentLabel->setFont(font);
+ m_trackLabel->setFont(font);
+
+ addWidget(m_instrumentLabel, ShowInstrument);
+ addWidget(m_trackLabel, ShowTrack);
+ raiseWidget(ShowTrack);
+
+ m_instrumentLabel->setFrameShape(QFrame::NoFrame);
+ m_trackLabel->setFrameShape(QFrame::NoFrame);
+
+ m_pressTimer = new QTimer(this);
+
+ connect(m_pressTimer, SIGNAL(timeout()),
+ this, SIGNAL(changeToInstrumentList()));
+
+ QToolTip::add
+ (this, i18n("Click and hold with left mouse button to assign this Track to an Instrument."));
+
+}
+
+TrackLabel::~TrackLabel()
+{}
+
+void TrackLabel::setIndent(int i)
+{
+ m_instrumentLabel->setIndent(i);
+ m_trackLabel->setIndent(i);
+}
+
+void TrackLabel::setAlternativeLabel(const QString &label)
+{
+ // recover saved original
+ if (label.isEmpty()) {
+
+ if (!m_alternativeLabel.isEmpty())
+ m_instrumentLabel->setText(m_alternativeLabel);
+
+ // do nothing if we've got nothing to swap
+ return ;
+ }
+
+ // Store the current (first) label
+ //
+ if (m_alternativeLabel.isEmpty())
+ m_alternativeLabel = m_instrumentLabel->text();
+
+ // set new label
+ m_instrumentLabel->setText(label);
+}
+
+void TrackLabel::clearAlternativeLabel()
+{
+ m_alternativeLabel = "";
+}
+
+void TrackLabel::showLabel(InstrumentTrackLabels l)
+{
+ raiseWidget(l);
+}
+
+void
+TrackLabel::setSelected(bool on)
+{
+ if (on) {
+ m_selected = true;
+
+ m_instrumentLabel->setPaletteBackgroundColor(colorGroup().highlight());
+ m_instrumentLabel->setPaletteForegroundColor(colorGroup().highlightedText());
+ m_trackLabel->setPaletteBackgroundColor(colorGroup().highlight());
+ m_trackLabel->setPaletteForegroundColor(colorGroup().highlightedText());
+
+ } else {
+ m_selected = false;
+
+ m_instrumentLabel->setPaletteBackgroundColor(colorGroup().background());
+ m_trackLabel->setPaletteBackgroundColor(colorGroup().background());
+ m_instrumentLabel->setPaletteForegroundColor(colorGroup().text());
+ m_trackLabel->setPaletteForegroundColor(colorGroup().text());
+ }
+ if (visibleWidget())
+ visibleWidget()->update();
+}
+
+void
+TrackLabel::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton) {
+
+ emit clicked();
+ emit changeToInstrumentList();
+
+ } else if (e->button() == LeftButton) {
+
+ // start a timer on this hold
+ m_pressTimer->start(200, true); // 200ms, single shot
+ }
+}
+
+void
+TrackLabel::mouseReleaseEvent(QMouseEvent *e)
+{
+ // stop the timer if running
+ if (m_pressTimer->isActive())
+ m_pressTimer->stop();
+
+ if (e->button() == LeftButton) {
+ emit clicked();
+ }
+}
+
+void
+TrackLabel::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return ;
+
+ // Highlight this label alone and cheat using
+ // the clicked signal
+ //
+ emit clicked();
+
+ // Just in case we've got our timing wrong - reapply
+ // this label highlight
+ //
+ setSelected(true);
+
+ bool ok = false;
+
+ QRegExpValidator validator(QRegExp(".*"), this); // empty is OK
+
+ QString newText = KLineEditDlg::getText(i18n("Change track name"),
+ i18n("Enter new track name"),
+ m_trackLabel->text(),
+ &ok,
+ this,
+ &validator);
+
+ if ( ok )
+ emit renameTrack(newText, m_id);
+}
+
+}
+#include "TrackLabel.moc"
diff --git a/src/gui/editors/segment/TrackLabel.h b/src/gui/editors/segment/TrackLabel.h
new file mode 100644
index 0000000..e56d0e5
--- /dev/null
+++ b/src/gui/editors/segment/TrackLabel.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKLABEL_H_
+#define _RG_TRACKLABEL_H_
+
+#include "base/Track.h"
+#include <qstring.h>
+#include <qwidgetstack.h>
+
+
+class QWidget;
+class QTimer;
+class QMouseEvent;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Specialises QLabel to create in effect a toggleable and hence
+ * selectable label/label list. In conjunction with TrackButtons
+ * provides a framework for Track selection on the TrackCanvas.
+ */
+class TrackLabel : public QWidgetStack
+{
+Q_OBJECT
+public:
+
+ enum InstrumentTrackLabels
+ {
+ ShowTrack,
+ ShowInstrument,
+ ShowBoth
+ };
+
+ TrackLabel(TrackId id,
+ int position,
+ QWidget *parent,
+ const char *name=0);
+
+ ~TrackLabel();
+
+ // QLabel API delegation - applies on both labels
+ void setIndent(int);
+
+ QLabel* getInstrumentLabel() { return m_instrumentLabel; }
+ QLabel* getTrackLabel() { return m_trackLabel; }
+ void setAlternativeLabel(const QString &label);
+ void clearAlternativeLabel();
+ void showLabel(InstrumentTrackLabels);
+
+ // Encapsulates setting the label to highlighted or not
+ //
+ void setSelected(bool on);
+ bool isSelected() const { return m_selected; }
+
+ void setId(TrackId id) { m_id = id; }
+ TrackId getId() const { return m_id; }
+
+ int getPosition() const { return m_position; }
+ void setPosition(int position) { m_position = position; }
+
+signals:
+ void clicked();
+
+ // We emit this once we've renamed a track
+ //
+ void renameTrack(QString, TrackId);
+
+ void changeToInstrumentList();
+
+protected:
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ QLabel* getVisibleLabel();
+
+ //--------------- Data members ---------------------------------
+
+ QLabel *m_instrumentLabel;
+ QLabel *m_trackLabel;
+ QString m_alternativeLabel;
+
+ TrackId m_id;
+ int m_position;
+ bool m_selected;
+
+ QTimer *m_pressTimer;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackVUMeter.cpp b/src/gui/editors/segment/TrackVUMeter.cpp
new file mode 100644
index 0000000..a638ee7
--- /dev/null
+++ b/src/gui/editors/segment/TrackVUMeter.cpp
@@ -0,0 +1,77 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackVUMeter.h"
+
+#include "gui/widgets/VUMeter.h"
+#include <qfont.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TrackVUMeter::TrackVUMeter(QWidget *parent,
+ VUMeterType type,
+ int width,
+ int height,
+ int position,
+ const char *name):
+ VUMeter(parent, type, false, false, width, height, VUMeter::Horizontal, name),
+ m_position(position), m_textHeight(12)
+{
+ setAlignment(AlignCenter);
+
+ QFont font;
+ font.setPointSize(font.pointSize() * 95 / 100);
+ if (font.pointSize() > 14)
+ font.setPointSize(14);
+ font.setBold(false);
+ setFont(font);
+}
+
+void
+TrackVUMeter::meterStart()
+{
+ clear();
+ setMinimumHeight(m_originalHeight);
+ setMaximumHeight(m_originalHeight);
+ m_active = true;
+}
+
+void
+TrackVUMeter::meterStop()
+{
+ setMinimumHeight(m_textHeight);
+ setMaximumHeight(m_textHeight);
+ setText(QString("%1").arg(m_position + 1));
+ if (m_active) {
+ m_active = false;
+ update();
+ }
+}
+
+}
diff --git a/src/gui/editors/segment/TrackVUMeter.h b/src/gui/editors/segment/TrackVUMeter.h
new file mode 100644
index 0000000..26b8e4e
--- /dev/null
+++ b/src/gui/editors/segment/TrackVUMeter.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKVUMETER_H_
+#define _RG_TRACKVUMETER_H_
+
+#include "gui/widgets/VUMeter.h"
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class TrackVUMeter: public VUMeter
+{
+public:
+ TrackVUMeter(QWidget *parent = 0,
+ VUMeterType type = Plain,
+ int width = 0,
+ int height = 0,
+ int position = 0,
+ const char *name = 0);
+
+ int getPosition() const { return m_position; }
+
+protected:
+ virtual void meterStart();
+ virtual void meterStop();
+
+private:
+ int m_position;
+ int m_textHeight;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TriggerManagerItem.cpp b/src/gui/editors/segment/TriggerManagerItem.cpp
new file mode 100644
index 0000000..2e7402d
--- /dev/null
+++ b/src/gui/editors/segment/TriggerManagerItem.cpp
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "TriggerManagerItem.h"
+
+namespace Rosegarden {
+
+int
+TriggerManagerItem::compare(QListViewItem * i, int col, bool ascending) const
+{
+ TriggerManagerItem *ei =
+ dynamic_cast<TriggerManagerItem *>(i);
+
+ if (!ei) return QListViewItem::compare(i, col, ascending);
+
+ // col 0 -> index -- numeric compare
+ // col 1 -> ID -- numeric compare
+ // col 2 -> label -- default string compare
+ // col 3 -> duration -- raw duration compare
+ // col 4 -> base pitch -- pitch compare
+ // col 5 -> base velocity -- numeric compare
+ // col 6 -> usage count -- numeric compare
+ //
+ if (col == 2) {
+ return QListViewItem::compare(i, col, ascending);
+ } else if (col == 3) {
+ if (m_rawDuration < ei->getRawDuration()) return -1;
+ else if (ei->getRawDuration() < m_rawDuration) return 1;
+ else return 0;
+ } else if (col == 4) {
+ if (m_pitch < ei->getPitch()) return -1;
+ else if (ei->getPitch() < m_pitch) return 1;
+ else return 0;
+ } else {
+ return key(col, ascending).toInt() - i->key(col, ascending).toInt();
+ }
+}
+
+}
diff --git a/src/gui/editors/segment/TriggerManagerItem.h b/src/gui/editors/segment/TriggerManagerItem.h
new file mode 100644
index 0000000..c1eb95a
--- /dev/null
+++ b/src/gui/editors/segment/TriggerManagerItem.h
@@ -0,0 +1,72 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRIGGERMANAGERITEM_H_
+#define _RG_TRIGGERMANAGERITEM_H_
+
+#include <klistview.h>
+
+#include "base/Event.h"
+#include "base/TriggerSegment.h"
+
+namespace Rosegarden {
+
+class TriggerManagerItem : public QListViewItem
+{
+public:
+ TriggerManagerItem(QListView * parent, QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null):
+ QListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8) { ; }
+
+ virtual int compare(QListViewItem * i, int col, bool ascending) const;
+
+ void setRawDuration(timeT raw) { m_rawDuration = raw; }
+ timeT getRawDuration() const { return m_rawDuration; }
+
+ void setId(TriggerSegmentId id) { m_id = id; }
+ TriggerSegmentId getId() const { return m_id; }
+
+ void setUsage(int usage) { m_usage = usage; }
+ int getUsage() const { return m_usage; }
+
+ void setPitch(int pitch) { m_pitch = pitch; }
+ int getPitch() const { return m_pitch; }
+
+protected:
+ timeT m_rawDuration;
+ TriggerSegmentId m_id;
+ int m_usage;
+ int m_pitch;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TriggerSegmentManager.cpp b/src/gui/editors/segment/TriggerSegmentManager.cpp
new file mode 100644
index 0000000..3fb1732
--- /dev/null
+++ b/src/gui/editors/segment/TriggerSegmentManager.cpp
@@ -0,0 +1,576 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TriggerSegmentManager.h"
+#include "TriggerManagerItem.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "commands/segment/AddTriggerSegmentCommand.h"
+#include "commands/segment/DeleteTriggerSegmentCommand.h"
+#include "commands/segment/PasteToTriggerSegmentCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/general/MidiPitchLabel.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kconfig.h>
+#include <qaccel.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlistview.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+TriggerSegmentManager::TriggerSegmentManager(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KMainWindow(parent, "triggereditordialog"),
+ m_doc(doc),
+ m_modified(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Triggered Segments"));
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn("Index");
+ m_listView->addColumn(i18n("ID"));
+ m_listView->addColumn(i18n("Label"));
+ m_listView->addColumn(i18n("Duration"));
+ m_listView->addColumn(i18n("Base pitch"));
+ m_listView->addColumn(i18n("Base velocity"));
+ m_listView->addColumn(i18n("Triggers"));
+
+ // Align centrally
+ for (int i = 0; i < 2; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+ m_deleteAllButton = new QPushButton(i18n("Delete All"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Triggered Segment"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Triggered Segment"));
+
+ QToolTip::add
+ (m_deleteAllButton,
+ i18n("Delete All Triggered Segments"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Triggered Segment Manager"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addWidget(m_deleteAllButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ connect(m_closeButton, SIGNAL(released()),
+ SLOT(slotClose()));
+
+ connect(m_deleteAllButton, SIGNAL(released()),
+ SLOT(slotDeleteAll()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ connect(m_listView, SIGNAL(pressed(QListViewItem *)),
+ this, SLOT(slotItemClicked(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->setItemsRenameable(true);
+
+ initDialog();
+
+ setAutoSaveSettings(TriggerManagerConfigGroup, true);
+
+ m_accelerators = new QAccel(this);
+}
+
+TriggerSegmentManager::~TriggerSegmentManager()
+{
+ RG_DEBUG << "TriggerSegmentManager::~TriggerSegmentManager" << endl;
+
+ m_listView->saveLayout(kapp->config(), TriggerManagerConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+TriggerSegmentManager::initDialog()
+{
+ RG_DEBUG << "TriggerSegmentManager::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotUpdate()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotUpdate" << endl;
+
+ TriggerManagerItem *item;
+
+ m_listView->clear();
+
+ Composition &comp = m_doc->getComposition();
+
+ const Composition::triggersegmentcontainer &triggers =
+ comp.getTriggerSegments();
+
+ Composition::triggersegmentcontainerconstiterator it;
+
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ int i = 0;
+
+ for (it = triggers.begin(); it != triggers.end(); ++it) {
+
+ // duration is as of first usage, or 0
+
+ int uses = 0;
+ timeT first = 0;
+ std::set
+ <int> tracks;
+
+ CompositionTimeSliceAdapter tsa(&m_doc->getComposition());
+ for (CompositionTimeSliceAdapter::iterator ci = tsa.begin();
+ ci != tsa.end(); ++ci) {
+ if ((*ci)->has(BaseProperties::TRIGGER_SEGMENT_ID) &&
+ (*ci)->get
+ <Int>(BaseProperties::TRIGGER_SEGMENT_ID) == (long)(*it)->getId()) {
+ ++uses;
+ if (tracks.empty()) {
+ first = (*ci)->getAbsoluteTime();
+ }
+ tracks.insert(ci.getTrack());
+ }
+ }
+
+ timeT duration =
+ (*it)->getSegment()->getEndMarkerTime() -
+ (*it)->getSegment()->getStartTime();
+
+ QString timeString = makeDurationString
+ (first, duration, timeMode);
+
+ QString label = strtoqstr((*it)->getSegment()->getLabel());
+ if (label == "")
+ label = i18n("<no label>");
+
+ QString used = i18n("%1 on 1 track",
+ "%1 on %n tracks",
+ tracks.size()).arg(uses);
+
+ QString pitch = QString("%1 (%2)")
+ .arg(MidiPitchLabel((*it)->getBasePitch()).getQString())
+ .arg((*it)->getBasePitch());
+
+ QString velocity = QString("%1").arg((*it)->getBaseVelocity());
+
+ item = new TriggerManagerItem
+ (m_listView, QString("%1").arg(i + 1), QString("%1").arg((*it)->getId()),
+ label, timeString, pitch, velocity, used);
+
+ item->setRawDuration(duration);
+ item->setId((*it)->getId());
+ item->setUsage(uses);
+ item->setPitch((*it)->getBasePitch());
+
+ m_listView->insertItem(item);
+ ++i;
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item =
+ new TriggerManagerItem(m_listView, i18n("<none>"));
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+}
+
+void
+TriggerSegmentManager::slotDeleteAll()
+{
+ if (KMessageBox::warningContinueCancel(this, i18n("This will remove all triggered segments from the whole composition. Are you sure?")) != KMessageBox::Continue)
+ return ;
+
+ RG_DEBUG << "TriggerSegmentManager::slotDeleteAll" << endl;
+ KMacroCommand *command = new KMacroCommand(i18n("Remove all triggered segments"));
+
+ QListViewItem *it = m_listView->firstChild();
+
+ do {
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(it);
+
+ if (!item)
+ continue;
+
+ DeleteTriggerSegmentCommand *c =
+ new DeleteTriggerSegmentCommand(m_doc,
+ item->getId());
+ command->addCommand(c);
+
+ } while ((it = it->nextSibling()));
+
+ addCommandToHistory(command);
+}
+
+void
+TriggerSegmentManager::slotAdd()
+{
+ TimeDialog dialog(this, i18n("Trigger Segment Duration"),
+ &m_doc->getComposition(),
+ 0, 3840, false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ addCommandToHistory(new AddTriggerSegmentCommand
+ (m_doc, dialog.getTime(), 64));
+ }
+}
+
+void
+TriggerSegmentManager::slotDelete()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotDelete" << endl;
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(m_listView->currentItem());
+
+ if (!item)
+ return ;
+
+ if (item->getUsage() > 0) {
+ if (KMessageBox::warningContinueCancel(this, i18n("This triggered segment is used 1 time in the current composition. Are you sure you want to remove it?",
+ "This triggered segment is used %n times in the current composition. Are you sure you want to remove it?", item->getUsage())) != KMessageBox::Continue)
+ return ;
+ }
+
+ DeleteTriggerSegmentCommand *command =
+ new DeleteTriggerSegmentCommand(m_doc, item->getId());
+
+ addCommandToHistory(command);
+}
+
+void
+TriggerSegmentManager::slotPasteAsNew()
+{
+ Clipboard *clipboard = m_doc->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ KMessageBox::information(this, i18n("Clipboard is empty"));
+ return ;
+ }
+
+ addCommandToHistory(new PasteToTriggerSegmentCommand
+ (&m_doc->getComposition(),
+ clipboard,
+ "",
+ -1));
+}
+
+void
+TriggerSegmentManager::slotClose()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+TriggerSegmentManager::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ new KAction(i18n("Pa&ste as New Triggered Segment"), CTRL + SHIFT + Key_V, this,
+ SLOT(slotPasteAsNew()), actionCollection(),
+ "paste_to_trigger_segment");
+
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/time-musical.png");
+ QIconSet icon(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ createGUI("triggermanager.rc");
+}
+
+void
+TriggerSegmentManager::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+TriggerSegmentManager::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+TriggerSegmentManager::setModified(bool modified)
+{
+ RG_DEBUG << "TriggerSegmentManager::setModified(" << modified << ")" << endl;
+
+ m_modified = modified;
+}
+
+void
+TriggerSegmentManager::checkModified()
+{
+ RG_DEBUG << "TriggerSegmentManager::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+TriggerSegmentManager::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "TriggerSegmentManager::slotEdit" << endl;
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(i);
+
+ if (!item)
+ return ;
+
+ TriggerSegmentId id = item->getId();
+
+ RG_DEBUG << "id is " << id << endl;
+
+ emit editTriggerSegment(id);
+}
+
+void
+TriggerSegmentManager::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+TriggerSegmentManager::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_modified = false;
+
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotItemClicked(QListViewItem *item)
+{
+ RG_DEBUG << "TriggerSegmentManager::slotItemClicked" << endl;
+}
+
+QString
+TriggerSegmentManager::makeDurationString(timeT time,
+ timeT duration, int timeMode)
+{
+ //!!! duplication with EventView::makeDurationString -- merge somewhere?
+
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ m_doc->getComposition().getMusicalTimeForDuration
+ (time, duration, bar, beat, fraction, remainder);
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ m_doc->getComposition().getRealTimeDifference
+ (time, time + duration);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(duration);
+ }
+}
+
+void
+TriggerSegmentManager::slotMusicalTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 0);
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotRealTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 1);
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotRawTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 2);
+ slotUpdate();
+}
+
+}
+#include "TriggerSegmentManager.moc"
diff --git a/src/gui/editors/segment/TriggerSegmentManager.h b/src/gui/editors/segment/TriggerSegmentManager.h
new file mode 100644
index 0000000..2de6488
--- /dev/null
+++ b/src/gui/editors/segment/TriggerSegmentManager.h
@@ -0,0 +1,116 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRIGGERSEGMENTMANAGER_H_
+#define _RG_TRIGGERSEGMENTMANAGER_H_
+
+#include <kmainwindow.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QCloseEvent;
+class QAccel;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class TriggerSegmentManager : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ TriggerSegmentManager(QWidget *parent,
+ RosegardenGUIDoc *doc);
+ ~TriggerSegmentManager();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+public slots:
+ void slotUpdate();
+
+ void slotAdd();
+ void slotDelete();
+ void slotDeleteAll();
+ void slotClose();
+ void slotEdit(QListViewItem *);
+ void slotItemClicked(QListViewItem *);
+ void slotPasteAsNew();
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+signals:
+ void editTriggerSegment(int);
+ void closing();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+ QString makeDurationString(timeT startTime,
+ timeT duration, int timeMode);
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc *m_doc;
+
+ QPushButton *m_closeButton;
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+ QPushButton *m_deleteAllButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ QAccel *m_accelerators;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
new file mode 100644
index 0000000..1b982dc
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
@@ -0,0 +1,316 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "AudioPreviewPainter.h"
+
+#include "CompositionModelImpl.h"
+#include "CompositionColourCache.h"
+#include "base/Composition.h"
+#include "base/Track.h"
+#include "base/AudioLevel.h"
+#include "base/Studio.h"
+
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+
+#include <qimage.h>
+#include <qapplication.h>
+
+#include <kapp.h>
+#include <kconfig.h>
+
+namespace Rosegarden {
+
+AudioPreviewPainter::AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment)
+ : m_model(model),
+ m_apData(apData),
+ m_composition(composition),
+ m_segment(segment),
+ m_rect(model.computeSegmentRect(*(segment))),
+ m_image(1, 1, 8, 4),
+ m_defaultCol(CompositionColourCache::getInstance()->SegmentAudioPreview),
+ m_height(model.grid().getYSnap()/2)
+{
+ int pixWidth = std::min(m_rect.getBaseWidth(), tileWidth());
+
+ m_image = QImage(pixWidth, m_rect.height(), 8, 4);
+ m_image.setAlphaBuffer(true);
+
+ m_penWidth = (std::max(1U, m_rect.getPen().width()) * 2);
+ m_halfRectHeight = m_model.grid().getYSnap()/2 - m_penWidth / 2 - 2;
+}
+
+int AudioPreviewPainter::tileWidth()
+{
+ static int tw = -1;
+ if (tw == -1) tw = QApplication::desktop()->width();
+ return tw;
+}
+
+void AudioPreviewPainter::paintPreviewImage()
+{
+ const std::vector<float>& values = m_apData->getValues();
+
+ if (values.size() == 0)
+ return;
+
+ float gain[2] = { 1.0, 1.0 };
+ int instrumentChannels = 2;
+ TrackId trackId = m_segment->getTrack();
+ Track *track = m_model.getComposition().getTrackById(trackId);
+ if (track) {
+ Instrument *instrument = m_model.getStudio().getInstrumentById(track->getInstrument());
+ if (instrument) {
+ float level = AudioLevel::dB_to_multiplier(instrument->getLevel());
+ float pan = instrument->getPan() - 100.0;
+ gain[0] = level * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
+ gain[1] = level * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+ instrumentChannels = instrument->getAudioChannels();
+ }
+ }
+
+ bool showMinima = m_apData->showsMinima();
+ unsigned int channels = m_apData->getChannels();
+ if (channels == 0) {
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage : problem with audio file for segment "
+ << m_segment->getLabel().c_str() << endl;
+ return;
+ }
+
+ int samplePoints = values.size() / (channels * (showMinima ? 2 : 1));
+ float h1, h2, l1 = 0, l2 = 0;
+ double sampleScaleFactor = samplePoints / double(m_rect.getBaseWidth());
+ m_sliceNb = 0;
+
+ m_image.fill(0);
+
+ int centre = m_image.height() / 2;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage width = " << m_rect.getBaseWidth() << ", height = " << m_rect.height() << ", halfRectHeight = " << m_halfRectHeight << endl;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage: channels = " << channels << ", gain left = " << gain[0] << ", right = " << gain[1] << endl;
+
+ double audioDuration = double(m_segment->getAudioEndTime().sec) +
+ double(m_segment->getAudioEndTime().nsec) / 1000000000.0;
+
+ // We need to take each pixel value and map it onto a point within
+ // the preview. We have samplePoints preview points in a known
+ // duration of audioDuration. Thus each point spans a real time
+ // of audioDuration / samplePoints. We need to convert the
+ // accumulated real time back into musical time, and map this
+ // proportionately across the segment width.
+
+ RealTime startRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getStartTime());
+ double startTime = double(startRT.sec) + double(startRT.nsec) / 1000000000.0;
+
+ RealTime endRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getEndMarkerTime());
+ double endTime = double(endRT.sec) + double(endRT.nsec) / 1000000000.0;
+
+ bool haveTempoChange = false;
+
+ int finalTempoChangeNumber =
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getEndMarkerTime());
+
+ if ((finalTempoChangeNumber >= 0) &&
+
+ (finalTempoChangeNumber >
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getStartTime()))) {
+
+ haveTempoChange = true;
+ }
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ bool meterLevels = (config->readUnsignedNumEntry("audiopreviewstyle", 1)
+ == 1);
+
+ for (int i = 0; i < m_rect.getBaseWidth(); ++i) {
+
+ // i is the x coordinate within the rectangle. We need to
+ // calculate the position within the audio preview from which
+ // to draw the peak for this coordinate. It's possible there
+ // may be more than one, in which case we need to find the
+ // peak of all of them.
+
+ int position = 0;
+
+ if (haveTempoChange) {
+
+ // First find the time corresponding to this i.
+ timeT musicalTime =
+ m_model.grid().getRulerScale()->getTimeForX(m_rect.x() + i);
+ RealTime realTime =
+ m_model.getComposition().getElapsedRealTime(musicalTime);
+
+ double time = double(realTime.sec) +
+ double(realTime.nsec) / 1000000000.0;
+ double offset = time - startTime;
+
+ if (endTime > startTime) {
+ position = offset * m_rect.getBaseWidth() / (endTime - startTime);
+ position = int(channels * position);
+ }
+
+ } else {
+
+ position = int(channels * i * sampleScaleFactor);
+ }
+
+ if (position < 0) continue;
+
+ if (position >= values.size() - channels) {
+ finalizeCurrentSlice();
+ break;
+ }
+
+ if (channels == 1) {
+
+ h1 = values[position++];
+ h2 = h1;
+
+ if (showMinima) {
+ l1 = values[position++];
+ l2 = l1;
+ }
+ } else {
+
+ h1 = values[position++];
+ if (showMinima) l1 = values[position++];
+
+ h2 = values[position++];
+ if (showMinima) l2 = values[position++];
+
+ }
+
+ if (instrumentChannels == 1 && channels == 2) {
+ h1 = h2 = (h1 + h2) / 2;
+ l1 = l2 = (l1 + l2) / 2;
+ }
+
+ h1 *= gain[0];
+ h2 *= gain[1];
+
+ l1 *= gain[0];
+ l2 *= gain[1];
+
+ int width = 1;
+ int pixel;
+
+ // h1 left, h2 right
+ if (h1 >= 1.0) { h1 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ int h;
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h1, m_height);
+ } else {
+ h = h1 * m_height;
+ }
+ if (h <= 0) h = 1;
+ if (h > m_halfRectHeight) h = m_halfRectHeight;
+
+ int rectX = i % tileWidth();
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre - py, pixel);
+ }
+
+ if (h2 >= 1.0) { h2 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h2, m_height);
+ } else {
+ h = h2 * m_height;
+ }
+ if (h < 0) h = 0;
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre + py, pixel);
+ }
+
+ if (((i+1) % tileWidth()) == 0 || i == (m_rect.getBaseWidth() - 1)) {
+ finalizeCurrentSlice();
+ }
+ }
+
+/* Auto-fade not yet implemented.
+
+ if (m_segment->isAutoFading()) {
+
+ Composition &comp = m_model.getComposition();
+
+ int audioFadeInEnd = int(
+ m_model.grid().getRulerScale()->getXForTime(comp.
+ getElapsedTimeForRealTime(m_segment->getFadeInTime()) +
+ m_segment->getStartTime()) -
+ m_model.grid().getRulerScale()->getXForTime(m_segment->getStartTime()));
+
+ m_p.setPen(Qt::blue);
+ m_p.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ m_pb.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ }
+
+ m_p.end();
+ m_pb.end();
+*/
+}
+
+void AudioPreviewPainter::finalizeCurrentSlice()
+{
+// RG_DEBUG << "AudioPreviewPainter::finalizeCurrentSlice : copying pixmap to image at " << m_sliceNb * tileWidth() << endl;
+
+ // transparent background
+ m_image.setColor(0, qRgba(255, 255, 255, 0));
+
+ // foreground from computeSegmentPreviewColor
+ QColor c = m_model.computeSegmentPreviewColor(m_segment);
+ QRgb rgba = qRgba(c.red(), c.green(), c.blue(), 255);
+ m_image.setColor(1, rgba);
+
+ // red for clipping
+ m_image.setColor(2, qRgba(255, 0, 0, 255));
+
+ m_previewPixmaps.push_back(m_image.copy());
+
+ m_image.fill(0);
+
+ ++m_sliceNb;
+}
+
+PixmapArray AudioPreviewPainter::getPreviewImage()
+{
+ return m_previewPixmaps;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
new file mode 100644
index 0000000..b3c1cac
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
@@ -0,0 +1,79 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWPAINTER_H_
+#define _RG_AUDIOPREVIEWPAINTER_H_
+
+#include "CompositionModel.h"
+
+#include <qpainter.h>
+#include <qcolor.h>
+
+namespace Rosegarden {
+
+class CompositionModelImpl;
+class Composition;
+class Segment;
+class CompositionRect;
+
+class AudioPreviewPainter {
+public:
+ AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment);
+
+ void paintPreviewImage();
+ PixmapArray getPreviewImage();
+ const CompositionRect& getSegmentRect() { return m_rect; }
+
+ static int tileWidth();
+
+protected:
+ void finalizeCurrentSlice();
+
+ //--------------- Data members ---------------------------------
+ CompositionModelImpl& m_model;
+ CompositionModel::AudioPreviewData* m_apData;
+ const Composition &m_composition;
+ const Segment* m_segment;
+ CompositionRect m_rect;
+
+ QImage m_image;
+ PixmapArray m_previewPixmaps;
+
+ QPainter m_p;
+ QPainter m_pb;
+ QColor m_defaultCol;
+ int m_penWidth;
+ int m_height;
+ int m_halfRectHeight;
+ int m_sliceNb;
+
+};
+
+}
+
+#endif
+
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
new file mode 100644
index 0000000..ae64134
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
@@ -0,0 +1,267 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "AudioPreviewThread.h"
+
+#include "base/RealTime.h"
+#include "sound/AudioFileManager.h"
+#include "sound/PeakFileManager.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qobject.h>
+#include <qthread.h>
+
+
+namespace Rosegarden
+{
+
+AudioPreviewThread::AudioPreviewThread(AudioFileManager *manager) :
+ m_manager(manager),
+ m_nextToken(0),
+ m_exiting(false),
+ m_emptyQueueListener(0)
+{}
+
+void
+AudioPreviewThread::run()
+{
+ bool emptyQueueSignalled = false;
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::run entering\n";
+#endif
+
+ while (!m_exiting) {
+
+ if (m_queue.empty()) {
+ if (m_emptyQueueListener && !emptyQueueSignalled) {
+ QApplication::postEvent(m_emptyQueueListener,
+ new QCustomEvent(AudioPreviewQueueEmpty, 0));
+ emptyQueueSignalled = true;
+ }
+
+ usleep(300000);
+ } else {
+ process();
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::run exiting\n";
+#endif
+}
+
+void
+AudioPreviewThread::finish()
+{
+ m_exiting = true;
+}
+
+bool
+AudioPreviewThread::process()
+{
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process()\n";
+#endif
+
+ if (!m_queue.empty()) {
+
+ int failed = 0;
+ int inQueue = 0;
+ //int count = 0;
+
+ m_mutex.lock();
+
+ // process 1st request and leave
+ inQueue = m_queue.size();
+ RequestQueue::iterator i = m_queue.begin();
+
+ // i->first is width, which we use only to provide an ordering to
+ // ensure we do smaller previews first. We don't use it here.
+
+ RequestRec &rec = i->second;
+ int token = rec.first;
+ Request req = rec.second;
+ m_mutex.unlock();
+
+ std::vector<float> results;
+
+ try {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() file id " << req.audioFileId << std::endl;
+#endif
+
+ // Requires thread-safe AudioFileManager::getPreview
+ results = m_manager->getPreview(req.audioFileId,
+ req.audioStartTime,
+ req.audioEndTime,
+ req.width,
+ req.showMinima);
+ } catch (AudioFileManager::BadAudioPathException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad audio path: " << e.getMessage() << std::endl;
+#endif
+
+ // OK, we hope this just means we're still recording -- so
+ // leave this one in the queue
+ ++failed;
+
+ } catch (PeakFileManager::BadPeakFileException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad peak file: " << e.getMessage() << std::endl;
+#endif
+
+ // As above
+ ++failed;
+ }
+
+ m_mutex.lock();
+
+ // We need to check that the token is still in the queue
+ // (i.e. hasn't been cancelled). Otherwise we shouldn't notify
+
+ bool found = false;
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ found = true;
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ if (found) {
+ unsigned int channels =
+ m_manager->getAudioFile(req.audioFileId)->getChannels();
+ m_results[token] = ResultsPair(channels, results);
+ QObject *notify = req.notify;
+ QApplication::postEvent
+ (notify,
+ new QCustomEvent(AudioPreviewReady, (void *)token));
+ }
+
+ m_mutex.unlock();
+
+ if (failed > 0 && failed == inQueue) {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return true\n";
+#endif
+
+ return true; // delay and try again
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return false\n";
+#endif
+
+ return false;
+}
+
+int
+AudioPreviewThread::requestPreview(const Request &request)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview for file id " << request.audioFileId << ", start " << request.audioStartTime << ", end " << request.audioEndTime << ", width " << request.width << ", notify " << request.notify << std::endl;
+#endif
+ /*!!!
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.second.notify == request.notify) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+ */
+ int token = m_nextToken;
+ m_queue.insert(RequestQueue::value_type(request.width,
+ RequestRec(token, request)));
+ ++m_nextToken;
+ m_mutex.unlock();
+
+ // if (!running()) start();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview : thread running : " << running()
+ << " - thread finished : " << finished() << std::endl;
+
+ std::cerr << "AudioPreviewThread::requestPreview - token = " << token << std::endl;
+#endif
+
+ return token;
+}
+
+void
+AudioPreviewThread::cancelPreview(int token)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::cancelPreview for token " << token << std::endl;
+#endif
+
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ m_mutex.unlock();
+}
+
+void
+AudioPreviewThread::getPreview(int token, unsigned int &channels,
+ std::vector<float> &values)
+{
+ m_mutex.lock();
+
+ values.clear();
+ if (m_results.find(token) == m_results.end()) {
+ channels = 0;
+ m_mutex.unlock();
+ return ;
+ }
+
+ channels = m_results[token].first;
+ values = m_results[token].second;
+ m_results.erase(m_results.find(token));
+
+ m_mutex.unlock();
+
+ return ;
+}
+
+const QEvent::Type AudioPreviewThread::AudioPreviewReady = QEvent::Type(QEvent::User + 1);
+const QEvent::Type AudioPreviewThread::AudioPreviewQueueEmpty = QEvent::Type(QEvent::User + 2);
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
new file mode 100644
index 0000000..ae3ac81
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
@@ -0,0 +1,99 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWTHREAD_H_
+#define _RG_AUDIOPREVIEWTHREAD_H_
+
+#include "base/RealTime.h"
+#include <map>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <utility>
+#include <vector>
+
+
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class AudioFileManager;
+
+
+class AudioPreviewThread : public QThread
+{
+public:
+ AudioPreviewThread(AudioFileManager *manager);
+
+ virtual void run();
+ virtual void finish();
+
+ struct Request {
+ int audioFileId;
+ RealTime audioStartTime;
+ RealTime audioEndTime;
+ int width;
+ bool showMinima;
+ QObject *notify;
+ };
+
+ virtual int requestPreview(const Request &request);
+ virtual void cancelPreview(int token);
+ virtual void getPreview(int token, unsigned int &channels,
+ std::vector<float> &values);
+
+ void setEmptyQueueListener(QObject* o) { m_emptyQueueListener = o; }
+
+ static const QEvent::Type AudioPreviewReady;
+ static const QEvent::Type AudioPreviewQueueEmpty;
+
+
+protected:
+ virtual bool process();
+
+
+ AudioFileManager *m_manager;
+ int m_nextToken;
+ bool m_exiting;
+
+ QObject* m_emptyQueueListener;
+
+ typedef std::pair<int, Request> RequestRec;
+ typedef std::multimap<int, RequestRec> RequestQueue;
+ RequestQueue m_queue;
+
+ typedef std::pair<unsigned int, std::vector<float> > ResultsPair;
+ typedef std::map<int, ResultsPair> ResultsQueue;
+ ResultsQueue m_results;
+
+ QMutex m_mutex;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
new file mode 100644
index 0000000..76497b9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
@@ -0,0 +1,149 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "AudioPreviewUpdater.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "CompositionModelImpl.h"
+#include <qevent.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+static int apuExtantCount = 0;
+
+AudioPreviewUpdater::AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition& c, const Segment* s,
+ const QRect& r,
+ CompositionModelImpl* parent)
+ : QObject(parent),
+ m_thread(thread),
+ m_composition(c),
+ m_segment(s),
+ m_rect(r),
+ m_showMinima(false),
+ m_channels(0),
+ m_previewToken( -1)
+{
+ ++apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::AudioPreviewUpdater " << this << " (now " << apuExtantCount << " extant)" << endl;
+}
+
+AudioPreviewUpdater::~AudioPreviewUpdater()
+{
+ --apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::~AudioPreviewUpdater on " << this << " ( token " << m_previewToken << ") (now " << apuExtantCount << " extant)" << endl;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+}
+
+void AudioPreviewUpdater::update()
+{
+ // Get sample start and end times and work out duration
+ //
+ RealTime audioStartTime = m_segment->getAudioStartTime();
+ RealTime audioEndTime = audioStartTime +
+ m_composition.getElapsedRealTime(m_segment->getEndMarkerTime()) -
+ m_composition.getElapsedRealTime(m_segment->getStartTime()) ;
+
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::update() - for file id "
+ << m_segment->getAudioFileId() << " requesting values - thread running : "
+ << m_thread.running() << " - thread finished : " << m_thread.finished() << endl;
+
+ AudioPreviewThread::Request request;
+ request.audioFileId = m_segment->getAudioFileId();
+ request.audioStartTime = audioStartTime;
+ request.audioEndTime = audioEndTime;
+ request.width = m_rect.width();
+ request.showMinima = m_showMinima;
+ request.notify = this;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = m_thread.requestPreview(request);
+ if (!m_thread.running())
+ m_thread.start();
+}
+
+void AudioPreviewUpdater::cancel()
+{
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = -1;
+}
+
+bool AudioPreviewUpdater::event(QEvent *e)
+{
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::event (" << e << ")" << endl;
+
+ if (e->type() == AudioPreviewThread::AudioPreviewReady) {
+ QCustomEvent *ev = dynamic_cast<QCustomEvent *>(e);
+ if (ev) {
+ intptr_t token = (intptr_t)ev->data();
+ m_channels = 0; // to be filled as getPreview return value
+
+ RG_DEBUG << "AudioPreviewUpdater::token " << token << ", my token " << m_previewToken << endl;
+
+ if (m_previewToken >= 0 && token >= m_previewToken) {
+
+ m_previewToken = -1;
+ m_thread.getPreview(token, m_channels, m_values);
+
+ if (m_channels == 0) {
+ RG_DEBUG << "AudioPreviewUpdater: failed to find preview!\n";
+ } else {
+
+ RG_DEBUG << "AudioPreviewUpdater: got correct preview (" << m_channels
+ << " channels, " << m_values.size() << " samples)\n";
+ }
+
+ emit audioPreviewComplete(this);
+
+ } else {
+
+ // this one is out of date already
+ std::vector<float> tmp;
+ unsigned int tmpChannels;
+ m_thread.getPreview(token, tmpChannels, tmp);
+
+ RG_DEBUG << "AudioPreviewUpdater: got obsolete preview (" << tmpChannels
+ << " channels, " << tmp.size() << " samples)\n";
+ }
+
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+}
+#include "AudioPreviewUpdater.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
new file mode 100644
index 0000000..ffc97c9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
@@ -0,0 +1,90 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWUPDATER_H_
+#define _RG_AUDIOPREVIEWUPDATER_H_
+
+#include <qobject.h>
+#include <qrect.h>
+#include <vector>
+
+
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class CompositionModelImpl;
+class Composition;
+class AudioPreviewThread;
+
+
+class AudioPreviewUpdater : public QObject
+{
+ Q_OBJECT
+
+public:
+ AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition &composition,
+ const Segment *segment,
+ const QRect &displayExtent,
+ CompositionModelImpl *parent);
+ ~AudioPreviewUpdater();
+
+ void update();
+ void cancel();
+
+ QRect getDisplayExtent() const { return m_rect; }
+ void setDisplayExtent(const QRect &rect) { m_rect = rect; }
+
+ const Segment *getSegment() const { return m_segment; }
+
+ const std::vector<float> &getComputedValues(unsigned int &channels) const
+ { channels = m_channels; return m_values; }
+
+signals:
+ void audioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ virtual bool event(QEvent*);
+
+ AudioPreviewThread& m_thread;
+
+ const Composition& m_composition;
+ const Segment* m_segment;
+ QRect m_rect;
+ bool m_showMinima;
+ unsigned int m_channels;
+ std::vector<float> m_values;
+
+ intptr_t m_previewToken;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
new file mode 100644
index 0000000..b36d6e0
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
@@ -0,0 +1,62 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionColourCache.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+void CompositionColourCache::init()
+{
+ SegmentCanvas = GUIPalette::getColour(GUIPalette::SegmentCanvas);
+ SegmentAudioPreview = GUIPalette::getColour(GUIPalette::SegmentAudioPreview);
+ SegmentInternalPreview = GUIPalette::getColour(GUIPalette::SegmentInternalPreview);
+ SegmentLabel = GUIPalette::getColour(GUIPalette::SegmentLabel);
+ SegmentBorder = GUIPalette::getColour(GUIPalette::SegmentBorder);
+ RepeatSegmentBorder = GUIPalette::getColour(GUIPalette::RepeatSegmentBorder);
+ RecordingSegmentBorder = GUIPalette::getColour(GUIPalette::RecordingSegmentBorder);
+ RecordingAudioSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingAudioSegmentBlock);
+ RecordingInternalSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingInternalSegmentBlock);
+ RotaryFloatBackground = GUIPalette::getColour(GUIPalette::RotaryFloatBackground);
+ RotaryFloatForeground = GUIPalette::getColour(GUIPalette::RotaryFloatForeground);
+
+}
+
+CompositionColourCache* CompositionColourCache::getInstance()
+{
+ if (!m_instance) {
+ m_instance = new CompositionColourCache();
+ }
+
+ return m_instance;
+}
+
+CompositionColourCache* CompositionColourCache::m_instance = 0;
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
new file mode 100644
index 0000000..32d4719
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
@@ -0,0 +1,69 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONCOLOURCACHE_H_
+#define _RG_COMPOSITIONCOLOURCACHE_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class CompositionColourCache
+{
+public:
+ static CompositionColourCache* getInstance();
+
+ void init();
+
+ QColor SegmentCanvas;
+ QColor SegmentAudioPreview;
+ QColor SegmentInternalPreview;
+ QColor SegmentLabel;
+ QColor SegmentBorder;
+ QColor RepeatSegmentBorder;
+ QColor RecordingSegmentBorder;
+ QColor RecordingAudioSegmentBlock;
+ QColor RecordingInternalSegmentBlock;
+ QColor Pointer;
+ QColor MovementGuide;
+ QColor RotaryFloatBackground;
+ QColor RotaryFloatForeground;
+
+protected:
+ CompositionColourCache() { init(); }
+ static CompositionColourCache* m_instance;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
new file mode 100644
index 0000000..798178a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionItem.h"
+
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.h b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
new file mode 100644
index 0000000..b5e749b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
@@ -0,0 +1,67 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEM_H_
+#define _RG_COMPOSITIONITEM_H_
+
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+class _CompositionItem : public QObject {
+public:
+ virtual bool isRepeating() const = 0;
+ virtual QRect rect() const = 0;
+ virtual void moveBy(int x, int y) = 0;
+ virtual void moveTo(int x, int y) = 0;
+ virtual void setX(int x) = 0;
+ virtual void setY(int y) = 0;
+ virtual void setZ(unsigned int z) = 0;
+ virtual int x() = 0;
+ virtual int y() = 0;
+ virtual unsigned int z() = 0;
+ virtual void setWidth(int w) = 0;
+
+ // used by itemcontainer
+ virtual long hashKey() = 0;
+
+ QRect savedRect() const { return m_savedRect; }
+ void saveRect() const { m_savedRect = rect(); }
+
+protected:
+ mutable QRect m_savedRect;
+};
+
+typedef QGuardedPtr<_CompositionItem> CompositionItem;
+bool operator<(const CompositionItem&, const CompositionItem&);
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
new file mode 100644
index 0000000..e1705cd
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
@@ -0,0 +1,150 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+
+#include "CompositionItemHelper.h"
+
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "misc/Debug.h"
+#include "CompositionModel.h"
+#include "CompositionItemImpl.h"
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+namespace Rosegarden
+{
+
+timeT CompositionItemHelper::getStartTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ // t = std::max(grid.snapX(item->rect().x()), 0L); - wrong, we can have negative start times,
+ // and if we do this we 'crop' segments when they are moved before the start of the composition
+ t = grid.snapX(item->rect().x());
+
+// RG_DEBUG << "CompositionItemHelper::getStartTime(): item is repeating : " << item->isRepeating()
+// << " - startTime = " << t
+// << endl;
+ }
+
+ return t;
+}
+
+timeT CompositionItemHelper::getEndTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ QRect itemRect = item->rect();
+
+ t = std::max(grid.snapX(itemRect.x() + itemRect.width()), 0L);
+
+// RG_DEBUG << "CompositionItemHelper::getEndTime() : rect width = "
+// << itemRect.width()
+// << " - item is repeating : " << item->isRepeating()
+// << " - endTime = " << t
+// << endl;
+
+ }
+
+ return t;
+}
+
+void CompositionItemHelper::setStartTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+
+ RG_DEBUG << "CompositionItemHelper::setStartTime() time = " << time
+ << " -> x = " << x << endl;
+
+ int curX = item->rect().x();
+ item->setX(x);
+ if (item->isRepeating()) {
+ int deltaX = curX - x;
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ int curW = sr.getBaseWidth();
+ sr.setBaseWidth(curW + deltaX);
+ }
+
+ }
+
+}
+
+void CompositionItemHelper::setEndTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+ QRect r = item->rect();
+ QPoint topRight = r.topRight();
+ topRight.setX(x);
+ r.setTopRight(topRight);
+ item->setWidth(r.width());
+
+ if (item->isRepeating()) {
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ sr.setBaseWidth(r.width());
+ }
+ }
+}
+
+int CompositionItemHelper::getTrackPos(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ return grid.getYBin(item->rect().y());
+}
+
+Rosegarden::Segment* CompositionItemHelper::getSegment(CompositionItem item)
+{
+ return (dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item))->getSegment();
+}
+
+CompositionItem CompositionItemHelper::makeCompositionItem(Rosegarden::Segment* segment)
+{
+ return CompositionItem(new CompositionItemImpl(*segment, QRect()));
+}
+
+CompositionItem CompositionItemHelper::findSiblingCompositionItem(const CompositionModel::itemcontainer& items,
+ const CompositionItem& referenceItem)
+{
+ CompositionModel::itemcontainer::const_iterator it;
+ Rosegarden::Segment* currentSegment = CompositionItemHelper::getSegment(referenceItem);
+
+ for (it = items.begin(); it != items.end(); it++) {
+ CompositionItem item = *it;
+ Rosegarden::Segment* segment = CompositionItemHelper::getSegment(item);
+ if (segment == currentSegment) {
+ return item;
+ }
+ }
+
+ return referenceItem;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
new file mode 100644
index 0000000..1b3ad95
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEMHELPER_H_
+#define _RG_COMPOSITIONITEMHELPER_H_
+
+#include "CompositionModel.h"
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class Segment;
+
+
+class CompositionItemHelper {
+public:
+ static timeT getStartTime(const CompositionItem&, const SnapGrid&);
+ static timeT getEndTime(const CompositionItem&, const SnapGrid&);
+ static int getTrackPos(const CompositionItem&, const SnapGrid&);
+ static void setStartTime(CompositionItem&, timeT, const SnapGrid&);
+ static void setEndTime(CompositionItem&, timeT, const SnapGrid&);
+ static Segment* getSegment(CompositionItem);
+ static CompositionItem makeCompositionItem(Segment*);
+ /**
+ * return the CompositionItem in the model which references the same segment as referenceItem
+ */
+ static CompositionItem findSiblingCompositionItem(const CompositionModel::itemcontainer& items, const CompositionItem& referenceItem);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
new file mode 100644
index 0000000..5508ad2
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionItemImpl.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "CompositionRect.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+CompositionItemImpl::CompositionItemImpl(Segment& s, const CompositionRect& rect)
+ : m_segment(s),
+ m_rect(rect),
+ m_z(0)
+{}
+
+QRect CompositionItemImpl::rect() const
+{
+ QRect res = m_rect;
+ if (m_rect.isRepeating()) {
+ CompositionRect::repeatmarks repeatMarks = m_rect.getRepeatMarks();
+ int neww = m_rect.getBaseWidth();
+
+ // RG_DEBUG << "CompositionItemImpl::rect() - width = "
+ // << m_rect.width() << " - base w = " << neww << endl;
+ res.setWidth(neww);
+ } else {
+ // RG_DEBUG << "CompositionItemImpl::rect() m_rect not repeating\n";
+ }
+
+
+ return res;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
new file mode 100644
index 0000000..b5b3ef7
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
@@ -0,0 +1,74 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEMIMPL_H_
+#define _RG_COMPOSITIONITEMIMPL_H_
+
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+#include <qrect.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class CompositionItemImpl : public _CompositionItem {
+public:
+ CompositionItemImpl(Segment& s, const CompositionRect&);
+ virtual bool isRepeating() const { return m_rect.isRepeating(); }
+ virtual QRect rect() const;
+ virtual void moveBy(int x, int y) { m_rect.moveBy(x, y); }
+ virtual void moveTo(int x, int y) { m_rect.setRect(x, y, m_rect.width(), m_rect.height()); }
+ virtual void setX(int x) { m_rect.setX(x); }
+ virtual void setY(int y) { m_rect.setY(y); }
+ virtual void setZ(unsigned int z) { m_z = z; }
+ virtual int x() { return m_rect.x(); }
+ virtual int y() { return m_rect.y(); }
+ virtual unsigned int z() { return m_z; }
+ virtual void setWidth(int w) { m_rect.setWidth(w); }
+ // use segment address as hash key
+ virtual long hashKey() { return (long)getSegment(); }
+
+ Segment* getSegment() { return &m_segment; }
+ const Segment* getSegment() const { return &m_segment; }
+ CompositionRect& getCompRect() { return m_rect; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ Segment& m_segment;
+ CompositionRect m_rect;
+ unsigned int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
new file mode 100644
index 0000000..9701c8a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionModel.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include "CompositionItemHelper.h"
+
+
+namespace Rosegarden
+{
+
+bool CompositionModel::CompositionItemCompare::operator()(const CompositionItem &c1, const CompositionItem &c2) const
+{
+ return CompositionItemHelper::getSegment(c1) < CompositionItemHelper::getSegment(c2);
+}
+
+}
+#include "CompositionModel.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.h b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
new file mode 100644
index 0000000..beafc2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
@@ -0,0 +1,179 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONMODEL_H_
+#define _RG_COMPOSITIONMODEL_H_
+
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <set>
+#include <qcolor.h>
+#include <qobject.h>
+#include <qimage.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+typedef std::vector<QImage> PixmapArray;
+
+
+class CompositionModel : public QObject, public CompositionObserver, public SegmentObserver
+{
+ Q_OBJECT
+public:
+
+ struct CompositionItemCompare {
+ bool operator()(const CompositionItem &c1, const CompositionItem &c2) const;
+ };
+
+ typedef std::vector<QRect> rectlist;
+ typedef std::vector<int> heightlist;
+ typedef std::vector<CompositionRect> rectcontainer;
+ typedef std::set<CompositionItem, CompositionItemCompare> itemcontainer;
+
+ struct AudioPreviewDrawDataItem {
+ AudioPreviewDrawDataItem(PixmapArray p, QPoint bp, QRect r) :
+ pixmap(p), basePoint(bp), rect(r), resizeOffset(0) {};
+ PixmapArray pixmap;
+ QPoint basePoint;
+ QRect rect;
+
+ // when showing a segment that is being resized from the
+ // beginning, this contains the offset between the current
+ // rect of the segment and the resized one
+ int resizeOffset;
+ };
+
+ typedef std::vector<AudioPreviewDrawDataItem> AudioPreviewDrawData;
+
+ struct RectRange {
+ std::pair<rectlist::iterator, rectlist::iterator> range;
+ QPoint basePoint;
+ QColor color;
+ };
+
+ typedef std::vector<RectRange> RectRanges;
+
+ class AudioPreviewData {
+ public:
+ AudioPreviewData(bool showMinima, unsigned int channels) : m_showMinima(showMinima), m_channels(channels) {};
+ // ~AudioPreviewData();
+
+ bool showsMinima() { return m_showMinima; }
+ void setShowMinima(bool s) { m_showMinima = s; }
+
+ unsigned int getChannels() { return m_channels; }
+ void setChannels(unsigned int c) { m_channels = c; }
+
+ const std::vector<float> &getValues() const { return m_values; }
+ void setValues(const std::vector<float>&v) { m_values = v; }
+
+ QRect getSegmentRect() { return m_segmentRect; }
+ void setSegmentRect(const QRect& r) { m_segmentRect = r; }
+
+ protected:
+ std::vector<float> m_values;
+ bool m_showMinima;
+ unsigned int m_channels;
+ QRect m_segmentRect;
+
+ private:
+ // no copy ctor
+ AudioPreviewData(const AudioPreviewData&);
+ };
+
+
+ virtual ~CompositionModel() {};
+
+ virtual unsigned int getNbRows() = 0;
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects) = 0;
+
+ virtual heightlist getTrackDividersIn(const QRect& rect) = 0;
+
+ virtual itemcontainer getItemsAt (const QPoint&) = 0;
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&) = 0;
+
+ virtual SnapGrid& grid() = 0;
+
+ virtual void setPointerPos(int xPos) = 0;
+ virtual void setSelected(const CompositionItem&, bool selected = true) = 0;
+ virtual bool isSelected(const CompositionItem&) const = 0;
+ virtual void setSelected(const itemcontainer&) = 0;
+ virtual void clearSelected() = 0;
+ virtual bool haveSelection() const = 0;
+ virtual bool haveMultipleSelection() const = 0;
+ virtual void signalSelection() = 0;
+ virtual void setSelectionRect(const QRect&) = 0;
+ virtual void finalizeSelectionRect() = 0;
+ virtual QRect getSelectionContentsRect() = 0;
+ virtual void signalContentChange() = 0;
+
+ virtual void addRecordingItem(const CompositionItem&) = 0;
+ virtual void removeRecordingItem(const CompositionItem&) = 0;
+ virtual void clearRecordingItems() = 0;
+ virtual bool haveRecordingItems() = 0;
+
+ enum ChangeType { ChangeMove, ChangeResizeFromStart, ChangeResizeFromEnd };
+
+ virtual void startChange(const CompositionItem&, ChangeType change) = 0;
+ virtual void startChangeSelection(ChangeType change) = 0;
+ virtual itemcontainer& getChangingItems() = 0;
+ virtual void endChange() = 0;
+ virtual ChangeType getChangeType() = 0;
+
+ virtual void setLength(int width) = 0;
+ virtual int getLength() = 0;
+
+signals:
+ void needContentUpdate();
+ void needContentUpdate(const QRect&);
+ void needArtifactsUpdate();
+
+protected:
+ CompositionItem* m_currentCompositionItem;
+};
+
+class AudioPreviewThread;
+class AudioPreviewUpdater;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
new file mode 100644
index 0000000..39deb2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
@@ -0,0 +1,1328 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+#include <algorithm>
+#include "CompositionModelImpl.h"
+
+#include "base/BaseProperties.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPreviewThread.h"
+#include "AudioPreviewUpdater.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include "CompositionColourCache.h"
+#include "AudioPreviewPainter.h"
+#include "gui/general/GUIPalette.h"
+#include "SegmentOrderer.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qregexp.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+
+namespace Rosegarden
+{
+
+CompositionModelImpl::CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep)
+ : m_composition(compo),
+ m_studio(studio),
+ m_grid(rulerScale, vStep),
+ m_pointerTimePos(0),
+ m_audioPreviewThread(0)
+{
+ m_notationPreviewDataCache.setAutoDelete(true);
+ m_audioPreviewDataCache.setAutoDelete(true);
+ m_composition.addObserver(this);
+
+ setTrackHeights();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->addObserver(this);
+ }
+}
+
+CompositionModelImpl::~CompositionModelImpl()
+{
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl()" << endl;
+
+ if (!isCompositionDeleted()) {
+
+ m_composition.removeObserver(this);
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->removeObserver(this);
+ }
+ }
+
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl(): removal from Segment & Composition observers OK" << endl;
+
+ if (m_audioPreviewThread) {
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+ }
+}
+
+struct RectCompare {
+ bool operator()(const QRect &r1, const QRect &r2) const {
+ return r1.x() < r2.x();
+ }
+};
+
+void CompositionModelImpl::makeNotationPreviewRects(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& clipRect)
+{
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end();
+
+ rectlist::iterator npi = std::lower_bound(cachedNPData->begin(), npEnd, clipRect, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != cachedNPData->begin())
+ --npi;
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int segEndX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getEndMarkerTime())));
+ int xLim = std::min(clipRect.topRight().x(), segEndX);
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRects : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setX(0);
+ interval.basePoint.setY(basePoint.y());
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeNotationPreviewRectsMovingSegment(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& currentSR)
+{
+ CompositionRect unmovedSR = computeSegmentRect(*segment);
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end(),
+ npBegin = cachedNPData->begin();
+
+ rectlist::iterator npi;
+
+ if (getChangeType() == ChangeResizeFromStart)
+ npi = std::lower_bound(npBegin, npEnd, currentSR, RectCompare());
+ else
+ npi = std::lower_bound(npBegin, npEnd, unmovedSR, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != npBegin && getChangeType() != ChangeResizeFromStart) {
+ --npi;
+ }
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int xLim = getChangeType() == ChangeMove ? unmovedSR.topRight().x() : currentSR.topRight().x();
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRectsMovingSegment : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setY(basePoint.y());
+
+ if (getChangeType() == ChangeMove)
+ interval.basePoint.setX(basePoint.x() - unmovedSR.x());
+ else
+ interval.basePoint.setX(0);
+
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment* segment,
+ const CompositionRect& segRect, const QRect& clipRect)
+{
+ Profiler profiler("CompositionModelImpl::makeAudioPreviewRects", true);
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewRects - segRect = " << segRect << endl;
+
+ PixmapArray previewImage = getAudioPreviewPixmap(segment);
+
+ QPoint basePoint = segRect.topLeft();
+
+ AudioPreviewDrawDataItem previewItem(previewImage, basePoint, segRect);
+
+ if (getChangeType() == ChangeResizeFromStart) {
+ CompositionRect originalRect = computeSegmentRect(*segment);
+ previewItem.resizeOffset = segRect.x() - originalRect.x();
+ }
+
+ apRects->push_back(previewItem);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionItem& item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ computeRepeatMarks(sr, s);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionRect& sr, const Segment* s)
+{
+ if (s->isRepeating()) {
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ if (repeatInterval <= 0) {
+ // std::cerr << "WARNING: CompositionModelImpl::computeRepeatMarks: Segment at " << startTime << " has repeatInterval " << repeatInterval << std::endl;
+ // std::cerr << kdBacktrace() << std::endl;
+ return ;
+ }
+
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s->getRepeatEndTime();
+ sr.setWidth(int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime))));
+
+ CompositionRect::repeatmarks repeatMarks;
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : repeatStart = "
+ // << repeatStart << " - repeatEnd = " << repeatEnd << endl;
+
+ for (timeT repeatMark = repeatStart; repeatMark < repeatEnd; repeatMark += repeatInterval) {
+ int mark = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatMark)));
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : mark at " << mark << endl;
+ repeatMarks.push_back(mark);
+ }
+ sr.setRepeatMarks(repeatMarks);
+ if (repeatMarks.size() > 0)
+ sr.setBaseWidth(repeatMarks[0] - sr.x());
+ else {
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : no repeat marks\n";
+ sr.setBaseWidth(sr.width());
+ }
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : s = "
+ // << s << " base width = " << sr.getBaseWidth()
+ // << " - nb repeat marks = " << repeatMarks.size() << endl;
+
+ }
+}
+
+void CompositionModelImpl::setAudioPreviewThread(AudioPreviewThread *thread)
+{
+ // std::cerr << "\nCompositionModelImpl::setAudioPreviewThread()\n" << std::endl;
+
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+
+ m_audioPreviewThread = thread;
+}
+
+void CompositionModelImpl::clearPreviewCache()
+{
+ RG_DEBUG << "CompositionModelImpl::clearPreviewCache\n";
+
+ m_notationPreviewDataCache.clear();
+ m_audioPreviewDataCache.clear();
+ m_audioSegmentPreviewMap.clear();
+
+ for (AudioPreviewUpdaterMap::iterator i = m_audioPreviewUpdaterMap.begin();
+ i != m_audioPreviewUpdaterMap.end(); ++i) {
+ i->second->cancel();
+ }
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // This will create the audio preview updater. The
+ // preview won't be calculated and cached until the
+ // updater completes and calls back.
+ updatePreviewCacheForAudioSegment((*i), 0);
+ }
+ }
+}
+
+void CompositionModelImpl::updatePreviewCacheForNotationSegment(const Segment* segment, rectlist* npData)
+{
+ npData->clear();
+
+ int segStartX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getStartTime())));
+
+ bool isPercussion = false;
+ Track *track = m_composition.getTrackById(segment->getTrack());
+ if (track) {
+ InstrumentId iid = track->getInstrument();
+ Instrument *instrument = m_studio.getInstrumentById(iid);
+ if (instrument && instrument->isPercussion()) isPercussion = true;
+ }
+
+ for (Segment::iterator i = segment->begin();
+ i != segment->end(); ++i) {
+
+ long pitch = 0;
+ if (!(*i)->isa(Note::EventType) ||
+ !(*i)->get<Int>(BaseProperties::PITCH, pitch)) {
+ continue;
+ }
+
+ timeT eventStart = (*i)->getAbsoluteTime();
+ timeT eventEnd = eventStart + (*i)->getDuration();
+ // if (eventEnd > segment->getEndMarkerTime()) {
+ // eventEnd = segment->getEndMarkerTime();
+ // }
+
+ int x = int(nearbyint(m_grid.getRulerScale()->getXForTime(eventStart)));
+ int width = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(eventStart,
+ eventEnd - eventStart)));
+
+ if (x <= segStartX) {
+ ++x;
+ if (width > 1)
+ --width;
+ }
+ if (width > 1)
+ --width;
+ if (width < 1)
+ ++width;
+
+ double y0 = 0;
+ double y1 = m_grid.getYSnap();
+ double y = y1 + ((y0 - y1) * (pitch - 16)) / 96;
+
+ int height = 2;
+
+ if (isPercussion) {
+ height = 3;
+ if (width > 2) width = 2;
+ }
+
+ if (y < y0)
+ y = y0;
+ if (y > y1 - height + 1)
+ y = y1 - height + 1;
+
+ QRect r(x, (int)y, width, height);
+
+ // RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForNotationSegment() : npData = "
+ // << npData << ", preview rect = "
+ // << r << endl;
+ npData->push_back(r);
+ }
+
+}
+
+QColor CompositionModelImpl::computeSegmentPreviewColor(const Segment* segment)
+{
+ // compute the preview color so it's as visible as possible over the segment's color
+ QColor segColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(segment->getColourIndex()));
+ int h, s, v;
+ segColor.hsv(&h, &s, &v);
+
+ // colors with saturation lower than value should be pastel tints, and
+ // they get a value of 0; yellow and green hues close to the dead center
+ // point for that hue were taking a value of 255 with the (s < v)
+ // formula, so I added an extra hack to force hues in those two narrow
+ // ranges toward black. Black always looks good, while white washes out
+ // badly against intense yellow, and doesn't look very good against
+ // intense green either... hacky, but this produces pleasant results against
+ // every bizarre extreme of color I could cook up to throw at it, plus
+ // (the real reason for all this convoluted fiddling, it does all that while keeping
+ // white against bright reds and blues, which looks better than black)
+ if ( ((((h > 57) && (h < 66)) || ((h > 93) && (h < 131))) && (s > 127) && (v > 127) ) ||
+ (s < v) ) {
+ v = 0;
+ } else {
+ v = 255;
+ }
+ s = 31;
+ h += 180;
+
+ segColor.setHsv(h, s, v);
+
+ return segColor;
+}
+
+void CompositionModelImpl::updatePreviewCacheForAudioSegment(const Segment* segment, AudioPreviewData* apData)
+{
+ if (m_audioPreviewThread) {
+ // std::cerr << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - new audio preview started" << std::endl;
+
+ CompositionRect segRect = computeSegmentRect(*segment);
+ segRect.setWidth(segRect.getBaseWidth()); // don't use repeating area
+ segRect.moveTopLeft(QPoint(0, 0));
+
+ if (apData)
+ apData->setSegmentRect(segRect);
+
+ if (m_audioPreviewUpdaterMap.find(segment) ==
+ m_audioPreviewUpdaterMap.end()) {
+
+ AudioPreviewUpdater *updater = new AudioPreviewUpdater
+ (*m_audioPreviewThread, m_composition, segment, segRect, this);
+
+ connect(updater, SIGNAL(audioPreviewComplete(AudioPreviewUpdater*)),
+ this, SLOT(slotAudioPreviewComplete(AudioPreviewUpdater*)));
+
+ m_audioPreviewUpdaterMap[segment] = updater;
+
+ } else {
+
+ m_audioPreviewUpdaterMap[segment]->setDisplayExtent(segRect);
+ }
+
+ m_audioPreviewUpdaterMap[segment]->update();
+
+ } else {
+ RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - no audio preview thread set\n";
+ }
+}
+
+void CompositionModelImpl::slotAudioPreviewComplete(AudioPreviewUpdater* apu)
+{
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete()\n";
+
+ AudioPreviewData *apData = getAudioPreviewData(apu->getSegment());
+ QRect updateRect;
+
+ if (apData) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete(" << apu << "): apData contains " << apData->getValues().size() << " values already" << endl;
+ unsigned int channels = 0;
+ const std::vector<float> &values = apu->getComputedValues(channels);
+ if (channels > 0) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete: set "
+ << values.size() << " samples on " << channels << " channels\n";
+ apData->setChannels(channels);
+ apData->setValues(values);
+ updateRect = postProcessAudioPreview(apData, apu->getSegment());
+ }
+ }
+
+ if (!updateRect.isEmpty())
+ emit needContentUpdate(updateRect);
+}
+
+QRect CompositionModelImpl::postProcessAudioPreview(AudioPreviewData* apData, const Segment* segment)
+{
+ // RG_DEBUG << "CompositionModelImpl::postProcessAudioPreview()\n";
+
+ AudioPreviewPainter previewPainter(*this, apData, m_composition, segment);
+ previewPainter.paintPreviewImage();
+
+ m_audioSegmentPreviewMap[segment] = previewPainter.getPreviewImage();
+
+ return previewPainter.getSegmentRect();
+}
+
+void CompositionModelImpl::slotInstrumentParametersChanged(InstrumentId id)
+{
+ std::cerr << "CompositionModelImpl::slotInstrumentParametersChanged()\n";
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ TrackId trackId = s->getTrack();
+ Track *track = getComposition().getTrackById(trackId);
+
+ // We need to update the cache for audio segments, because the
+ // instrument playback level is reflected in the audio
+ // preview. And we need to update it for midi segments,
+ // because the preview style differs depending on whether the
+ // segment is on a percussion instrument or not
+
+ if (track && track->getInstrument() == id) {
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+ }
+}
+
+void CompositionModelImpl::slotAudioFileFinalized(Segment* s)
+{
+ // RG_DEBUG << "CompositionModelImpl::slotAudioFileFinalized()\n";
+ removePreviewCache(s);
+}
+
+PixmapArray CompositionModelImpl::getAudioPreviewPixmap(const Segment* s)
+{
+ getAudioPreviewData(s);
+ return m_audioSegmentPreviewMap[s];
+}
+
+void CompositionModelImpl::eventAdded(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventAdded()\n";
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::eventRemoved(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventRemoved" << endl;
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::appearanceChanged(const Segment *s)
+{
+ // RG_DEBUG << "CompositionModelImpl::appearanceChanged" << endl;
+ clearInCache(s, true);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Segment *s, bool shorten)
+{
+ // RG_DEBUG << "CompositionModelImpl::endMarkerTimeChanged(" << shorten << ")" << endl;
+ clearInCache(s, true);
+ if (shorten) {
+ emit needContentUpdate(); // no longer know former segment dimension
+ } else {
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+}
+
+void CompositionModelImpl::makePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ makeNotationPreviewDataCache(s);
+ } else {
+ makeAudioPreviewDataCache(s);
+ }
+}
+
+void CompositionModelImpl::removePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ m_notationPreviewDataCache.remove(const_cast<Segment*>(s));
+ } else {
+ m_audioPreviewDataCache.remove(const_cast<Segment*>(s));
+ m_audioSegmentPreviewMap.erase(s);
+ }
+
+}
+
+void CompositionModelImpl::segmentAdded(const Composition *, Segment *s)
+{
+ std::cerr << "CompositionModelImpl::segmentAdded: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ setTrackHeights(s);
+
+ makePreviewCache(s);
+ s->addObserver(this);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentRemoved(const Composition *, Segment *s)
+{
+ setTrackHeights();
+
+ QRect r = computeSegmentRect(*s);
+
+ m_selectedSegments.erase(s);
+
+ clearInCache(s, true);
+ s->removeObserver(this);
+ m_recordingSegments.erase(s); // this could be a recording segment
+ emit needContentUpdate(r);
+}
+
+void CompositionModelImpl::segmentTrackChanged(const Composition *, Segment *s, TrackId tid)
+{
+ std::cerr << "CompositionModelImpl::segmentTrackChanged: segment " << s << " on track " << tid << ", calling setTrackHeights" << std::endl;
+
+ // we don't call setTrackHeights(s), because some of the tracks
+ // above s may have changed height as well (if s was moved off one
+ // of them)
+ if (setTrackHeights()) {
+ std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentStartChanged(const Composition *, Segment *s, timeT)
+{
+// std::cerr << "CompositionModelImpl::segmentStartChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentEndMarkerChanged(const Composition *, Segment *s, bool)
+{
+// std::cerr << "CompositionModelImpl::segmentEndMarkerChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) {
+// std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentRepeatChanged(const Composition *, Segment *s, bool)
+{
+ clearInCache(s);
+ setTrackHeights(s);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Composition *, bool)
+{
+ emit needSizeUpdate();
+}
+
+void CompositionModelImpl::setSelectionRect(const QRect& r)
+{
+ m_selectionRect = r.normalize();
+
+ m_previousTmpSelectedSegments = m_tmpSelectedSegments;
+ m_tmpSelectedSegments.clear();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ QRect updateRect = m_selectionRect;
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ m_tmpSelectedSegments.insert(s);
+ updateRect |= sr;
+ }
+ }
+
+ updateRect = updateRect.normalize();
+
+ if (!updateRect.isNull() && !m_previousSelectionUpdateRect.isNull()) {
+
+ if (m_tmpSelectedSegments != m_previousTmpSelectedSegments)
+ emit needContentUpdate(updateRect | m_previousSelectionUpdateRect);
+
+ emit needArtifactsUpdate();
+ }
+
+
+ m_previousSelectionUpdateRect = updateRect;
+
+}
+
+void CompositionModelImpl::finalizeSelectionRect()
+{
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ setSelected(s);
+ }
+ }
+
+ m_previousSelectionUpdateRect = m_selectionRect = QRect();
+ m_tmpSelectedSegments.clear();
+}
+
+QRect CompositionModelImpl::getSelectionContentsRect()
+{
+ QRect selectionRect;
+
+ SegmentSelection sel = getSelectedSegments();
+ for (SegmentSelection::iterator i = sel.begin();
+ i != sel.end(); ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ selectionRect |= sr;
+ }
+
+ return selectionRect;
+}
+
+void CompositionModelImpl::addRecordingItem(const CompositionItem& item)
+{
+ m_recordingSegments.insert(CompositionItemHelper::getSegment(item));
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::addRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::removeRecordingItem(const CompositionItem &item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+
+ m_recordingSegments.erase(s);
+ clearInCache(s, true);
+
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::removeRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::clearRecordingItems()
+{
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i)
+ clearInCache(*i, true);
+
+ m_recordingSegments.clear();
+
+ emit needContentUpdate();
+ RG_DEBUG << "CompositionModelImpl::clearRecordingItem\n";
+}
+
+bool CompositionModelImpl::isMoving(const Segment* sm) const
+{
+ itemcontainer::const_iterator movEnd = m_changingItems.end();
+
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ const CompositionItemImpl* ci = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)(*i));
+ const Segment* s = ci->getSegment();
+ if (sm == s)
+ return true;
+ }
+
+ return false;
+}
+
+bool CompositionModelImpl::isRecording(const Segment* s) const
+{
+ return m_recordingSegments.find(const_cast<Segment*>(s)) != m_recordingSegments.end();
+}
+
+CompositionModel::itemcontainer CompositionModelImpl::getItemsAt(const QPoint& point)
+{
+ itemcontainer res;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segments.end(); ++i) {
+
+ Segment* s = *i;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.contains(point)) {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() adding " << sr << " for segment " << s << endl;
+ CompositionItem item(new CompositionItemImpl(*s, sr));
+ unsigned int z = computeZForSegment(s);
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() z = " << z << endl;
+ item->setZ(z);
+ res.insert(item);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() skiping " << sr << endl;
+ }
+
+ }
+
+ if (res.size() == 1) { // only one segment under click point
+ Segment* s = CompositionItemHelper::getSegment(*(res.begin()));
+ m_segmentOrderer.segmentClicked(s);
+ }
+
+ return res;
+}
+
+void CompositionModelImpl::setPointerPos(int xPos)
+{
+ m_pointerTimePos = grid().getRulerScale()->getTimeForX(xPos);
+
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i) {
+ emit needContentUpdate(computeSegmentRect(**i));
+ }
+}
+
+void CompositionModelImpl::setSelected(const CompositionItem& item, bool selected)
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)item);
+ if (itemImpl) {
+ Segment* segment = const_cast<Segment*>(itemImpl->getSegment());
+ setSelected(segment, selected);
+ }
+}
+
+void CompositionModelImpl::setSelected(const itemcontainer& items)
+{
+ for (itemcontainer::const_iterator i = items.begin(); i != items.end(); ++i) {
+ setSelected(*i);
+ }
+}
+
+void CompositionModelImpl::setSelected(const Segment* segment, bool selected)
+{
+ RG_DEBUG << "CompositionModelImpl::setSelected " << segment << " - " << selected << endl;
+ if (selected) {
+ if (!isSelected(segment))
+ m_selectedSegments.insert(const_cast<Segment*>(segment));
+ } else {
+ SegmentSelection::iterator i = m_selectedSegments.find(const_cast<Segment*>(segment));
+ if (i != m_selectedSegments.end())
+ m_selectedSegments.erase(i);
+ }
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::signalSelection()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalSelection()\n";
+ emit selectedSegments(getSelectedSegments());
+}
+
+void CompositionModelImpl::signalContentChange()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalContentChange" << endl;
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::clearSelected()
+{
+ RG_DEBUG << "CompositionModelImpl::clearSelected" << endl;
+ m_selectedSegments.clear();
+ emit needContentUpdate();
+}
+
+bool CompositionModelImpl::isSelected(const CompositionItem& ci) const
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)ci);
+ return itemImpl ? isSelected(itemImpl->getSegment()) : 0;
+}
+
+bool CompositionModelImpl::isSelected(const Segment* s) const
+{
+ return m_selectedSegments.find(const_cast<Segment*>(s)) != m_selectedSegments.end();
+}
+
+bool CompositionModelImpl::isTmpSelected(const Segment* s) const
+{
+ return m_tmpSelectedSegments.find(const_cast<Segment*>(s)) != m_tmpSelectedSegments.end();
+}
+
+bool CompositionModelImpl::wasTmpSelected(const Segment* s) const
+{
+ return m_previousTmpSelectedSegments.find(const_cast<Segment*>(s)) != m_previousTmpSelectedSegments.end();
+}
+
+void CompositionModelImpl::startChange(const CompositionItem& item, CompositionModel::ChangeType change)
+{
+ m_changeType = change;
+
+ itemcontainer::iterator i = m_changingItems.find(item);
+
+ // if an "identical" composition item has already been inserted, drop this one
+ if (i != m_changingItems.end()) {
+ RG_DEBUG << "CompositionModelImpl::startChange : item already in\n";
+ m_itemGC.push_back(item);
+ } else {
+ item->saveRect();
+ m_changingItems.insert(item);
+ }
+}
+
+void CompositionModelImpl::startChangeSelection(CompositionModel::ChangeType change)
+{
+ SegmentSelection::iterator i = m_selectedSegments.begin();
+ for (; i != m_selectedSegments.end(); ++i) {
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ startChange(CompositionItem(new CompositionItemImpl(*s, sr)), change);
+ }
+
+}
+
+void CompositionModelImpl::endChange()
+{
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != m_changingItems.end(); ++i) {
+ delete *i;
+ }
+
+ m_changingItems.clear();
+
+ for (itemgc::iterator i = m_itemGC.begin(); i != m_itemGC.end(); ++i) {
+ delete *i;
+ }
+ m_itemGC.clear();
+ RG_DEBUG << "CompositionModelImpl::endChange\n";
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::setLength(int width)
+{
+ timeT endMarker = m_grid.snapX(width);
+ m_composition.setEndMarker(endMarker);
+}
+
+int CompositionModelImpl::getLength()
+{
+ timeT endMarker = m_composition.getEndMarker();
+ int w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(0, endMarker)));
+ return w;
+}
+
+timeT CompositionModelImpl::getRepeatTimeAt(const QPoint& p, const CompositionItem& cItem)
+{
+ // timeT timeAtClick = m_grid.getRulerScale()->getTimeForX(p.x());
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)cItem);
+
+ const Segment* s = itemImpl->getSegment();
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ int rWidth = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatInterval)));
+
+ int count = (p.x() - int(itemImpl->rect().x())) / rWidth;
+ RG_DEBUG << "CompositionModelImpl::getRepeatTimeAt() : count = " << count << endl;
+
+ return count != 0 ? startTime + (count * (s->getEndMarkerTime() - s->getStartTime())) : 0;
+}
+
+bool CompositionModelImpl::setTrackHeights(Segment *s)
+{
+ bool heightsChanged = false;
+
+// std::cerr << "CompositionModelImpl::setTrackHeights" << std::endl;
+
+ for (Composition::trackcontainer::const_iterator i =
+ m_composition.getTracks().begin();
+ i != m_composition.getTracks().end(); ++i) {
+
+ if (s && i->first != s->getTrack()) continue;
+
+ int max = m_composition.getMaxContemporaneousSegmentsOnTrack(i->first);
+ if (max == 0) max = 1;
+
+// std::cerr << "for track " << i->first << ": height = " << max << ", old height = " << m_trackHeights[i->first] << std::endl;
+
+ if (max != m_trackHeights[i->first]) {
+ heightsChanged = true;
+ m_trackHeights[i->first] = max;
+ }
+
+ m_grid.setBinHeightMultiple(i->second->getPosition(), max);
+ }
+
+ if (heightsChanged) {
+// std::cerr << "CompositionModelImpl::setTrackHeights: heights have changed" << std::endl;
+ for (Composition::segmentcontainer::iterator i = m_composition.begin();
+ i != m_composition.end(); ++i) {
+ computeSegmentRect(**i);
+ }
+ }
+
+ return heightsChanged;
+}
+
+QPoint CompositionModelImpl::computeSegmentOrigin(const Segment& s)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentOrigin", true);
+
+ int trackPosition = m_composition.getTrackPositionById(s.getTrack());
+ timeT startTime = s.getStartTime();
+
+ QPoint res;
+
+ res.setX(int(nearbyint(m_grid.getRulerScale()->getXForTime(startTime))));
+
+ res.setY(m_grid.getYBinCoordinate(trackPosition) +
+ m_composition.getSegmentVoiceIndex(&s) *
+ m_grid.getYSnap() + 1);
+
+ return res;
+}
+
+bool CompositionModelImpl::isCachedRectCurrent(const Segment& s, const CompositionRect& r, QPoint cachedSegmentOrigin, timeT cachedSegmentEndTime)
+{
+ return s.isRepeating() == r.isRepeating() &&
+ ((cachedSegmentOrigin.x() != r.x() && s.getEndMarkerTime() != cachedSegmentEndTime) ||
+ (cachedSegmentOrigin.x() == r.x() && s.getEndMarkerTime() == cachedSegmentEndTime));
+}
+
+void CompositionModelImpl::clearInCache(const Segment* s, bool clearPreview)
+{
+ if (s) {
+ m_segmentRectMap.erase(s);
+ m_segmentEndTimeMap.erase(s);
+ if (clearPreview)
+ removePreviewCache(s);
+ } else { // clear the whole cache
+ m_segmentRectMap.clear();
+ m_segmentEndTimeMap.clear();
+ if (clearPreview)
+ clearPreviewCache();
+ }
+}
+
+void CompositionModelImpl::putInCache(const Segment*s, const CompositionRect& cr)
+{
+ m_segmentRectMap[s] = cr;
+ m_segmentEndTimeMap[s] = s->getEndMarkerTime();
+}
+
+CompositionRect CompositionModelImpl::computeSegmentRect(const Segment& s, bool computeZ)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentRect", true);
+
+ QPoint origin = computeSegmentOrigin(s);
+
+ bool isRecordingSegment = isRecording(&s);
+
+ if (!isRecordingSegment) {
+ timeT endTime = 0;
+
+ CompositionRect cachedCR = getFromCache(&s, endTime);
+ // don't cache repeating segments - it's just hopeless, because the segment's rect may have to be recomputed
+ // in other cases than just when the segment itself is moved,
+ // for instance if another segment is moved over it
+ if (!s.isRepeating() && cachedCR.isValid() && isCachedRectCurrent(s, cachedCR, origin, endTime)) {
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect() : using cache for seg "
+ // << &s << " - cached rect repeating = " << cachedCR.isRepeating() << " - base width = "
+ // << cachedCR.getBaseWidth() << endl;
+
+ bool xChanged = origin.x() != cachedCR.x();
+ bool yChanged = origin.y() != cachedCR.y();
+
+ cachedCR.moveTopLeft(origin);
+
+ if (s.isRepeating() && (xChanged || yChanged)) { // update repeat marks
+
+ // this doesn't work in the general case (if there's another segment on the same track for instance),
+ // it's better to simply recompute all the marks
+ // CompositionRect::repeatmarks repeatMarks = cachedCR.getRepeatMarks();
+ // for(unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ // repeatMarks[i] += deltaX;
+ // }
+ // cachedCR.setRepeatMarks(repeatMarks);
+ computeRepeatMarks(cachedCR, &s);
+ }
+ putInCache(&s, cachedCR);
+ return cachedCR;
+ }
+ }
+
+ timeT startTime = s.getStartTime();
+ timeT endTime = isRecordingSegment ? m_pointerTimePos /*s.getEndTime()*/ : s.getEndMarkerTime();
+
+
+ int h = m_grid.getYSnap() - 2;
+ int w;
+
+ RG_DEBUG << "CompositionModelImpl::computeSegmentRect: x " << origin.x() << ", y " << origin.y() << " startTime " << startTime << ", endTime " << endTime << endl;
+
+ if (s.isRepeating()) {
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s.getRepeatEndTime();
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is repeating - repeatStart = "
+ // << repeatStart << " - repeatEnd : " << repeatEnd
+ // << " w = " << w << endl;
+ } else {
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime, endTime - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is NOT repeating"
+ // << " w = " << w << " (x for time at start is " << m_grid.getRulerScale()->getXForTime(startTime) << ", end is " << m_grid.getRulerScale()->getXForTime(endTime) << ")" << endl;
+ }
+
+ CompositionRect cr(origin, QSize(w, h));
+ QString label = strtoqstr(s.getLabel());
+ if (s.getType() == Segment::Audio) {
+ static QRegExp re1("( *\\([^)]*\\))*$"); // (inserted) (copied) (etc)
+ static QRegExp re2("\\.[^.]+$"); // filename suffix
+ label.replace(re1, "").replace(re2, "");
+ }
+ cr.setLabel(label);
+
+ if (s.isRepeating()) {
+ computeRepeatMarks(cr, &s);
+ } else {
+ cr.setBaseWidth(cr.width());
+ }
+
+ putInCache(&s, cr);
+
+ return cr;
+}
+
+unsigned int CompositionModelImpl::computeZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentOrderer.getZForSegment(s);
+}
+
+const CompositionRect& CompositionModelImpl::getFromCache(const Rosegarden::Segment* s, timeT& endTime)
+{
+ endTime = m_segmentEndTimeMap[s];
+ return m_segmentRectMap[s];
+}
+
+unsigned int CompositionModelImpl::getNbRows()
+{
+ return m_composition.getNbTracks();
+}
+
+const CompositionModel::rectcontainer& CompositionModelImpl::getRectanglesIn(const QRect& rect,
+ RectRanges* npData,
+ AudioPreviewDrawData* apData)
+{
+ // Profiler profiler("CompositionModelImpl::getRectanglesIn", true);
+
+ m_res.clear();
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: ruler scale is "
+ // << (dynamic_cast<SimpleRulerScale *>(m_grid.getRulerScale()))->getUnitsPerPixel() << endl;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: Composition contains segment " << *i << " (" << (*i)->getStartTime() << "->" << (*i)->getEndTime() << ")"<< endl;
+
+ Segment* s = *i;
+
+ if (isMoving(s))
+ continue;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: seg rect = " << sr << endl;
+
+ if (sr.intersects(rect)) {
+ bool tmpSelected = isTmpSelected(s),
+ pTmpSelected = wasTmpSelected(s);
+
+// RG_DEBUG << "CompositionModelImpl::getRectanglesIn: segment " << s
+// << " selected : " << isSelected(s) << " - tmpSelected : " << isTmpSelected(s) << endl;
+
+ if (isSelected(s) || isTmpSelected(s) || sr.intersects(m_selectionRect)) {
+ sr.setSelected(true);
+ }
+
+ if (pTmpSelected != tmpSelected)
+ sr.setNeedsFullUpdate(true);
+
+ bool isAudio = (s && s->getType() == Segment::Audio);
+
+ if (!isRecording(s)) {
+ QColor brushColor = GUIPalette::convertColour(m_composition.
+ getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ } else {
+ // border is the same for both audio and MIDI
+ sr.setPen(CompositionColourCache::getInstance()->RecordingSegmentBorder);
+ // audio color
+ if (isAudio) {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingAudioSegmentBlock);
+ // MIDI/default color
+ } else {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingInternalSegmentBlock);
+ }
+ }
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRects(npData, QPoint(0, sr.y()), s, rect);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: - segment out of rect\n";
+ }
+
+ }
+
+ // changing items
+
+ itemcontainer::iterator movEnd = m_changingItems.end();
+ for (itemcontainer::iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ CompositionRect sr((*i)->rect());
+ if (sr.intersects(rect)) {
+ Segment* s = CompositionItemHelper::getSegment(*i);
+ sr.setSelected(true);
+ QColor brushColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRectsMovingSegment(npData, sr.topLeft(), s, sr);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ }
+ }
+
+ return m_res;
+}
+
+CompositionModel::heightlist
+CompositionModelImpl::getTrackDividersIn(const QRect& rect)
+{
+ int top = m_grid.getYBin(rect.y());
+ int bottom = m_grid.getYBin(rect.y() + rect.height());
+
+// std::cerr << "CompositionModelImpl::getTrackDividersIn: rect "
+// << rect.x() << ", " << rect.y() << ", "
+// << rect.width() << "x" << rect.height() << ", top = " << top
+// << ", bottom = " << bottom << std::endl;
+
+ CompositionModel::heightlist list;
+
+ for (int pos = top; pos <= bottom; ++pos) {
+ int divider = m_grid.getYBinCoordinate(pos);
+ list.push_back(divider);
+// std::cerr << "divider at " << divider << std::endl;
+ }
+
+ return list;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::getNotationPreviewData(const Segment* s)
+{
+ rectlist* npData = m_notationPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!npData) {
+ npData = makeNotationPreviewDataCache(s);
+ }
+
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::getAudioPreviewData(const Segment* s)
+{
+ // Profiler profiler("CompositionModelImpl::getAudioPreviewData", true);
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData\n";
+
+ AudioPreviewData* apData = m_audioPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!apData) {
+ apData = makeAudioPreviewDataCache(s);
+ }
+
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData returning\n";
+ return apData;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::makeNotationPreviewDataCache(const Segment *s)
+{
+ rectlist* npData = new rectlist();
+ updatePreviewCacheForNotationSegment(s, npData);
+ m_notationPreviewDataCache.insert(const_cast<Segment*>(s), npData);
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::makeAudioPreviewDataCache(const Segment *s)
+{
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewDataCache(" << s << ")" << endl;
+
+ AudioPreviewData* apData = new AudioPreviewData(false, 0); // 0 channels -> empty
+ updatePreviewCacheForAudioSegment(s, apData);
+ m_audioPreviewDataCache.insert(const_cast<Segment*>(s), apData);
+ return apData;
+}
+
+}
+#include "CompositionModelImpl.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
new file mode 100644
index 0000000..6e1c9d6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
@@ -0,0 +1,239 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONMODELIMPL_H_
+#define _RG_COMPOSITIONMODELIMPL_H_
+
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include <map>
+#include "SegmentOrderer.h"
+#include <set>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qptrdict.h>
+#include <qrect.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+class AudioPreviewData;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Segment;
+class RulerScale;
+class Event;
+class Composition;
+class AudioPreviewUpdater;
+class AudioPreviewThread;
+
+
+class CompositionModelImpl : public CompositionModel
+{
+ Q_OBJECT
+public:
+
+ CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep);
+
+ virtual ~CompositionModelImpl();
+
+ virtual unsigned int getNbRows();
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects);
+ virtual heightlist getTrackDividersIn(const QRect& rect);
+ virtual itemcontainer getItemsAt (const QPoint&);
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&);
+
+ virtual SnapGrid& grid() { return m_grid; }
+
+ virtual void setPointerPos(int xPos);
+ virtual void setSelected(const CompositionItem&, bool selected = true);
+ virtual bool isSelected(const CompositionItem&) const;
+ virtual void setSelected(const itemcontainer&);
+ virtual void clearSelected();
+ virtual bool haveSelection() const { return !m_selectedSegments.empty(); }
+ virtual bool haveMultipleSelection() const { return m_selectedSegments.size() > 1; }
+ virtual void signalSelection();
+ virtual void setSelectionRect(const QRect&);
+ virtual void finalizeSelectionRect();
+ virtual QRect getSelectionContentsRect();
+ virtual void signalContentChange();
+
+ virtual void addRecordingItem(const CompositionItem&);
+ virtual void removeRecordingItem(const CompositionItem &);
+ virtual void clearRecordingItems();
+ virtual bool haveRecordingItems() { return m_recordingSegments.size() > 0; }
+
+ virtual void startChange(const CompositionItem&, ChangeType change);
+ virtual void startChangeSelection(ChangeType change);
+ virtual itemcontainer& getChangingItems() { return m_changingItems; }
+ virtual void endChange();
+ virtual ChangeType getChangeType() { return m_changeType; }
+
+ virtual void setLength(int width);
+ virtual int getLength();
+
+ void setAudioPreviewThread(AudioPreviewThread *thread);
+ AudioPreviewThread* getAudioPreviewThread() { return m_audioPreviewThread; }
+
+ void clearPreviewCache();
+ void clearSegmentRectsCache(bool clearPreviews = false) { clearInCache(0, clearPreviews); }
+
+ rectlist* makeNotationPreviewDataCache(const Segment *s);
+ AudioPreviewData* makeAudioPreviewDataCache(const Segment *s);
+
+ CompositionRect computeSegmentRect(const Segment&, bool computeZ = false);
+ QColor computeSegmentPreviewColor(const Segment*);
+ QPoint computeSegmentOrigin(const Segment&);
+ void computeRepeatMarks(CompositionItem&);
+
+ SegmentSelection getSelectedSegments() { return m_selectedSegments; }
+ Composition& getComposition() { return m_composition; }
+ Studio& getStudio() { return m_studio; }
+
+
+ // CompositionObserver
+ virtual void segmentAdded(const Composition *, Segment *);
+ virtual void segmentRemoved(const Composition *, Segment *);
+ virtual void segmentRepeatChanged(const Composition *, Segment *, bool);
+ virtual void segmentStartChanged(const Composition *, Segment *, timeT);
+ virtual void segmentEndMarkerChanged(const Composition *, Segment *, bool);
+ virtual void segmentTrackChanged(const Composition *, Segment *, TrackId);
+ virtual void endMarkerTimeChanged(const Composition *, bool /*shorten*/);
+
+ // SegmentObserver
+ virtual void eventAdded(const Segment *, Event *);
+ virtual void eventRemoved(const Segment *, Event *);
+ virtual void appearanceChanged(const Segment *);
+ virtual void endMarkerTimeChanged(const Segment *, bool /*shorten*/);
+ virtual void segmentDeleted(const Segment*) { /* nothing to do - handled by CompositionObserver::segmentRemoved() */ };
+
+signals:
+ void selectedSegments(const SegmentSelection &);
+ void needSizeUpdate();
+
+public slots:
+ void slotAudioFileFinalized(Segment*);
+ void slotInstrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotAudioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ bool setTrackHeights(Segment *changed = 0); // true if something changed
+
+ void setSelected(const Segment*, bool selected = true);
+ bool isSelected(const Segment*) const;
+ bool isTmpSelected(const Segment*) const;
+ bool wasTmpSelected(const Segment*) const;
+ bool isMoving(const Segment*) const;
+ bool isRecording(const Segment*) const;
+
+ void computeRepeatMarks(CompositionRect& sr, const Segment* s);
+ unsigned int computeZForSegment(const Segment* s);
+
+ // segment preview stuff
+
+ void updatePreviewCacheForNotationSegment(const Segment* s, rectlist*);
+ void updatePreviewCacheForAudioSegment(const Segment* s, AudioPreviewData*);
+ rectlist* getNotationPreviewData(const Segment* s);
+ AudioPreviewData* getAudioPreviewData(const Segment* s);
+ PixmapArray getAudioPreviewPixmap(const Segment* s);
+ QRect postProcessAudioPreview(AudioPreviewData*, const Segment*);
+
+ void makePreviewCache(const Segment* s);
+ void removePreviewCache(const Segment* s);
+ void makeNotationPreviewRects(RectRanges* npData, QPoint basePoint, const Segment*, const QRect&);
+ void makeNotationPreviewRectsMovingSegment(RectRanges* npData, QPoint basePoint, const Segment*,
+ const QRect&);
+ void makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment*,
+ const CompositionRect& segRect, const QRect& clipRect);
+
+ void clearInCache(const Segment*, bool clearPreviewCache = false);
+ void putInCache(const Segment*, const CompositionRect&);
+ const CompositionRect& getFromCache(const Segment*, timeT& endTime);
+ bool isCachedRectCurrent(const Segment& s, const CompositionRect& r,
+ QPoint segmentOrigin, timeT segmentEndTime);
+
+ //--------------- Data members ---------------------------------
+ Composition& m_composition;
+ Studio& m_studio;
+ SnapGrid m_grid;
+ SegmentSelection m_selectedSegments;
+ SegmentSelection m_tmpSelectedSegments;
+ SegmentSelection m_previousTmpSelectedSegments;
+
+ timeT m_pointerTimePos;
+
+ typedef std::set<Segment *> recordingsegmentset;
+ recordingsegmentset m_recordingSegments;
+
+ typedef std::vector<CompositionItem> itemgc;
+
+ AudioPreviewThread* m_audioPreviewThread;
+
+ typedef QPtrDict<rectlist> NotationPreviewDataCache;
+ typedef QPtrDict<AudioPreviewData> AudioPreviewDataCache;
+
+ NotationPreviewDataCache m_notationPreviewDataCache;
+ AudioPreviewDataCache m_audioPreviewDataCache;
+
+ rectcontainer m_res;
+ itemcontainer m_changingItems;
+ ChangeType m_changeType;
+ itemgc m_itemGC;
+
+ QRect m_selectionRect;
+ QRect m_previousSelectionUpdateRect;
+
+ std::map<const Segment*, CompositionRect> m_segmentRectMap;
+ std::map<const Segment*, timeT> m_segmentEndTimeMap;
+ std::map<const Segment*, PixmapArray> m_audioSegmentPreviewMap;
+ std::map<TrackId, int> m_trackHeights;
+
+ typedef std::map<const Segment*, AudioPreviewUpdater *>
+ AudioPreviewUpdaterMap;
+ AudioPreviewUpdaterMap m_audioPreviewUpdaterMap;
+
+ SegmentOrderer m_segmentOrderer;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
new file mode 100644
index 0000000..9e34d71
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionRect.h"
+#include "base/ColourMap.h"
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+ const QColor CompositionRect::DefaultPenColor = Qt::black;
+ const QColor CompositionRect::DefaultBrushColor = QColor(COLOUR_DEF_R, COLOUR_DEF_G, COLOUR_DEF_B);
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.h b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
new file mode 100644
index 0000000..3c3d2b6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
@@ -0,0 +1,108 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONRECT_H_
+#define _RG_COMPOSITIONRECT_H_
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+
+class QSize;
+class QPoint;
+
+
+namespace Rosegarden
+{
+
+class CompositionRect : public QRect
+{
+public:
+ typedef QValueVector<int> repeatmarks;
+
+ friend bool operator<(const CompositionRect&, const CompositionRect&);
+
+ CompositionRect() : QRect(), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor) {};
+ CompositionRect(const QRect& r) : QRect(r), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QPoint & bottomRight)
+ : QRect(topLeft, bottomRight), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QSize & size)
+ : QRect(topLeft, size), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(int left, int top, int width, int height)
+ : QRect(left, top, width, height), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+
+ void setResized(bool s) { m_resized = s; }
+ bool isResized() const { return m_resized; }
+ void setSelected(bool s) { m_selected = s; }
+ bool isSelected() const { return m_selected; }
+ bool needsFullUpdate() const { return m_needUpdate; }
+ void setNeedsFullUpdate(bool s) { m_needUpdate = s; }
+
+ void setZ(int z) { m_z = z; }
+ int z() const { return m_z; }
+
+ // brush, pen draw info
+ void setBrush(QBrush b) { m_brush = b; }
+ QBrush getBrush() const { return m_brush; }
+ void setPen(QPen b) { m_pen = b; }
+ QPen getPen() const { return m_pen; }
+
+ // repeating segments
+ void setRepeatMarks(const repeatmarks& rm) { m_repeatMarks = rm; }
+ const repeatmarks& getRepeatMarks() const { return m_repeatMarks; }
+ bool isRepeating() const { return m_repeatMarks.size() > 0; }
+ int getBaseWidth() const { return m_baseWidth; }
+ void setBaseWidth(int bw) { m_baseWidth = bw; }
+ QString getLabel() const { return m_label; }
+ void setLabel(QString l) { m_label = l; }
+
+ static const QColor DefaultPenColor;
+ static const QColor DefaultBrushColor;
+
+protected:
+ bool m_resized;
+ bool m_selected;
+ bool m_needUpdate;
+ QBrush m_brush;
+ QPen m_pen;
+ repeatmarks m_repeatMarks;
+ int m_baseWidth;
+ QString m_label;
+ int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.cpp b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
new file mode 100644
index 0000000..8e83a6b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
@@ -0,0 +1,1591 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionView.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionColourCache.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionModelImpl.h"
+#include "CompositionRect.h"
+#include "AudioPreviewPainter.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/general/RosegardenScrollView.h"
+#include "SegmentSelector.h"
+#include "SegmentToolBox.h"
+#include "SegmentTool.h"
+#include <kmessagebox.h>
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qmemarray.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+CompositionView::CompositionView(RosegardenGUIDoc* doc,
+ CompositionModel* model,
+ QWidget * parent, const char * name, WFlags f)
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0)
+ : RosegardenScrollView(parent, name, f | WNoAutoErase | WStaticContents),
+#else
+ :
+ RosegardenScrollView(parent, name, f | WRepaintNoErase | WResizeNoErase | WStaticContents),
+#endif
+ m_model(model),
+ m_currentItem(0),
+ m_tool(0),
+ m_toolBox(0),
+ m_showPreviews(false),
+ m_showSegmentLabels(true),
+ m_fineGrain(false),
+ m_pencilOverExisting(false),
+ m_minWidth(m_model->getLength()),
+ m_stepSize(0),
+ m_rectFill(0xF0, 0xF0, 0xF0),
+ m_selectedRectFill(0x00, 0x00, 0xF0),
+ m_pointerPos(0),
+ m_pointerColor(GUIPalette::getColour(GUIPalette::Pointer)),
+ m_pointerWidth(4),
+ m_pointerPen(QPen(m_pointerColor, m_pointerWidth)),
+ m_tmpRect(QRect(QPoint(0, 0), QPoint( -1, -1))),
+ m_tmpRectFill(CompositionRect::DefaultBrushColor),
+ m_trackDividerColor(GUIPalette::getColour(GUIPalette::TrackDivider)),
+ m_drawGuides(false),
+ m_guideColor(GUIPalette::getColour(GUIPalette::MovementGuide)),
+ m_topGuidePos(0),
+ m_foreGuidePos(0),
+ m_drawSelectionRect(false),
+ m_drawTextFloat(false),
+ m_segmentsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_artifactsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_segmentsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_artifactsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_lastBufferRefreshX(0),
+ m_lastBufferRefreshY(0),
+ m_lastPointerRefreshX(0),
+ m_contextHelpShown(false)
+{
+ if (doc) {
+ m_toolBox = new SegmentToolBox(this, doc);
+
+ connect(m_toolBox, SIGNAL(showContextHelp(const QString &)),
+ this, SLOT(slotToolHelpChanged(const QString &)));
+ }
+
+ setDragAutoScroll(true);
+ setBackgroundMode(NoBackground);
+ viewport()->setBackgroundMode(NoBackground);
+ viewport()->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas));
+
+ slotUpdateSize();
+
+ QScrollBar* hsb = horizontalScrollBar();
+
+ // dynamically adjust content size when scrolling past current composition's end
+ connect(hsb, SIGNAL(nextLine()),
+ this, SLOT(scrollRight()));
+ connect(hsb, SIGNAL(prevLine()),
+ this, SLOT(scrollLeft()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotAllDrawBuffersNeedRefresh()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotContentsMoving(int, int)));
+
+ connect(model, SIGNAL(needContentUpdate()),
+ this, SLOT(slotUpdateSegmentsDrawBuffer()));
+ connect(model, SIGNAL(needContentUpdate(const QRect&)),
+ this, SLOT(slotUpdateSegmentsDrawBuffer(const QRect&)));
+ connect(model, SIGNAL(needArtifactsUpdate()),
+ this, SLOT(slotArtifactsDrawBufferNeedsRefresh()));
+ connect(model, SIGNAL(needSizeUpdate()),
+ this, SLOT(slotUpdateSize()));
+
+ if (doc) {
+ connect(doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotRefreshColourCache()));
+
+ // recording-related signals
+ connect(doc, SIGNAL(newMIDIRecordingSegment(Segment*)),
+ this, SLOT(slotNewMIDIRecordingSegment(Segment*)));
+ connect(doc, SIGNAL(newAudioRecordingSegment(Segment*)),
+ this, SLOT(slotNewAudioRecordingSegment(Segment*)));
+ // connect(doc, SIGNAL(recordMIDISegmentUpdated(Segment*, timeT)),
+ // this, SLOT(slotRecordMIDISegmentUpdated(Segment*, timeT)));
+ connect(doc, SIGNAL(stoppedAudioRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(stoppedMIDIRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(audioFileFinalized(Segment*)),
+ getModel(), SLOT(slotAudioFileFinalized(Segment*)));
+ }
+
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(&doc->getAudioPreviewThread());
+ }
+
+ if (doc) {
+ doc->getAudioPreviewThread().setEmptyQueueListener(this);
+ }
+
+ m_segmentsDrawBuffer.setOptimization(QPixmap::BestOptim);
+ m_artifactsDrawBuffer.setOptimization(QPixmap::BestOptim);
+
+ viewport()->setMouseTracking(true);
+}
+
+void CompositionView::endAudioPreviewGeneration()
+{
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(m_model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(0);
+ }
+}
+
+void CompositionView::setBackgroundPixmap(const QPixmap &m)
+{
+ m_backgroundPixmap = m;
+ // viewport()->setErasePixmap(m_backgroundPixmap);
+}
+
+void CompositionView::initStepSize()
+{
+ QScrollBar* hsb = horizontalScrollBar();
+ m_stepSize = hsb->lineStep();
+}
+
+void CompositionView::slotUpdateSize()
+{
+ int vStep = getModel()->grid().getYSnap();
+ int height = std::max(getModel()->getNbRows() * vStep, (unsigned)visibleHeight());
+
+ RulerScale *ruler = grid().getRulerScale();
+
+ int minWidth = sizeHint().width();
+ int computedWidth = int(nearbyint(ruler->getTotalWidth()));
+
+ int width = std::max(computedWidth, minWidth);
+
+ resizeContents(width, height);
+}
+
+void CompositionView::scrollRight()
+{
+ RG_DEBUG << "CompositionView::scrollRight()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::scrollLeft()
+{
+ RG_DEBUG << "CompositionView::scrollLeft()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ int cWidth = contentsWidth();
+
+ if (horizontalScrollBar()->value() < cWidth && cWidth > m_minWidth) {
+ resizeContents(cWidth - m_stepSize, contentsHeight());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::setSelectionRectPos(const QPoint& pos)
+{
+ m_selectionRect.setRect(pos.x(), pos.y(), 0, 0);
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setSelectionRectSize(int w, int h)
+{
+ m_selectionRect.setSize(QSize(w, h));
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setDrawSelectionRect(bool d)
+{
+ if (m_drawSelectionRect != d) {
+ m_drawSelectionRect = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+ slotUpdateSegmentsDrawBuffer(m_selectionRect);
+ }
+}
+
+void CompositionView::clearSegmentRectsCache(bool clearPreviews)
+{
+ dynamic_cast<CompositionModelImpl*>(getModel())->clearSegmentRectsCache(clearPreviews);
+}
+
+SegmentSelection
+CompositionView::getSelectedSegments()
+{
+ return (dynamic_cast<CompositionModelImpl*>(m_model))->getSelectedSegments();
+}
+
+void CompositionView::updateSelectionContents()
+{
+ if (!haveSelection())
+ return ;
+
+
+ QRect selectionRect = getModel()->getSelectionContentsRect();
+ updateContents(selectionRect);
+}
+
+void CompositionView::slotContentsMoving(int x, int y)
+{
+ // qDebug("contents moving : x=%d", x);
+}
+
+void CompositionView::slotSetTool(const QString& toolName)
+{
+ RG_DEBUG << "CompositionView::slotSetTool(" << toolName << ")"
+ << this << "\n";
+
+ if (m_tool)
+ m_tool->stow();
+
+ m_toolContextHelp = "";
+
+ m_tool = m_toolBox->getTool(toolName);
+
+ if (m_tool)
+ m_tool->ready();
+ else {
+ KMessageBox::error(0, QString("CompositionView::slotSetTool() : unknown tool name %1").arg(toolName));
+ }
+}
+
+void CompositionView::slotSelectSegments(const SegmentSelection &segments)
+{
+ RG_DEBUG << "CompositionView::slotSelectSegments\n";
+
+ static QRect dummy;
+
+ getModel()->clearSelected();
+
+ for (SegmentSelection::iterator i = segments.begin(); i != segments.end(); ++i) {
+ getModel()->setSelected(CompositionItem(new CompositionItemImpl(**i, dummy)));
+ }
+ slotUpdateSegmentsDrawBuffer();
+}
+
+SegmentSelector*
+CompositionView::getSegmentSelectorTool()
+{
+ return dynamic_cast<SegmentSelector*>(getToolBox()->getTool(SegmentSelector::ToolName));
+}
+
+void CompositionView::slotSetSelectAdd(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentAdd(value);
+}
+
+void CompositionView::slotSetSelectCopy(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentCopy(value);
+}
+
+void CompositionView::slotShowSplitLine(int x, int y)
+{
+ m_splitLinePos.setX(x);
+ m_splitLinePos.setY(y);
+}
+
+void CompositionView::slotHideSplitLine()
+{
+ m_splitLinePos.setX( -1);
+ m_splitLinePos.setY( -1);
+}
+
+void CompositionView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ e->accept();
+ wheelEvent(e);
+}
+
+CompositionItem CompositionView::getFirstItemAt(QPoint pos)
+{
+ CompositionModel::itemcontainer items = getModel()->getItemsAt(pos);
+
+ if (items.size()) {
+ // find topmost item
+ CompositionItem res = *(items.begin());
+
+ unsigned int maxZ = res->z();
+
+ CompositionModel::itemcontainer::iterator maxZItemPos = items.begin();
+
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i) {
+ CompositionItem ic = *i;
+ if (ic->z() > maxZ) {
+ RG_DEBUG << k_funcinfo << "found new topmost at z=" << ic->z() << endl;
+ res = ic;
+ maxZ = ic->z();
+ maxZItemPos = i;
+ }
+ }
+
+ // get rid of the rest;
+ items.erase(maxZItemPos);
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i)
+ delete *i;
+
+ return res;
+ } else {
+ RG_DEBUG << k_funcinfo << "no item under cursor\n";
+ }
+
+
+ return CompositionItem();
+}
+
+void CompositionView::setSnapGrain(bool fine)
+{
+ if (m_fineGrain) {
+ grid().setSnapTime(SnapGrid::NoSnap);
+ } else {
+ grid().setSnapTime(fine ? SnapGrid::SnapToBeat : SnapGrid::SnapToBar);
+ }
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer()
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer()\n";
+ slotAllDrawBuffersNeedRefresh();
+ updateContents();
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer() rect "
+ // << rect << " - valid : " << rect.isValid() << endl;
+
+ slotAllDrawBuffersNeedRefresh(rect);
+
+ if (rect.isValid()) {
+ updateContents(rect);
+ } else {
+ updateContents();
+ }
+}
+
+void CompositionView::slotRefreshColourCache()
+{
+ CompositionColourCache::getInstance()->init();
+ clearSegmentRectsCache();
+ slotUpdateSegmentsDrawBuffer();
+}
+
+void CompositionView::slotNewMIDIRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotNewAudioRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotStoppedRecording()
+{
+ getModel()->clearRecordingItems();
+}
+
+void CompositionView::resizeEvent(QResizeEvent* e)
+{
+ QScrollView::resizeEvent(e);
+ slotUpdateSize();
+
+ int w = std::max(m_segmentsDrawBuffer.width(), visibleWidth());
+ int h = std::max(m_segmentsDrawBuffer.height(), visibleHeight());
+
+ m_segmentsDrawBuffer.resize(w, h);
+ m_artifactsDrawBuffer.resize(w, h);
+ slotAllDrawBuffersNeedRefresh();
+ // RG_DEBUG << "CompositionView::resizeEvent() : drawBuffer size = " << m_segmentsDrawBuffer.size() << endl;
+}
+
+void CompositionView::viewportPaintEvent(QPaintEvent* e)
+{
+ QMemArray<QRect> rects = e->region().rects();
+
+ for (unsigned int i = 0; i < rects.size(); ++i) {
+ viewportPaintRect(rects[i]);
+ }
+}
+
+void CompositionView::viewportPaintRect(QRect r)
+{
+ QRect updateRect = r;
+
+ r &= viewport()->rect();
+ r.moveBy(contentsX(), contentsY());
+
+ // RG_DEBUG << "CompositionView::viewportPaintRect() r = " << r
+ // << " - moveBy " << contentsX() << "," << contentsY() << " - updateRect = " << updateRect
+ // << " - refresh " << m_segmentsDrawBufferRefresh << " artrefresh " << m_artifactsDrawBufferRefresh << endl;
+
+
+ bool scroll = false;
+ bool changed = checkScrollAndRefreshDrawBuffer(r, scroll);
+
+ if (changed || m_artifactsDrawBufferRefresh.isValid()) {
+
+ // r was modified by checkScrollAndRefreshDrawBuffer
+ QRect copyRect(r | m_artifactsDrawBufferRefresh);
+ copyRect.moveBy( -contentsX(), -contentsY());
+
+ // RG_DEBUG << "copying from segment to artifacts buffer: " << copyRect << endl;
+
+ bitBlt(&m_artifactsDrawBuffer,
+ copyRect.x(), copyRect.y(),
+ &m_segmentsDrawBuffer,
+ copyRect.x(), copyRect.y(), copyRect.width(), copyRect.height());
+ m_artifactsDrawBufferRefresh |= r;
+ }
+
+ if (m_artifactsDrawBufferRefresh.isValid()) {
+ refreshArtifactsDrawBuffer(m_artifactsDrawBufferRefresh);
+ m_artifactsDrawBufferRefresh = QRect();
+ }
+
+ if (scroll) {
+ bitBlt(viewport(), 0, 0,
+ &m_artifactsDrawBuffer, 0, 0,
+ m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ } else {
+ bitBlt(viewport(), updateRect.x(), updateRect.y(),
+ &m_artifactsDrawBuffer, updateRect.x(), updateRect.y(),
+ updateRect.width(), updateRect.height());
+ }
+
+ // DEBUG
+
+ // QPainter pdebug(viewport());
+ // static QPen framePen(Qt::red, 1);
+ // pdebug.setPen(framePen);
+ // pdebug.drawRect(updateRect);
+
+}
+
+bool CompositionView::checkScrollAndRefreshDrawBuffer(QRect &rect, bool& scroll)
+{
+ bool all = false;
+ QRect refreshRect = m_segmentsDrawBufferRefresh;
+
+ int w = visibleWidth(), h = visibleHeight();
+ int cx = contentsX(), cy = contentsY();
+
+ scroll = (cx != m_lastBufferRefreshX || cy != m_lastBufferRefreshY);
+
+ if (scroll) {
+
+ // RG_DEBUG << "checkScrollAndRefreshDrawBuffer: scrolling by ("
+ // << cx - m_lastBufferRefreshX << "," << cy - m_lastBufferRefreshY << ")" << endl;
+
+ if (refreshRect.isValid()) {
+
+ // If we've scrolled and there was an existing refresh
+ // rect, we can't be sure whether the refresh rect
+ // predated or postdated the internal update of scroll
+ // location. Cut our losses and refresh everything.
+
+ refreshRect.setRect(cx, cy, w, h);
+
+ } else {
+
+ // No existing refresh rect: we only need to handle the
+ // scroll
+
+ if (cx != m_lastBufferRefreshX) {
+
+ int dx = m_lastBufferRefreshX - cx;
+
+ if (dx > -w && dx < w) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(dx, 0, m_segmentsDrawBuffer);
+
+ if (dx < 0) {
+ refreshRect |= QRect(cx + w + dx, cy, -dx, h);
+ } else {
+ refreshRect |= QRect(cx, cy, dx, h);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+
+ if (cy != m_lastBufferRefreshY && !all) {
+
+ int dy = m_lastBufferRefreshY - cy;
+
+ if (dy > -h && dy < h) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(0, dy, m_segmentsDrawBuffer);
+
+ if (dy < 0) {
+ refreshRect |= QRect(cx, cy + h + dy, w, -dy);
+ } else {
+ refreshRect |= QRect(cx, cy, w, dy);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+ }
+ }
+
+ bool needRefresh = false;
+
+ if (refreshRect.isValid()) {
+ needRefresh = true;
+ }
+
+ if (needRefresh)
+ refreshSegmentsDrawBuffer(refreshRect);
+
+ m_segmentsDrawBufferRefresh = QRect();
+ m_lastBufferRefreshX = cx;
+ m_lastBufferRefreshY = cy;
+
+ rect |= refreshRect;
+ if (scroll)
+ rect.setRect(cx, cy, w, h);
+ return needRefresh;
+}
+
+void CompositionView::refreshSegmentsDrawBuffer(const QRect& rect)
+{
+ // Profiler profiler("CompositionView::refreshDrawBuffer", true);
+ // RG_DEBUG << "CompositionView::refreshSegmentsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p(&m_segmentsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+
+ if (!m_backgroundPixmap.isNull()) {
+ QPoint pp(rect.x() % m_backgroundPixmap.height(), rect.y() % m_backgroundPixmap.width());
+ p.drawTiledPixmap(rect, m_backgroundPixmap, pp);
+ } else {
+ p.eraseRect(rect);
+ }
+
+ drawArea(&p, rect);
+
+ // DEBUG - show what's updated
+ // QPen framePen(Qt::red, 1);
+ // p.setPen(framePen);
+ // p.drawRect(rect);
+
+ // m_segmentsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::refreshArtifactsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::refreshArtifactsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p;
+ p.begin(&m_artifactsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+ // QRect r(contentsX(), contentsY(), m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ drawAreaArtifacts(&p, rect);
+ p.end();
+
+ // m_artifactsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::drawArea(QPainter *p, const QRect& clipRect)
+{
+ // Profiler profiler("CompositionView::drawArea", true);
+
+ // RG_DEBUG << "CompositionView::drawArea() clipRect = " << clipRect << endl;
+
+ //
+ // Fetch track dividing lines
+ //
+ CompositionModel::heightlist lineHeights = getModel()->getTrackDividersIn(clipRect);
+
+ if (!lineHeights.empty()) {
+
+ p->save();
+ QColor light = m_trackDividerColor.light();
+ p->setPen(light);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-1,
+ clipRect.x() + clipRect.width() - 1, y-1);
+ }
+ if (y >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y,
+ clipRect.x() + clipRect.width() - 1, y);
+ }
+ }
+
+ p->setPen(m_trackDividerColor);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-2 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-2,
+ clipRect.x() + clipRect.width() - 1, y-2);
+ }
+ if (y+1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y+1,
+ clipRect.x() + clipRect.width() - 1, y+1);
+ }
+ }
+
+ p->restore();
+ }
+
+ CompositionModel::AudioPreviewDrawData* audioPreviewData = 0;
+ CompositionModel::RectRanges* notationPreviewData = 0;
+
+ //
+ // Fetch previews
+ //
+ if (m_showPreviews) {
+ notationPreviewData = &m_notationPreviewRects;
+ m_notationPreviewRects.clear();
+ audioPreviewData = &m_audioPreviewRects;
+ m_audioPreviewRects.clear();
+ }
+
+ //
+ // Fetch segment rectangles to draw
+ //
+ const CompositionModel::rectcontainer& rects = getModel()->getRectanglesIn(clipRect,
+ notationPreviewData, audioPreviewData);
+ CompositionModel::rectcontainer::const_iterator i = rects.begin();
+ CompositionModel::rectcontainer::const_iterator end = rects.end();
+
+ //
+ // Draw Segment Rectangles
+ //
+ p->save();
+ for (; i != end; ++i) {
+ p->setBrush(i->getBrush());
+ p->setPen(i->getPen());
+
+ // RG_DEBUG << "CompositionView::drawArea : draw comp rect " << *i << endl;
+ drawCompRect(*i, p, clipRect);
+ }
+
+ p->restore();
+
+ if (rects.size() > 1) {
+ // RG_DEBUG << "CompositionView::drawArea : drawing intersections\n";
+ drawIntersections(rects, p, clipRect);
+ }
+
+ //
+ // Previews
+ //
+ if (m_showPreviews) {
+ p->save();
+
+ // draw audio previews
+ //
+ drawAreaAudioPreviews(p, clipRect);
+
+ // draw notation previews
+ //
+ CompositionModel::RectRanges::const_iterator npi = m_notationPreviewRects.begin();
+ CompositionModel::RectRanges::const_iterator npEnd = m_notationPreviewRects.end();
+
+ for (; npi != npEnd; ++npi) {
+ CompositionModel::RectRange interval = *npi;
+ p->save();
+ p->translate(interval.basePoint.x(), interval.basePoint.y());
+ // RG_DEBUG << "CompositionView::drawArea : translating to x = " << interval.basePoint.x() << endl;
+ for (; interval.range.first != interval.range.second; ++interval.range.first) {
+
+ const PreviewRect& pr = *(interval.range.first);
+ QColor defaultCol = CompositionColourCache::getInstance()->SegmentInternalPreview;
+ QColor col = interval.color.isValid() ? interval.color : defaultCol;
+ p->setBrush(col);
+ p->setPen(col);
+ // RG_DEBUG << "CompositionView::drawArea : drawing preview rect at x = " << pr.x() << endl;
+ p->drawRect(pr);
+ }
+ p->restore();
+ }
+
+ p->restore();
+ }
+
+ //
+ // Draw segment labels (they must be drawn over the preview rects)
+ //
+ if (m_showSegmentLabels) {
+ for (i = rects.begin(); i != end; ++i) {
+ drawCompRectLabel(*i, p, clipRect);
+ }
+ }
+
+ // drawAreaArtifacts(p, clipRect);
+
+}
+
+void CompositionView::drawAreaAudioPreviews(QPainter * p, const QRect& clipRect)
+{
+ CompositionModel::AudioPreviewDrawData::const_iterator api = m_audioPreviewRects.begin();
+ CompositionModel::AudioPreviewDrawData::const_iterator apEnd = m_audioPreviewRects.end();
+ QRect rectToFill, // rect to fill on canvas
+ localRect; // the rect of the tile to draw on the canvas
+ QPoint basePoint, // origin of segment rect
+ drawBasePoint; // origin of rect to fill on canvas
+ QRect r;
+ for (; api != apEnd; ++api) {
+ rectToFill = api->rect;
+ basePoint = api->basePoint;
+ rectToFill.moveTopLeft(basePoint);
+ rectToFill &= clipRect;
+ r = rectToFill;
+ drawBasePoint = rectToFill.topLeft();
+ rectToFill.moveBy( -basePoint.x(), -basePoint.y());
+ int firstPixmapIdx = (r.x() - basePoint.x()) / AudioPreviewPainter::tileWidth();
+ if (firstPixmapIdx >= api->pixmap.size()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : WARNING - miscomputed pixmap array : r.x = "
+ // << r.x() << " - basePoint.x = " << basePoint.x() << " - firstPixmapIdx = " << firstPixmapIdx
+ // << endl;
+ continue;
+ }
+ int x = 0, idx = firstPixmapIdx;
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : clipRect = " << clipRect
+ // << " - firstPixmapIdx = " << firstPixmapIdx << endl;
+ while (x < clipRect.width()) {
+ int pixmapRectXOffset = idx * AudioPreviewPainter::tileWidth();
+ localRect.setRect(basePoint.x() + pixmapRectXOffset, basePoint.y(),
+ AudioPreviewPainter::tileWidth(), api->rect.height());
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : initial localRect = "
+ // << localRect << endl;
+ localRect &= r;
+ if (idx == firstPixmapIdx && api->resizeOffset != 0) {
+ // this segment is being resized from start, clip beginning of preview
+ localRect.moveBy(api->resizeOffset, 0);
+ }
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect = "
+ // << localRect << endl;
+ if (localRect.isEmpty()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect is empty\n";
+ break;
+ }
+ localRect.moveBy( -(basePoint.x() + pixmapRectXOffset), -basePoint.y());
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : drawing pixmap "
+ // << idx << " at " << drawBasePoint << " - localRect = " << localRect
+ // << " - preResizeOrigin : " << api->preResizeOrigin << endl;
+
+ p->drawImage(drawBasePoint, api->pixmap[idx], localRect,
+ Qt::ColorOnly | Qt::ThresholdDither | Qt::AvoidDither);
+
+ ++idx;
+ if (idx >= api->pixmap.size())
+ break;
+ drawBasePoint.setX(drawBasePoint.x() + localRect.width());
+ x += localRect.width();
+ }
+ }
+}
+
+void CompositionView::drawAreaArtifacts(QPainter * p, const QRect& clipRect)
+{
+ //
+ // Playback Pointer
+ //
+ drawPointer(p, clipRect);
+
+ //
+ // Tmp rect (rect displayed while drawing a new segment)
+ //
+ if (m_tmpRect.isValid() && m_tmpRect.intersects(clipRect)) {
+ p->setBrush(m_tmpRectFill);
+ p->setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ drawRect(m_tmpRect, p, clipRect);
+ }
+
+ //
+ // Tool guides (crosshairs)
+ //
+ if (m_drawGuides)
+ drawGuides(p, clipRect);
+
+ //
+ // Selection Rect
+ //
+ if (m_drawSelectionRect) {
+ drawRect(m_selectionRect, p, clipRect, false, 0, false);
+ }
+
+ //
+ // Floating Text
+ //
+ if (m_drawTextFloat)
+ drawTextFloat(p, clipRect);
+
+ //
+ // Split line
+ //
+ if (m_splitLinePos.x() > 0 && clipRect.contains(m_splitLinePos)) {
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(m_splitLinePos.x(), m_splitLinePos.y(),
+ m_splitLinePos.x(), m_splitLinePos.y() + getModel()->grid().getYSnap());
+ p->restore();
+ }
+}
+
+void CompositionView::drawGuides(QPainter * p, const QRect& /*clipRect*/)
+{
+ // no need to check for clipping, these guides are meant to follow the mouse cursor
+ QPoint guideOrig(m_topGuidePos, m_foreGuidePos);
+
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(guideOrig.x(), 0, guideOrig.x(), contentsHeight());
+ p->drawLine(0, guideOrig.y(), contentsWidth(), guideOrig.y());
+ p->restore();
+}
+
+void CompositionView::drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl, bool fill)
+{
+ p->save();
+
+ QBrush brush = r.getBrush();
+
+ if (r.isRepeating()) {
+ QColor brushColor = brush.color();
+ brush.setColor(brushColor.light(150));
+ }
+
+ p->setBrush(brush);
+ p->setPen(r.getPen());
+ drawRect(r, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+ if (r.isRepeating()) {
+
+ CompositionRect::repeatmarks repeatMarks = r.getRepeatMarks();
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeating rect " << r
+ // << " nb repeat marks = " << repeatMarks.size() << endl;
+
+ // draw 'start' rectangle with original brush
+ //
+ QRect startRect = r;
+ startRect.setWidth(repeatMarks[0] - r.x());
+ p->setBrush(r.getBrush());
+ drawRect(startRect, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+
+ // now draw the 'repeat' marks
+ //
+ p->setPen(CompositionColourCache::getInstance()->RepeatSegmentBorder);
+ int penWidth = std::max(r.getPen().width(), 1u);
+
+ for (unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ int pos = repeatMarks[i];
+ if (pos > clipRect.right())
+ break;
+
+ if (pos >= clipRect.left()) {
+ QPoint p1(pos, r.y() + penWidth),
+ p2(pos, r.y() + r.height() - penWidth - 1);
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeat mark at "
+ // << p1 << "-" << p2 << endl;
+ p->drawLine(p1, p2);
+ }
+
+ }
+
+ }
+
+ p->restore();
+}
+
+void CompositionView::drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect)
+{
+ // draw segment label
+ //
+#ifdef NOT_DEFINED
+ if (!r.getLabel().isEmpty() /* && !r.isSelected() */)
+ {
+ p->save();
+ p->setPen(GUIPalette::getColour(GUIPalette::SegmentLabel));
+ p->setBrush(white);
+ QRect textRect(r);
+ textRect.setX(textRect.x() + 3);
+ QString label = " " + r.getLabel() + " ";
+ QRect textBoundingRect = p->boundingRect(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->drawRect(textBoundingRect & r);
+ p->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->restore();
+ }
+#else
+ if (!r.getLabel().isEmpty()) {
+
+ p->save();
+
+ QFont font;
+ font.setPixelSize(r.height() / 2.2);
+ font.setWeight(QFont::Bold);
+ font.setItalic(false);
+ p->setFont(font);
+
+ QRect labelRect = QRect
+ (r.x(),
+ r.y() + ((r.height() - p->fontMetrics().height()) / 2) + 1,
+ r.width(),
+ p->fontMetrics().height());
+
+ int x = labelRect.x() + p->fontMetrics().width('x');
+ int y = labelRect.y();
+
+ QBrush brush = r.getBrush();
+ QColor surroundColour = brush.color().light(110);
+
+ int h, s, v;
+ surroundColour.hsv(&h, &s, &v);
+ if (v < 150)
+ surroundColour.setHsv(h, s, 225);
+ p->setPen(surroundColour);
+
+ for (int i = 0; i < 9; ++i) {
+
+ if (i == 4)
+ continue;
+
+ int wx = x, wy = y;
+
+ if (i < 3)
+ --wx;
+ if (i > 5)
+ ++wx;
+ if (i % 3 == 0)
+ --wy;
+ if (i % 3 == 2)
+ ++wy;
+
+ labelRect.setX(wx);
+ labelRect.setY(wy);
+
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignTop,
+ r.getLabel());
+ }
+
+ labelRect.setX(x);
+ labelRect.setY(y);
+
+ p->setPen(GUIPalette::getColour
+ (GUIPalette::SegmentLabel));
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignVCenter, r.getLabel());
+ p->restore();
+ }
+#endif
+}
+
+void CompositionView::drawRect(const QRect& r, QPainter *p, const QRect& clipRect,
+ bool isSelected, int intersectLvl, bool fill)
+{
+ // RG_DEBUG << "CompositionView::drawRect : intersectLvl = " << intersectLvl
+ // << " - brush col = " << p->brush().color() << endl;
+
+ // RG_DEBUG << "CompositionView::drawRect " << r << " - xformed : " << p->xForm(r)
+ // << " - contents x = " << contentsX() << ", contents y = " << contentsY() << endl;
+
+ p->save();
+
+ QRect rect = r;
+
+ if (fill) {
+ if (isSelected) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark(200);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : selected color : " << fillColor << endl;
+ }
+
+ if (intersectLvl > 0) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark((intersectLvl) * 105);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : intersected color : " << fillColor << " isSelected : " << isSelected << endl;
+ }
+ } else {
+ p->setBrush(Qt::NoBrush);
+ }
+
+ // Paint using the small coordinates...
+ QRect intersection = rect.intersect(clipRect);
+
+ if (clipRect.contains(rect)) {
+ p->drawRect(rect);
+ } else {
+ // draw only what's necessary
+ if (!intersection.isEmpty() && fill)
+ p->fillRect(intersection, p->brush());
+
+ int rectTopY = rect.y();
+
+ if (rectTopY >= clipRect.y() &&
+ rectTopY <= (clipRect.y() + clipRect.height())) {
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.topLeft(), intersection.topRight());
+ }
+
+ int rectBottomY = rect.y() + rect.height();
+ if (rectBottomY >= clipRect.y() &&
+ rectBottomY <= (clipRect.y() + clipRect.height()))
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.bottomLeft(), intersection.bottomRight());
+
+ int rectLeftX = rect.x();
+ if (rectLeftX >= clipRect.x() &&
+ rectLeftX <= (clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topLeft(), rect.bottomLeft());
+
+ unsigned int rectRightX = rect.x() + rect.width(); // make sure we don't overflow
+ if (rectRightX >= unsigned(clipRect.x()) &&
+ rectRightX <= unsigned(clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topRight(), rect.bottomRight());
+
+ }
+
+ p->restore();
+}
+
+QColor CompositionView::mixBrushes(QBrush a, QBrush b)
+{
+ QColor ac = a.color(), bc = b.color();
+
+ int aR = ac.red(), aG = ac.green(), aB = ac.blue(),
+ bR = bc.red(), bG = bc.green(), bB = ac.blue();
+
+ ac.setRgb((aR + bR) / 2, (aG + bG) / 2, (aB + bB) / 2);
+
+ return ac;
+}
+
+void CompositionView::drawIntersections(const CompositionModel::rectcontainer& rects,
+ QPainter * p, const QRect& clipRect)
+{
+ if (! (rects.size() > 1))
+ return ;
+
+ CompositionModel::rectcontainer intersections;
+
+ CompositionModel::rectcontainer::const_iterator i = rects.begin(),
+ j = rects.begin();
+
+ for (; j != rects.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == rects.end())
+ break;
+
+ for (; i != rects.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty()) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections.begin(),
+ intersections.end(), ri);
+ if (t == intersections.end()) {
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ ri.setSelected(testRect.isSelected() || i->isSelected());
+ intersections.push_back(ri);
+ }
+
+ }
+ }
+ }
+
+ //
+ // draw this level of intersections then compute and draw further ones
+ //
+ int intersectionLvl = 1;
+
+ while (!intersections.empty()) {
+
+ for (CompositionModel::rectcontainer::iterator intIter = intersections.begin();
+ intIter != intersections.end(); ++intIter) {
+ CompositionRect r = *intIter;
+ drawCompRect(r, p, clipRect, intersectionLvl);
+ }
+
+ if (intersections.size() > 10)
+ break; // put a limit on how many intersections we can compute and draw - this grows exponentially
+
+ ++intersectionLvl;
+
+ CompositionModel::rectcontainer intersections2;
+
+ CompositionModel::rectcontainer::iterator i = intersections.begin(),
+ j = intersections.begin();
+
+ for (; j != intersections.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == intersections.end())
+ break;
+
+ for (; i != intersections.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty() && ri != *i) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections2.begin(),
+ intersections2.end(), ri);
+ if (t == intersections2.end())
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ intersections2.push_back(ri);
+ }
+ }
+ }
+
+ intersections = intersections2;
+ }
+
+}
+
+void CompositionView::drawPointer(QPainter *p, const QRect& clipRect)
+{
+ // RG_DEBUG << "CompositionView::drawPointer: clipRect "
+ // << clipRect.x() << "," << clipRect.y() << " " << clipRect.width()
+ // << "x" << clipRect.height() << " pointer pos is " << m_pointerPos << endl;
+
+ if (m_pointerPos >= clipRect.x() && m_pointerPos <= (clipRect.x() + clipRect.width())) {
+ p->save();
+ p->setPen(m_pointerPen);
+ p->drawLine(m_pointerPos, clipRect.y(), m_pointerPos, clipRect.y() + clipRect.height());
+ p->restore();
+ }
+
+}
+
+void CompositionView::drawTextFloat(QPainter *p, const QRect& clipRect)
+{
+ QFontMetrics metrics(p->fontMetrics());
+
+ QRect bound = p->boundingRect(0, 0, 300, metrics.height() + 6, AlignAuto, m_textFloatText);
+
+ p->save();
+
+ bound.setLeft(bound.left() - 2);
+ bound.setRight(bound.right() + 2);
+ bound.setTop(bound.top() - 2);
+ bound.setBottom(bound.bottom() + 2);
+
+ QPoint pos(m_textFloatPos);
+ if (pos.y() < 0 && getModel()) {
+ if (pos.y() + bound.height() < 0) {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 3);
+ } else {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 2);
+ }
+ }
+
+ bound.moveTopLeft(pos);
+
+ if (bound.intersects(clipRect)) {
+
+ p->setBrush(CompositionColourCache::getInstance()->RotaryFloatBackground);
+
+ drawRect(bound, p, clipRect, false, 0, true);
+
+ p->setPen(CompositionColourCache::getInstance()->RotaryFloatForeground);
+
+ p->drawText(pos.x() + 2, pos.y() + 3 + metrics.ascent(), m_textFloatText);
+
+ }
+
+ p->restore();
+}
+
+bool CompositionView::event(QEvent* e)
+{
+ if (e->type() == AudioPreviewThread::AudioPreviewQueueEmpty) {
+ RG_DEBUG << "CompositionView::event - AudioPreviewQueueEmpty\n";
+ slotSegmentsDrawBufferNeedsRefresh();
+ viewport()->update();
+ return true;
+ }
+
+ return RosegardenScrollView::event(e);
+}
+
+void CompositionView::enterEvent(QEvent *e)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ emit showContextHelp(m_toolContextHelp);
+ m_contextHelpShown = true;
+}
+
+void CompositionView::leaveEvent(QEvent *e)
+{
+ emit showContextHelp("");
+ m_contextHelpShown = false;
+}
+
+void CompositionView::slotToolHelpChanged(const QString &text)
+{
+ if (m_toolContextHelp == text) return;
+ m_toolContextHelp = text;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ if (m_contextHelpShown) emit showContextHelp(text);
+}
+
+void CompositionView::contentsMousePressEvent(QMouseEvent* e)
+{
+ Qt::ButtonState bs = e->state();
+ slotSetSelectCopy((bs & Qt::ControlButton) != 0);
+ slotSetSelectAdd((bs & Qt::ShiftButton) != 0);
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton + Qt::ControlButton) != 0);
+
+ switch (e->button()) {
+ case LeftButton:
+ case MidButton:
+ startAutoScroll();
+
+ if (m_tool)
+ m_tool->handleMouseButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ case RightButton:
+ if (m_tool)
+ m_tool->handleRightButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ default:
+ break;
+ }
+}
+
+void CompositionView::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ RG_DEBUG << "CompositionView::contentsMouseReleaseEvent()\n";
+
+ stopAutoScroll();
+
+ if (!m_tool)
+ return ;
+
+ if (e->button() == LeftButton ||
+ e->button() == MidButton )
+ m_tool->handleMouseButtonRelease(e);
+}
+
+void CompositionView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ m_currentItem = getFirstItemAt(e->pos());
+
+ if (!m_currentItem) {
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - no currentItem\n";
+ RulerScale *ruler = grid().getRulerScale();
+ if (ruler) emit setPointerPosition(ruler->getTimeForX(e->pos().x()));
+ return ;
+ }
+
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - have currentItem\n";
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ if (m_currentItem->isRepeating()) {
+ timeT time = getModel()->getRepeatTimeAt(e->pos(), m_currentItem);
+
+ RG_DEBUG << "editRepeat at time " << time << endl;
+ if (time > 0)
+ emit editRepeat(itemImpl->getSegment(), time);
+ else
+ emit editSegment(itemImpl->getSegment());
+
+ } else {
+
+ emit editSegment(itemImpl->getSegment());
+ }
+}
+
+void CompositionView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (!m_tool)
+ return ;
+
+ Qt::ButtonState bs = e->state();
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton) != 0);
+
+ int follow = m_tool->handleMouseMove(e);
+ setScrollDirectionConstraint(follow);
+
+ if (follow != RosegardenCanvasView::NoFollow) {
+ doAutoScroll();
+
+ if (follow & RosegardenCanvasView::FollowHorizontal) {
+ slotScrollHorizSmallSteps(e->pos().x());
+
+ // enlarge composition if needed
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ slotUpdateSize();
+ }
+ }
+
+ if (follow & RosegardenCanvasView::FollowVertical)
+ slotScrollVertSmallSteps(e->pos().y());
+ }
+}
+
+void CompositionView::releaseCurrentItem()
+{
+ m_currentItem = CompositionItem();
+}
+
+void CompositionView::setPointerPos(int pos)
+{
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << ")\n";
+ int oldPos = m_pointerPos;
+ if (oldPos == pos)
+ return ;
+
+ m_pointerPos = pos;
+ getModel()->setPointerPos(pos);
+
+ // automagically grow contents width if pointer position goes beyond right end
+ //
+ if (pos >= (contentsWidth() - m_stepSize)) {
+ resizeContents(pos + m_stepSize, contentsHeight());
+ // grow composition too, if needed (it may not be the case if
+ if (getModel()->getLength() < contentsWidth())
+ getModel()->setLength(contentsWidth());
+ }
+
+
+ // interesting -- isAutoScrolling() never seems to return true?
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << "), isAutoScrolling " << isAutoScrolling() << ", contentsX " << contentsX() << ", m_lastPointerRefreshX " << m_lastPointerRefreshX << ", contentsHeight " << contentsHeight() << endl;
+
+ if (contentsX() != m_lastPointerRefreshX) {
+ m_lastPointerRefreshX = contentsX();
+ // We'll need to shift the whole canvas anyway, so
+ slotArtifactsDrawBufferNeedsRefresh();
+ return ;
+ }
+
+ int deltaW = abs(m_pointerPos - oldPos);
+
+ if (deltaW <= m_pointerPen.width() * 2) { // use one rect instead of two separate ones
+
+ QRect updateRect
+ (std::min(m_pointerPos, oldPos) - m_pointerPen.width(), 0,
+ deltaW + m_pointerPen.width() * 2, contentsHeight());
+
+ slotArtifactsDrawBufferNeedsRefresh(updateRect);
+
+ } else {
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(m_pointerPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(oldPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+ }
+}
+
+void CompositionView::setGuidesPos(int x, int y)
+{
+ m_topGuidePos = x;
+ m_foreGuidePos = y;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setGuidesPos(const QPoint& p)
+{
+ m_topGuidePos = p.x();
+ m_foreGuidePos = p.y();
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setDrawGuides(bool d)
+{
+ m_drawGuides = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setTmpRect(const QRect& r)
+{
+ setTmpRect(r, m_tmpRectFill);
+}
+
+void CompositionView::setTmpRect(const QRect& r, const QColor &c)
+{
+ QRect pRect = m_tmpRect;
+ m_tmpRect = r;
+ m_tmpRectFill = c;
+ slotUpdateSegmentsDrawBuffer(m_tmpRect | pRect);
+}
+
+void CompositionView::setTextFloat(int x, int y, const QString &text)
+{
+ m_textFloatPos.setX(x);
+ m_textFloatPos.setY(y);
+ m_textFloatText = text;
+ m_drawTextFloat = true;
+ slotArtifactsDrawBufferNeedsRefresh();
+
+ // most of the time when the floating text is drawn
+ // we want to update a larger part of the view
+ // so don't update here
+ // QRect r = fontMetrics().boundingRect(x, y, 300, 40, AlignAuto, m_textFloatText);
+ // slotUpdateSegmentsDrawBuffer(r);
+
+
+ // rgapp->slotSetStatusMessage(text);
+}
+
+void CompositionView::slotSetFineGrain(bool value)
+{
+ m_fineGrain = value;
+}
+
+void CompositionView::slotSetPencilOverExisting(bool value)
+{
+ m_pencilOverExisting = value;
+}
+
+void
+CompositionView::slotTextFloatTimeout()
+{
+ hideTextFloat();
+ slotArtifactsDrawBufferNeedsRefresh();
+ // rgapp->slotSetStatusMessage(QString::null);
+}
+
+}
+#include "CompositionView.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.h b/src/gui/editors/segment/segmentcanvas/CompositionView.h
new file mode 100644
index 0000000..ff0d440
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.h
@@ -0,0 +1,366 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONVIEW_H_
+#define _RG_COMPOSITIONVIEW_H_
+
+#include "base/Selection.h"
+#include "CompositionModel.h"
+#include "CompositionItem.h"
+#include "gui/general/RosegardenScrollView.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QWheelEvent;
+class QResizeEvent;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class SegmentToolBox;
+class SegmentTool;
+class SegmentSelector;
+class Segment;
+class RosegardenGUIDoc;
+class CompositionRect;
+
+
+class CompositionView : public RosegardenScrollView
+{
+ Q_OBJECT
+public:
+ CompositionView(RosegardenGUIDoc*, CompositionModel*,
+ QWidget * parent=0, const char* name=0, WFlags f=0);
+
+ void setPointerPos(int pos);
+ int getPointerPos() { return m_pointerPos; }
+
+ void setGuidesPos(int x, int y);
+ void setGuidesPos(const QPoint& p);
+ void setDrawGuides(bool d);
+
+ QRect getSelectionRect() const { return m_selectionRect; }
+ void setSelectionRectPos(const QPoint& pos);
+ void setSelectionRectSize(int w, int h);
+ void setDrawSelectionRect(bool d);
+
+ SnapGrid& grid() { return m_model->grid(); }
+
+ CompositionItem getFirstItemAt(QPoint pos);
+
+ SegmentToolBox* getToolBox() { return m_toolBox; }
+
+ CompositionModel* getModel() { return m_model; }
+
+ void setTmpRect(const QRect& r);
+ void setTmpRect(const QRect& r, const QColor &c);
+ const QRect& getTmpRect() const { return m_tmpRect; }
+
+ /**
+ * Set the snap resolution of the grid to something suitable.
+ *
+ * fineTool indicates whether the current tool is a fine-grain sort
+ * (such as the resize or move tools) or a coarse one (such as the
+ * segment creation pencil). If the user is requesting extra-fine
+ * resolution (through the setFineGrain method) that will also be
+ * taken into account.
+ */
+ void setSnapGrain(bool fine);
+
+ /**
+ * Find out whether the user is requesting extra-fine resolution
+ * (e.g. by holding Shift key). This is seldom necessary -- most
+ * client code will only need to query the snap grid that is
+ * adjusted appropriately by the view when interactions take
+ * place.
+ */
+ bool isFineGrain() const { return m_fineGrain; }
+
+ /**
+ * Find out whether the user is requesting to draw over an existing segment
+ * with the pencil, by holding the Ctrl key. This is used by the segment
+ * pencil to decide whether to abort or not if a user attempts to draw over
+ * an existing segment, and this is all necessary in order to avoid breaking
+ * the double-click-to-open behavior.
+ */
+ bool pencilOverExisting() const { return m_pencilOverExisting; }
+
+ /**
+ * Set whether the segment items contain previews or not
+ */
+ void setShowPreviews(bool previews) { m_showPreviews = previews; }
+
+ /**
+ * Return whether the segment items contain previews or not
+ */
+ bool isShowingPreviews() { return m_showPreviews; }
+
+ /**
+ * clear all seg rect cache
+ */
+ void clearSegmentRectsCache(bool clearPreviews = false);
+
+ /// Return the selected Segments if we're currently using a "Selector"
+ SegmentSelection getSelectedSegments();
+
+ bool haveSelection() const { return m_model->haveSelection(); }
+
+ void updateSelectionContents();
+
+ /**
+ * Set and hide a text float on this canvas - it can contain
+ * anything and can be left to timeout or you can hide it
+ * explicitly.
+ *
+ */
+ void setTextFloat(int x, int y, const QString &text);
+ void hideTextFloat() { m_drawTextFloat = false; }
+
+ void setShowSegmentLabels(bool b) { m_showSegmentLabels = b; }
+
+ void setBackgroundPixmap(const QPixmap &m);
+
+ void endAudioPreviewGeneration();
+
+public slots:
+ void scrollRight();
+ void scrollLeft();
+ void slotContentsMoving(int x, int y);
+
+ /// Set the current segment editing tool
+ void slotSetTool(const QString& toolName);
+
+ // This method only operates if we're of the "Selector"
+ // tool type - it's called from the View to enable it
+ // to automatically set the selection of Segments (say
+ // by Track).
+ //
+ void slotSelectSegments(const SegmentSelection &segment);
+
+ // These are sent from the top level app when it gets key
+ // depresses relating to selection add (usually SHIFT) and
+ // selection copy (usually CONTROL)
+ //
+ void slotSetSelectAdd(bool value);
+ void slotSetSelectCopy(bool value);
+
+ void slotSetFineGrain(bool value);
+ void slotSetPencilOverExisting(bool value);
+
+ // Show and hige the splitting line on a Segment
+ //
+ void slotShowSplitLine(int x, int y);
+ void slotHideSplitLine();
+
+ void slotExternalWheelEvent(QWheelEvent*);
+
+ // TextFloat timer
+ void slotTextFloatTimeout();
+
+ void slotUpdateSegmentsDrawBuffer();
+ void slotUpdateSegmentsDrawBuffer(const QRect&);
+
+ void slotRefreshColourCache();
+
+ void slotNewMIDIRecordingSegment(Segment*);
+ void slotNewAudioRecordingSegment(Segment*);
+ // no longer used, see RosegardenGUIDoc::insertRecordedMidi
+// void slotRecordMIDISegmentUpdated(Segment*, timeT updatedFrom);
+ void slotStoppedRecording();
+
+ void slotUpdateSize();
+
+signals:
+ void editSegment(Segment*); // use default editor
+ void editSegmentNotation(Segment*);
+ void editSegmentMatrix(Segment*);
+ void editSegmentAudio(Segment*);
+ void editSegmentEventList(Segment*);
+ void audioSegmentAutoSplit(Segment*);
+ void editRepeat(Segment*, timeT);
+
+ void setPointerPosition(timeT);
+
+ void showContextHelp(const QString &);
+
+protected:
+ virtual bool event(QEvent *);
+
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ virtual void viewportPaintEvent(QPaintEvent*);
+ virtual void resizeEvent(QResizeEvent*);
+
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ virtual void viewportPaintRect(QRect);
+
+ /**
+ * if something changed, returns true and sets rect accordingly
+ * sets 'scroll' if some scrolling occurred
+ */
+ bool checkScrollAndRefreshDrawBuffer(QRect &, bool& scroll);
+ void refreshSegmentsDrawBuffer(const QRect&);
+ void refreshArtifactsDrawBuffer(const QRect&);
+ void drawArea(QPainter * p, const QRect& rect);
+ void drawAreaAudioPreviews(QPainter * p, const QRect& rect);
+ void drawAreaArtifacts(QPainter * p, const QRect& rect);
+ void drawRect(const QRect& rect, QPainter * p, const QRect& clipRect,
+ bool isSelected = false, int intersectLvl = 0, bool fill = true);
+ void drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl = 0, bool fill = true);
+ void drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect);
+ void drawIntersections(const CompositionModel::rectcontainer&, QPainter * p, const QRect& clipRect);
+
+ void drawPointer(QPainter * p, const QRect& clipRect);
+ void drawGuides(QPainter * p, const QRect& clipRect);
+ void drawTextFloat(QPainter * p, const QRect& clipRect);
+
+ void initStepSize();
+ void releaseCurrentItem();
+
+ static QColor mixBrushes(QBrush a, QBrush b);
+
+ SegmentSelector* getSegmentSelectorTool();
+
+protected slots:
+ void slotSegmentsDrawBufferNeedsRefresh() {
+ m_segmentsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ }
+
+ void slotSegmentsDrawBufferNeedsRefresh(QRect r) {
+ m_segmentsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh() {
+ m_artifactsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ updateContents();
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh(QRect r) {
+ m_artifactsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ updateContents(r);
+ }
+
+ void slotAllDrawBuffersNeedRefresh() {
+ slotSegmentsDrawBufferNeedsRefresh();
+ slotArtifactsDrawBufferNeedsRefresh();
+ }
+
+ void slotAllDrawBuffersNeedRefresh(QRect r) {
+ slotSegmentsDrawBufferNeedsRefresh(r);
+ slotArtifactsDrawBufferNeedsRefresh(r);
+ }
+
+ void slotToolHelpChanged(const QString &);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ CompositionModel* m_model;
+ CompositionItem m_currentItem;
+
+ SegmentTool* m_tool;
+ SegmentToolBox* m_toolBox;
+
+ bool m_showPreviews;
+ bool m_showSegmentLabels;
+ bool m_fineGrain;
+ bool m_pencilOverExisting;
+
+ int m_minWidth;
+
+ int m_stepSize;
+ QColor m_rectFill;
+ QColor m_selectedRectFill;
+
+ int m_pointerPos;
+ QColor m_pointerColor;
+ int m_pointerWidth;
+ QPen m_pointerPen;
+
+ QRect m_tmpRect;
+ QColor m_tmpRectFill;
+ QPoint m_splitLinePos;
+
+ QColor m_trackDividerColor;
+
+ bool m_drawGuides;
+ QColor m_guideColor;
+ int m_topGuidePos;
+ int m_foreGuidePos;
+
+ bool m_drawSelectionRect;
+ QRect m_selectionRect;
+
+ bool m_drawTextFloat;
+ QString m_textFloatText;
+ QPoint m_textFloatPos;
+
+ QPixmap m_segmentsDrawBuffer;
+ QPixmap m_artifactsDrawBuffer;
+ QRect m_segmentsDrawBufferRefresh;
+ QRect m_artifactsDrawBufferRefresh;
+ int m_lastBufferRefreshX;
+ int m_lastBufferRefreshY;
+ int m_lastPointerRefreshX;
+ QPixmap m_backgroundPixmap;
+
+ QString m_toolContextHelp;
+ bool m_contextHelpShown;
+
+ mutable CompositionModel::AudioPreviewDrawData m_audioPreviewRects;
+ mutable CompositionModel::RectRanges m_notationPreviewRects;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
new file mode 100644
index 0000000..fa09644
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PreviewRect.h"
+
+#include <qcolor.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.h b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
new file mode 100644
index 0000000..59f3113
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
@@ -0,0 +1,62 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PREVIEWRECT_H_
+#define _RG_PREVIEWRECT_H_
+
+#include <qcolor.h>
+#include <qrect.h>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+typedef std::vector<QImage> PixmapArray;
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
new file mode 100644
index 0000000..3d1e26f
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
@@ -0,0 +1,88 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentEraser.h"
+
+#include "misc/Debug.h"
+#include "commands/segment/SegmentEraseCommand.h"
+#include "CompositionView.h"
+#include "CompositionItemImpl.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentEraser::SegmentEraser(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentEraser()\n";
+}
+
+void SegmentEraser::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::pointingHandCursor);
+ setBasicContextHelp();
+}
+
+void SegmentEraser::handleMouseButtonPress(QMouseEvent *e)
+{
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+}
+
+void SegmentEraser::handleMouseButtonRelease(QMouseEvent*)
+{
+ if (m_currentItem) {
+ // no need to test the result, we know it's good (see handleMouseButtonPress)
+ CompositionItemImpl* item = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ addCommandToHistory(new SegmentEraseCommand(item->getSegment()));
+ }
+
+ setCurrentItem(CompositionItem());
+}
+
+int SegmentEraser::handleMouseMove(QMouseEvent*)
+{
+ setBasicContextHelp();
+ return RosegardenCanvasView::NoFollow;
+}
+
+void SegmentEraser::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click on a segment to delete it"));
+}
+
+const QString SegmentEraser::ToolName = "segmenteraser";
+
+}
+#include "SegmentEraser.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.h b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
new file mode 100644
index 0000000..f428c28
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
@@ -0,0 +1,67 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTERASER_H_
+#define _RG_SEGMENTERASER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentEraser : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentEraser(CompositionView*, RosegardenGUIDoc*);
+ void setBasicContextHelp();
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
new file mode 100644
index 0000000..f0c4598
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
@@ -0,0 +1,37 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentItemPreview.h"
+
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include <qpainter.h>
+#include <qrect.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
new file mode 100644
index 0000000..e190a5c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
@@ -0,0 +1,91 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTITEMPREVIEW_H_
+#define _RG_SEGMENTITEMPREVIEW_H_
+
+#include <qrect.h>
+
+
+class QWMatrix;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+//////////////////////////////////////////////////////////////////////
+class SegmentItemPreview
+{
+public:
+ SegmentItemPreview(Segment& parent,
+ RulerScale* scale);
+ virtual ~SegmentItemPreview();
+
+ enum PreviewState {
+ PreviewChanged,
+ PreviewCalculating,
+ PreviewCurrent
+ };
+
+ virtual void drawShape(QPainter&) = 0;
+
+ PreviewState getPreviewState() const { return m_previewState; }
+
+ /**
+ * Sets whether the preview shape shown in the segment needs
+ * to be refreshed
+ */
+ void setPreviewCurrent(bool c)
+ { m_previewState = (c ? PreviewCurrent : PreviewChanged); }
+
+ /**
+ * Clears out the preview entirely so that it will be regenerated
+ * next time
+ */
+ virtual void clearPreview() = 0;
+
+ QRect rect();
+
+protected:
+ virtual void updatePreview(const QWMatrix &matrix) = 0;
+
+ //--------------- Data members ---------------------------------
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ PreviewState m_previewState;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
new file mode 100644
index 0000000..5129202
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
@@ -0,0 +1,73 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentJoiner.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentJoiner::SegmentJoiner(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentJoiner() - not implemented\n";
+}
+
+SegmentJoiner::~SegmentJoiner()
+{}
+
+void
+SegmentJoiner::handleMouseButtonPress(QMouseEvent*)
+{}
+
+void
+SegmentJoiner::handleMouseButtonRelease(QMouseEvent*)
+{}
+
+int
+SegmentJoiner::handleMouseMove(QMouseEvent*)
+{
+ return RosegardenCanvasView::NoFollow;
+}
+
+void
+SegmentJoiner::contentsMouseDoubleClickEvent(QMouseEvent*)
+{}
+
+const QString SegmentJoiner::ToolName = "segmentjoiner";
+
+}
+#include "SegmentJoiner.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
new file mode 100644
index 0000000..2c83a26
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
@@ -0,0 +1,70 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTJOINER_H_
+#define _RG_SEGMENTJOINER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentJoiner : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentJoiner();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentJoiner(CompositionView*, RosegardenGUIDoc*);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
new file mode 100644
index 0000000..a3d2a59
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
@@ -0,0 +1,348 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentMover.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Track.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include "SegmentToolBox.h"
+#include "SegmentSelector.h"
+#include <kcommand.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentMover::SegmentMover(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentMover()\n";
+}
+
+void SegmentMover::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp();
+}
+
+void SegmentMover::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentMover::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentMover::handleMouseButtonPress(QMouseEvent *e)
+{
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+ SegmentSelector* selector = dynamic_cast<SegmentSelector*>
+ (getToolBox()->getTool("segmentselector"));
+
+ // #1027303: Segment move issue
+ // Clear selection if we're clicking on an item that's not in it
+ // and we're not in add mode
+
+ if (selector && item &&
+ !m_canvas->getModel()->isSelected(item) && !selector->isSegmentAdding()) {
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->signalSelection();
+ m_canvas->updateContents();
+ }
+
+ if (item) {
+
+ setCurrentItem(item);
+ m_clickPoint = e->pos();
+ Segment* s = CompositionItemHelper::getSegment(m_currentItem);
+
+ int x = int(m_canvas->grid().getRulerScale()->getXForTime(s->getStartTime()));
+ int y = int(m_canvas->grid().getYBinCoordinate(s->getTrack()));
+
+ m_canvas->setGuidesPos(x, y);
+ m_canvas->setDrawGuides(true);
+
+ if (m_canvas->getModel()->haveSelection()) {
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : haveSelection\n";
+ // startChange on all selected segments
+ m_canvas->getModel()->startChangeSelection(CompositionModel::ChangeMove);
+
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ // set m_currentItem to its "sibling" among selected (now moving) items
+ setCurrentItem(CompositionItemHelper::findSiblingCompositionItem(changingItems, m_currentItem));
+
+ } else {
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : no selection\n";
+ m_canvas->getModel()->startChange(item, CompositionModel::ChangeMove);
+ }
+
+ m_canvas->updateContents();
+
+ m_passedInertiaEdge = false;
+
+ } else {
+
+ // check for addmode - clear the selection if not
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : clear selection\n";
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->signalSelection();
+ m_canvas->updateContents();
+ }
+
+}
+
+void SegmentMover::handleMouseButtonRelease(QMouseEvent *e)
+{
+ Composition &comp = m_doc->getComposition();
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (m_currentItem) {
+
+ if (changeMade()) {
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand
+ (changingItems.size() == 1 ? i18n("Move Segment") : i18n("Move Segments"));
+
+
+ CompositionModel::itemcontainer::iterator it;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+
+ CompositionItem item = *it;
+
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ TrackId origTrackId = segment->getTrack();
+ int trackPos = comp.getTrackPositionById(origTrackId);
+ trackPos += trackDiff;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ Track *newTrack = comp.getTrackByPosition(trackPos);
+ int newTrackId = origTrackId;
+ if (newTrack) newTrackId = newTrack->getId();
+
+ timeT newStartTime = CompositionItemHelper::getStartTime(item, m_canvas->grid());
+
+ // We absolutely don't want to snap the end time
+ // to the grid. We want it to remain exactly the same
+ // as it was, but relative to the new start time.
+ timeT newEndTime = newStartTime + segment->getEndMarkerTime()
+ - segment->getStartTime();
+
+ command->addSegment(segment,
+ newStartTime,
+ newEndTime,
+ newTrackId);
+ }
+
+ addCommandToHistory(command);
+ }
+
+ m_canvas->hideTextFloat();
+ m_canvas->setDrawGuides(false);
+ m_canvas->getModel()->endChange();
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+
+ }
+
+ setChangeMade(false);
+ m_currentItem = CompositionItem();
+
+ setBasicContextHelp();
+}
+
+int SegmentMover::handleMouseMove(QMouseEvent *e)
+{
+ m_canvas->setSnapGrain(true);
+
+ Composition &comp = m_doc->getComposition();
+
+ if (!m_currentItem) {
+ setBasicContextHelp();
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove : nb changingItems = "
+ // << changingItems.size() << endl;
+
+ CompositionModel::itemcontainer::iterator it;
+ int guideX = 0;
+ int guideY = 0;
+ QRect updateRect;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+ // it->second->showRepeatRect(false);
+
+ int dx = e->pos().x() - m_clickPoint.x(),
+ dy = e->pos().y() - m_clickPoint.y();
+
+ const int inertiaDistance = m_canvas->grid().getYSnap() / 3;
+ if (!m_passedInertiaEdge &&
+ (dx < inertiaDistance && dx > -inertiaDistance) &&
+ (dy < inertiaDistance && dy > -inertiaDistance)) {
+ return RosegardenCanvasView::NoFollow;
+ } else {
+ m_passedInertiaEdge = true;
+ }
+
+ timeT newStartTime = m_canvas->grid().snapX((*it)->savedRect().x() + dx);
+
+ int newX = int(m_canvas->grid().getRulerScale()->getXForTime(newStartTime));
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+ int trackPos = m_canvas->grid().getYBin((*it)->savedRect().y());
+
+// std::cerr << "segment " << *it << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", trackPos from " << trackPos << " to ";
+
+ trackPos += trackDiff;
+
+// std::cerr << trackPos << std::endl;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+/*!!!
+ int newY = m_canvas->grid().snapY((*it)->savedRect().y() + dy);
+ // Make sure we don't set a non-existing track
+ if (newY < 0) {
+ newY = 0;
+ }
+ int trackPos = m_canvas->grid().getYBin(newY);
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove: orig y "
+ // << (*it)->savedRect().y()
+ // << ", dy " << dy << ", newY " << newY
+ // << ", track " << track << endl;
+
+ // Make sure we don't set a non-existing track (c'td)
+ // TODO: make this suck less. Either the tool should
+ // not allow it in the first place, or we automatically
+ // create new tracks - might make undo very tricky though
+ //
+ if (trackPos >= comp.getNbTracks())
+ trackPos = comp.getNbTracks() - 1;
+*/
+ int newY = m_canvas->grid().getYBinCoordinate(trackPos);
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove: moving to "
+ // << newX << "," << newY << endl;
+
+ updateRect |= (*it)->rect();
+ (*it)->moveTo(newX, newY);
+ updateRect |= (*it)->rect();
+ setChangeMade(true);
+ }
+
+ if (changeMade())
+ m_canvas->getModel()->signalContentChange();
+
+ guideX = m_currentItem->rect().x();
+ guideY = m_currentItem->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ timeT currentItemStartTime = m_canvas->grid().snapX(m_currentItem->rect().x());
+
+ RealTime time = comp.getElapsedRealTime(currentItemStartTime);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%1.%2s (%3, %4, %5)")
+ .arg(time.sec).arg(ms)
+ .arg(bar + 1).arg(beat).arg(fraction);
+
+ m_canvas->setTextFloat(guideX + 10, guideY - 30, posString);
+ m_canvas->updateContents();
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void SegmentMover::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click and drag to move a segment"));
+}
+
+const QString SegmentMover::ToolName = "segmentmover";
+
+}
+#include "SegmentMover.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.h b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
new file mode 100644
index 0000000..776189e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTMOVER_H_
+#define _RG_SEGMENTMOVER_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentMover : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentMover(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ //--------------- Data members ---------------------------------
+
+ QPoint m_clickPoint;
+ bool m_passedInertiaEdge;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
new file mode 100644
index 0000000..4262eb9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentOrderer.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+void SegmentOrderer::segmentClicked(const Segment* s)
+{
+ m_segmentZs[s] = ++m_currentMaxZ;
+ RG_DEBUG << "SegmentOrderer::segmentClicked() s = " << s << " - max Z = " << m_currentMaxZ << endl;
+}
+
+unsigned int SegmentOrderer::getZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentZs[s];
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
new file mode 100644
index 0000000..f4b3d9a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
@@ -0,0 +1,59 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTORDERER_H_
+#define _RG_SEGMENTORDERER_H_
+
+#include "base/Composition.h"
+#include <map>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class SegmentOrderer : public CompositionObserver {
+public:
+ SegmentOrderer() : m_currentMaxZ(0) {};
+
+ unsigned int getZForSegment(const Segment*);
+
+ void segmentClicked(const Segment *);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ std::map<const Segment*, unsigned int> m_segmentZs;
+ unsigned int m_currentMaxZ;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
new file mode 100644
index 0000000..68ca020
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
@@ -0,0 +1,295 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentPencil.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentInsertCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <klocale.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentPencil::SegmentPencil(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_newRect(false),
+ m_track(0),
+ m_startTime(0),
+ m_endTime(0)
+{
+ RG_DEBUG << "SegmentPencil()\n";
+}
+
+void SegmentPencil::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::ibeamCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelpFor(QPoint(0, 0));
+}
+
+void SegmentPencil::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentPencil::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentPencil::handleMouseButtonPress(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ return;
+
+ // is user holding Ctrl+Alt? (ugly, but we are running short on available
+ // modifiers; Alt is grabbed by the window manager, and right clicking, my
+ // (dmm) original idea, is grabbed by the context menu, so let's see how
+ // this goes over
+ bool pencilAnyway = (m_canvas->pencilOverExisting());
+
+ m_newRect = false;
+
+ // Check if mouse click was on a rect
+ //
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If user clicked a rect, and pencilAnyway is false, then there's nothing
+ // left to do here
+ if (item) {
+ delete item;
+ if (!pencilAnyway) return ;
+ }
+
+ // make new item
+ //
+ m_canvas->setSnapGrain(false);
+
+ int trackPosition = m_canvas->grid().getYBin(e->pos().y());
+
+ // Don't do anything if the user clicked beyond the track buttons
+ //
+ if (trackPosition >= m_doc->getComposition().getNbTracks())
+ return ;
+
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (!t)
+ return ;
+
+ TrackId trackId = t->getId();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), SnapGrid::SnapLeft)));
+ timeT duration = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (duration == 0)
+ duration = Note(Note::Shortest).getDuration();
+
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(trackId);
+ if (multiple < 1) multiple = 1;
+
+ QRect tmpRect;
+ tmpRect.setX(int(nearbyint(m_canvas->grid().getRulerScale()->getXForTime(time))));
+ tmpRect.setY(m_canvas->grid().getYBinCoordinate(trackPosition) + 1);
+ tmpRect.setHeight(m_canvas->grid().getYSnap() * multiple - 2);
+ tmpRect.setWidth(int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(time, duration))));
+
+ m_canvas->setTmpRect(tmpRect,
+ GUIPalette::convertColour
+ (m_doc->getComposition().getSegmentColourMap().
+ getColourByIndex(t->getColor())));
+
+ m_newRect = true;
+ m_origPos = e->pos();
+
+ m_canvas->updateContents(tmpRect);
+}
+
+void SegmentPencil::handleMouseButtonRelease(QMouseEvent* e)
+{
+ if (e->button() == RightButton)
+ return ;
+
+ setContextHelpFor(e->pos());
+
+ if (m_newRect) {
+
+ QRect tmpRect = m_canvas->getTmpRect();
+
+ int trackPosition = m_canvas->grid().getYBin(tmpRect.y());
+ Track *track = m_doc->getComposition().getTrackByPosition(trackPosition);
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x()))),
+ endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ // RG_DEBUG << "SegmentPencil::handleMouseButtonRelease() : new segment with track id "
+ // << track->getId() << endl;
+
+ SegmentInsertCommand *command =
+ new SegmentInsertCommand(m_doc, track->getId(),
+ startTime, endTime);
+
+ m_newRect = false;
+
+ addCommandToHistory(command);
+
+ // add the SegmentItem by hand, instead of allowing the usual
+ // update mechanism to spot it. This way we can select the
+ // segment as we add it; otherwise we'd have no way to know
+ // that the segment was created by this tool rather than by
+ // e.g. a simple file load
+
+ Segment *segment = command->getSegment();
+
+ // add a clef to the start of the segment (tracks initialize to a
+ // default of 0 for this property, so treble will be the default if it
+ // is not specified elsewhere)
+ segment->insert(clefIndexToClef(track->getClef()).getAsEvent
+ (segment->getStartTime()));
+ segment->setTranspose(track->getTranspose());
+ segment->setColourIndex(track->getColor());
+ segment->setLowestPlayable(track->getLowestPlayable());
+ segment->setHighestPlayable(track->getHighestPlayable());
+
+ std::string label = qstrtostr(track->getPresetLabel());
+ if (label != "") {
+ segment->setLabel(qstrtostr(track->getPresetLabel()));
+ }
+
+ CompositionItem item = CompositionItemHelper::makeCompositionItem(segment);
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(item);
+ m_canvas->getModel()->signalSelection();
+ m_canvas->setTmpRect(QRect());
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+
+ } else {
+
+ m_newRect = false;
+ }
+}
+
+int SegmentPencil::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_newRect) {
+ setContextHelpFor(e->pos());
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to bar lines"));
+ } else {
+ clearContextHelp();
+ }
+
+ QRect tmpRect = m_canvas->getTmpRect();
+ QRect oldTmpRect = tmpRect;
+
+ m_canvas->setSnapGrain(false);
+
+ SnapGrid::SnapDirection direction = SnapGrid::SnapRight;
+ if (e->pos().x() <= m_origPos.x())
+ direction = SnapGrid::SnapLeft;
+
+ timeT snap = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (snap == 0)
+ snap = Note(Note::Shortest).getDuration();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), direction)));
+
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x())));
+ timeT endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ if (direction == SnapGrid::SnapRight) {
+
+ if (time >= startTime) {
+ if ((time - startTime) < snap) {
+ time = startTime + snap;
+ }
+ } else {
+ if ((startTime - time) < snap) {
+ time = startTime - snap;
+ }
+ }
+
+ int w = int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(startTime, time - startTime)));
+ tmpRect.setWidth(w);
+
+ } else { // SnapGrid::SnapLeft
+
+ // time += std::max(endTime - startTime, timeT(0));
+ tmpRect.setX(int(m_canvas->grid().getRulerScale()->getXForTime(time)));
+
+ }
+
+ m_canvas->setTmpRect(tmpRect);
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+void SegmentPencil::setContextHelpFor(QPoint p)
+{
+ int trackPosition = m_canvas->grid().getYBin(p.y());
+
+ if (trackPosition < m_doc->getComposition().getNbTracks()) {
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (t) {
+ InstrumentId id = t->getInstrument();
+ if (id >= AudioInstrumentBase && id < MidiInstrumentBase) {
+ setContextHelp(i18n("Record or drop audio here"));
+ return;
+ }
+ }
+ }
+
+ setContextHelp(i18n("Click and drag to draw an empty segment. Control+Alt click and drag to draw in overlap mode."));
+}
+
+const QString SegmentPencil::ToolName = "segmentpencil";
+
+}
+#include "SegmentPencil.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.h b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
new file mode 100644
index 0000000..8b55917
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTPENCIL_H_
+#define _RG_SEGMENTPENCIL_H_
+
+#include "base/Track.h"
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////
+
+class SegmentPencil : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentPencil(CompositionView*, RosegardenGUIDoc*);
+ void setContextHelpFor(QPoint p);
+
+ //--------------- Data members ---------------------------------
+
+ bool m_newRect;
+ TrackId m_track;
+ timeT m_startTime;
+ timeT m_endTime;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
new file mode 100644
index 0000000..6ae7433
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
@@ -0,0 +1,393 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentResizer.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/Track.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/AudioSegmentResizeFromStartCommand.h"
+#include "commands/segment/AudioSegmentRescaleCommand.h"
+#include "commands/segment/SegmentRescaleCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "commands/segment/SegmentResizeFromStartCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <kmessagebox.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentResizer::SegmentResizer(CompositionView *c, RosegardenGUIDoc *d,
+ int edgeThreshold)
+ : SegmentTool(c, d),
+ m_edgeThreshold(edgeThreshold)
+{
+ RG_DEBUG << "SegmentResizer()\n";
+}
+
+void SegmentResizer::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::sizeHorCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp(false);
+}
+
+void SegmentResizer::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentResizer::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentResizer::handleMouseButtonPress(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentResizer::handleMouseButtonPress" << endl;
+ m_canvas->getModel()->clearSelected();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ RG_DEBUG << "SegmentResizer::handleMouseButtonPress - got item" << endl;
+ setCurrentItem(item);
+
+ // Are we resizing from start or end?
+ if (item->rect().x() + item->rect().width() / 2 > e->pos().x()) {
+ m_resizeStart = true;
+ } else {
+ m_resizeStart = false;
+ }
+
+ m_canvas->getModel()->startChange(item, m_resizeStart ? CompositionModel::ChangeResizeFromStart : CompositionModel::ChangeResizeFromEnd);
+
+ }
+}
+
+void SegmentResizer::handleMouseButtonRelease(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentResizer::handleMouseButtonRelease" << endl;
+
+ bool rescale = (e->state() & Qt::ControlButton);
+
+ if (m_currentItem) {
+
+ Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
+
+ // We only want to snap the end that we were actually resizing.
+
+ timeT oldStartTime, oldEndTime;
+
+ oldStartTime = segment->getStartTime();
+ oldEndTime = segment->getEndMarkerTime();
+
+ timeT newStartTime, newEndTime;
+
+ if (m_resizeStart) {
+ newStartTime = CompositionItemHelper::getStartTime
+ (m_currentItem, m_canvas->grid());
+ newEndTime = oldEndTime;
+ } else {
+ newEndTime = CompositionItemHelper::getEndTime
+ (m_currentItem, m_canvas->grid());
+ newStartTime = oldStartTime;
+ }
+
+ if (changeMade()) {
+
+ if (newStartTime > newEndTime) std::swap(newStartTime, newEndTime);
+
+ if (rescale) {
+
+ if (segment->getType() == Segment::Audio) {
+
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException) {
+ if (KMessageBox::warningContinueCancel
+ (0,
+ i18n("The audio file path does not exist or is not writable.\nYou must set the audio file path to a valid directory in Document Properties before rescaling an audio file.\nWould you like to set it now?"),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ RosegardenGUIApp::self()->slotOpenAudioPathSettings();
+ }
+ }
+
+ float ratio = float(newEndTime - newStartTime) /
+ float(oldEndTime - oldStartTime);
+
+ AudioSegmentRescaleCommand *command =
+ new AudioSegmentRescaleCommand(m_doc, segment, ratio,
+ newStartTime, newEndTime);
+
+ ProgressDialog progressDlg
+ (i18n("Rescaling audio file..."), 100, 0);
+ progressDlg.setAutoClose(false);
+ progressDlg.setAutoReset(false);
+ progressDlg.show();
+ command->connectProgressDialog(&progressDlg);
+
+ addCommandToHistory(command);
+
+ progressDlg.setLabel(i18n("Generating audio preview..."));
+ command->disconnectProgressDialog(&progressDlg);
+ connect(&m_doc->getAudioFileManager(), SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &m_doc->getAudioFileManager(), SLOT(slotStopPreview()));
+
+ int fid = command->getNewAudioFileId();
+ if (fid >= 0) {
+ RosegardenGUIApp::self()->slotAddAudioFile(fid);
+ m_doc->getAudioFileManager().generatePreview(fid);
+ }
+
+ } else {
+
+ SegmentRescaleCommand *command =
+ new SegmentRescaleCommand(segment,
+ newEndTime - newStartTime,
+ oldEndTime - oldStartTime,
+ newStartTime);
+ addCommandToHistory(command);
+ }
+ } else {
+
+ if (m_resizeStart) {
+
+ if (segment->getType() == Segment::Audio) {
+ addCommandToHistory(new AudioSegmentResizeFromStartCommand
+ (segment, newStartTime));
+ } else {
+ addCommandToHistory(new SegmentResizeFromStartCommand
+ (segment, newStartTime));
+ }
+
+ } else {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand("Resize Segment");
+
+ int trackPos = CompositionItemHelper::getTrackPos
+ (m_currentItem, m_canvas->grid());
+
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackByPosition(trackPos);
+
+ command->addSegment(segment,
+ newStartTime,
+ newEndTime,
+ track->getId());
+ addCommandToHistory(command);
+ }
+ }
+ }
+ }
+
+ m_canvas->getModel()->endChange();
+ m_canvas->updateContents();
+ setChangeMade(false);
+ m_currentItem = CompositionItem();
+ setBasicContextHelp();
+}
+
+int SegmentResizer::handleMouseMove(QMouseEvent *e)
+{
+ // RG_DEBUG << "SegmentResizer::handleMouseMove" << endl;
+
+ bool rescale = (e->state() & Qt::ControlButton);
+
+ if (!m_currentItem) {
+ setBasicContextHelp(rescale);
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (rescale) {
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+ } else {
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid; hold Ctrl as well to rescale contents"));
+ } else {
+ setContextHelp("Hold Ctrl to rescale contents");
+ }
+ }
+
+ Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
+
+ // Don't allow Audio segments to resize yet
+ //
+ /*!!!
+ if (segment->getType() == Segment::Audio)
+ {
+ m_currentItem = CompositionItem();
+ KMessageBox::information(m_canvas,
+ i18n("You can't yet resize an audio segment!"));
+ return RosegardenCanvasView::NoFollow;
+ }
+ */
+
+ QRect oldRect = m_currentItem->rect();
+
+ m_canvas->setSnapGrain(true);
+
+ timeT time = m_canvas->grid().snapX(e->pos().x());
+ timeT snap = m_canvas->grid().getSnapTime(double(e->pos().x()));
+ if (snap == 0)
+ snap = Note(Note::Shortest).getDuration();
+
+ // We only want to snap the end that we were actually resizing.
+
+ timeT itemStartTime, itemEndTime;
+
+ if (m_resizeStart) {
+ itemStartTime = CompositionItemHelper::getStartTime
+ (m_currentItem, m_canvas->grid());
+ itemEndTime = segment->getEndMarkerTime();
+ } else {
+ itemEndTime = CompositionItemHelper::getEndTime
+ (m_currentItem, m_canvas->grid());
+ itemStartTime = segment->getStartTime();
+ }
+
+ timeT duration = 0;
+
+ if (m_resizeStart) {
+
+ duration = itemEndTime - time;
+ // RG_DEBUG << "SegmentResizer::handleMouseMove() resize start : duration = "
+ // << duration << " - snap = " << snap
+ // << " - itemEndTime : " << itemEndTime
+ // << " - time : " << time
+ // << endl;
+
+ timeT newStartTime = 0;
+
+ if ((duration > 0 && duration < snap) ||
+ (duration < 0 && duration > -snap)) {
+
+ newStartTime = itemEndTime - (duration < 0 ? -snap : snap);
+
+ } else {
+
+ newStartTime = itemEndTime - duration;
+
+ }
+
+ CompositionItemHelper::setStartTime(m_currentItem,
+ newStartTime,
+ m_canvas->grid());
+ } else { // resize end
+
+ duration = time - itemStartTime;
+
+ timeT newEndTime = 0;
+
+ // RG_DEBUG << "SegmentResizer::handleMouseMove() resize end : duration = "
+ // << duration << " - snap = " << snap
+ // << " - itemEndTime : " << itemEndTime
+ // << " - time : " << time
+ // << endl;
+
+ if ((duration > 0 && duration < snap) ||
+ (duration < 0 && duration > -snap)) {
+
+ newEndTime = (duration < 0 ? -snap : snap) + itemStartTime;
+
+ } else {
+
+ newEndTime = duration + itemStartTime;
+
+ }
+
+ CompositionItemHelper::setEndTime(m_currentItem,
+ newEndTime,
+ m_canvas->grid());
+ }
+
+ if (duration != 0)
+ setChangeMade(true);
+
+ m_canvas->slotUpdateSegmentsDrawBuffer(m_currentItem->rect() | oldRect);
+
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+bool SegmentResizer::cursorIsCloseEnoughToEdge(const CompositionItem& p, const QPoint &coord,
+ int edgeThreshold, bool &start)
+{
+ if (abs(p->rect().x() + p->rect().width() - coord.x()) < edgeThreshold) {
+ start = false;
+ return true;
+ } else if (abs(p->rect().x() - coord.x()) < edgeThreshold) {
+ start = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void SegmentResizer::setBasicContextHelp(bool ctrlPressed)
+{
+ if (ctrlPressed) {
+ setContextHelp(i18n("Click and drag to resize a segment; hold Ctrl as well to rescale its contents"));
+ } else {
+ setContextHelp(i18n("Click and drag to rescale segment"));
+ }
+}
+
+const QString SegmentResizer::ToolName = "segmentresizer";
+
+}
+#include "SegmentResizer.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.h b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
new file mode 100644
index 0000000..9d54573
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
@@ -0,0 +1,87 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTRESIZER_H_
+#define _RG_SEGMENTRESIZER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QPoint;
+class QMouseEvent;
+class CompositionItem;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+/**
+ * Segment Resizer tool. Allows resizing only at the end of the segment part
+ */
+class SegmentResizer : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static bool cursorIsCloseEnoughToEdge(const CompositionItem&, const QPoint&, int, bool &);
+
+ void setEdgeThreshold(int e) { m_edgeThreshold = e; }
+ int getEdgeThreshold() { return m_edgeThreshold; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentResizer(CompositionView*, RosegardenGUIDoc*, int edgeThreshold = 10);
+ void setBasicContextHelp(bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ int m_edgeThreshold;
+ bool m_resizeStart;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
new file mode 100644
index 0000000..35ec639
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
@@ -0,0 +1,532 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentSelector.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/SnapGrid.h"
+#include "base/Selection.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentQuickCopyCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentTool.h"
+#include "SegmentToolBox.h"
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSelector::SegmentSelector(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_segmentAddMode(false),
+ m_segmentCopyMode(false),
+ m_segmentQuickCopyDone(false),
+ m_buttonPressed(false),
+ m_selectionMoveStarted(false),
+ m_dispatchTool(0)
+{
+ RG_DEBUG << "SegmentSelector()\n";
+}
+
+SegmentSelector::~SegmentSelector()
+{}
+
+void SegmentSelector::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelp(i18n("Click and drag to select segments"));
+}
+
+void SegmentSelector::stow()
+{}
+
+void SegmentSelector::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void
+SegmentSelector::handleMouseButtonPress(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentSelector::handleMouseButtonPress\n";
+ m_buttonPressed = true;
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If we're in segmentAddMode or not clicking on an item then we don't
+ // clear the selection vector. If we're clicking on an item and it's
+ // not in the selection - then also clear the selection.
+ //
+ if ((!m_segmentAddMode && !item) ||
+ (!m_segmentAddMode && !(m_canvas->getModel()->isSelected(item)))) {
+ m_canvas->getModel()->clearSelected();
+ }
+
+ if (item) {
+
+ // Fifteen percent of the width of the SegmentItem, up to 10px
+ //
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+
+ bool start = false;
+
+ // Resize if we're dragging from the edge, provided we aren't
+ // in segment-add mode with at least one segment already
+ // selected -- as we aren't able to resize multiple segments
+ // at once, we should assume the segment-add aspect takes
+ // priority
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, e->pos(), threshold, start)) {
+
+ SegmentResizer* resizer =
+ dynamic_cast<SegmentResizer*>(getToolBox()->getTool(SegmentResizer::ToolName));
+
+ resizer->setEdgeThreshold(threshold);
+
+ // For the moment we only allow resizing of a single segment
+ // at a time.
+ //
+ m_canvas->getModel()->clearSelected();
+
+ m_dispatchTool = resizer;
+
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ return ;
+ }
+
+ bool selecting = true;
+
+ if (m_segmentAddMode && m_canvas->getModel()->isSelected(item)) {
+ selecting = false;
+ } else {
+ // put the segment in 'move' mode only if it's being selected
+ m_canvas->getModel()->startChange(item, CompositionModel::ChangeMove);
+ }
+
+ m_canvas->getModel()->setSelected(item, selecting);
+
+ // Moving
+ //
+ // RG_DEBUG << "SegmentSelector::handleMouseButtonPress - m_currentItem = " << item << endl;
+ m_currentItem = item;
+ m_clickPoint = e->pos();
+
+ int guideX = item->rect().x();
+ int guideY = item->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ m_canvas->setDrawGuides(true);
+
+ } else {
+
+ // Add on middle button or ctrl+left - bounding box on rest
+ //
+ if (e->button() == MidButton ||
+ (e->button() == LeftButton && (e->state() & Qt::ControlButton))) {
+
+ m_dispatchTool = getToolBox()->getTool(SegmentPencil::ToolName);
+
+ if (m_dispatchTool) {
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ }
+
+ return ;
+
+ } else {
+
+ m_canvas->setSelectionRectPos(e->pos());
+ m_canvas->setDrawSelectionRect(true);
+ if (!m_segmentAddMode)
+ m_canvas->getModel()->clearSelected();
+
+ }
+ }
+
+ // Tell the RosegardenGUIView that we've selected some new Segments -
+ // when the list is empty we're just unselecting.
+ //
+ m_canvas->getModel()->signalSelection();
+
+ m_passedInertiaEdge = false;
+}
+
+void
+SegmentSelector::handleMouseButtonRelease(QMouseEvent *e)
+{
+ m_buttonPressed = false;
+
+ // Hide guides and stuff
+ //
+ m_canvas->setDrawGuides(false);
+ m_canvas->hideTextFloat();
+
+ if (m_dispatchTool) {
+ m_dispatchTool->handleMouseButtonRelease(e);
+ m_dispatchTool = 0;
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (!m_currentItem) {
+ m_canvas->setDrawSelectionRect(false);
+ m_canvas->getModel()->finalizeSelectionRect();
+ m_canvas->getModel()->signalSelection();
+ return ;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+
+ Composition &comp = m_doc->getComposition();
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ CompositionModel::itemcontainer::iterator it;
+
+ if (changeMade()) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand
+ (m_selectedItems.size() == 1 ? i18n("Move Segment") :
+ i18n("Move Segments"));
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+
+ CompositionItem item = *it;
+
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ TrackId origTrackId = segment->getTrack();
+ int trackPos = comp.getTrackPositionById(origTrackId);
+ trackPos += trackDiff;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ Track *newTrack = comp.getTrackByPosition(trackPos);
+ int newTrackId = origTrackId;
+ if (newTrack) newTrackId = newTrack->getId();
+
+ timeT itemStartTime = CompositionItemHelper::getStartTime
+ (item, m_canvas->grid());
+
+ // We absolutely don't want to snap the end time to
+ // the grid. We want it to remain exactly the same as
+ // it was, but relative to the new start time.
+ timeT itemEndTime = itemStartTime + segment->getEndMarkerTime()
+ - segment->getStartTime();
+
+// std::cerr << "releasing segment " << segment << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", diff is " << trackDiff << ", moving from track pos " << comp.getTrackPositionById(origTrackId) << " to " << trackPos << ", id " << origTrackId << " to " << newTrackId << std::endl;
+
+ command->addSegment(segment,
+ itemStartTime,
+ itemEndTime,
+ newTrackId);
+ }
+
+ addCommandToHistory(command);
+ }
+
+ m_canvas->getModel()->endChange();
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+ }
+
+ // if we've just finished a quick copy then drop the Z level back
+ if (m_segmentQuickCopyDone) {
+ m_segmentQuickCopyDone = false;
+ // m_currentItem->setZ(2); // see SegmentItem::setSelected --??
+ }
+
+ setChangeMade(false);
+
+ m_selectionMoveStarted = false;
+
+ m_currentItem = CompositionItem();
+
+ setContextHelpFor(e->pos());
+}
+
+int
+SegmentSelector::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_buttonPressed) {
+ setContextHelpFor(e->pos(), (e->state() & Qt::ControlButton));
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (m_dispatchTool) {
+ return m_dispatchTool->handleMouseMove(e);
+ }
+
+ Composition &comp = m_doc->getComposition();
+
+ if (!m_currentItem) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: no current item\n";
+
+ // do a bounding box
+ QRect selectionRect = m_canvas->getSelectionRect();
+
+ m_canvas->setDrawSelectionRect(true);
+
+ // same as for notation view
+ int w = int(e->pos().x() - selectionRect.x());
+ int h = int(e->pos().y() - selectionRect.y());
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ // Translate these points
+ //
+ m_canvas->setSelectionRectSize(w, h);
+
+ m_canvas->getModel()->signalSelection();
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+
+ if (m_segmentCopyMode && !m_segmentQuickCopyDone) {
+ KMacroCommand *mcommand = new KMacroCommand
+ (SegmentQuickCopyCommand::getGlobalName());
+
+ SegmentSelection selectedItems = m_canvas->getSelectedSegments();
+ SegmentSelection::iterator it;
+ for (it = selectedItems.begin();
+ it != selectedItems.end();
+ it++) {
+ SegmentQuickCopyCommand *command =
+ new SegmentQuickCopyCommand(*it);
+
+ mcommand->addCommand(command);
+ }
+
+ addCommandToHistory(mcommand);
+
+ // generate SegmentItem
+ //
+ m_canvas->updateContents();
+ m_segmentQuickCopyDone = true;
+ }
+
+ m_canvas->setSnapGrain(true);
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item is selected\n";
+
+ if (!m_selectionMoveStarted) { // start move on selected items only once
+ m_canvas->getModel()->startChangeSelection(CompositionModel::ChangeMove);
+ m_selectionMoveStarted = true;
+ }
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ setCurrentItem(CompositionItemHelper::findSiblingCompositionItem(changingItems, m_currentItem));
+
+ CompositionModel::itemcontainer::iterator it;
+ int guideX = 0;
+ int guideY = 0;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ ++it) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove() : movingItem at "
+ // << (*it)->rect().x() << "," << (*it)->rect().y() << endl;
+
+ int dx = e->pos().x() - m_clickPoint.x(),
+ dy = e->pos().y() - m_clickPoint.y();
+
+ const int inertiaDistance = m_canvas->grid().getYSnap() / 3;
+ if (!m_passedInertiaEdge &&
+ (dx < inertiaDistance && dx > -inertiaDistance) &&
+ (dy < inertiaDistance && dy > -inertiaDistance)) {
+ return RosegardenCanvasView::NoFollow;
+ } else {
+ m_passedInertiaEdge = true;
+ }
+
+ timeT newStartTime = m_canvas->grid().snapX((*it)->savedRect().x() + dx);
+
+ int newX = int(m_canvas->grid().getRulerScale()->getXForTime(newStartTime));
+
+ int trackPos = m_canvas->grid().getYBin((*it)->savedRect().y());
+
+// std::cerr << "segment " << *it << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", trackPos from " << trackPos << " to ";
+
+ trackPos += trackDiff;
+
+// std::cerr << trackPos << std::endl;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ int newY = m_canvas->grid().getYBinCoordinate(trackPos);
+
+ (*it)->moveTo(newX, newY);
+ setChangeMade(true);
+ }
+
+ if (changeMade())
+ m_canvas->getModel()->signalContentChange();
+
+ guideX = m_currentItem->rect().x();
+ guideY = m_currentItem->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ timeT currentItemStartTime = m_canvas->grid().snapX(m_currentItem->rect().x());
+
+ RealTime time = comp.getElapsedRealTime(currentItemStartTime);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%1.%2s (%3, %4, %5)")
+ .arg(time.sec).arg(ms)
+ .arg(bar + 1).arg(beat).arg(fraction);
+
+ m_canvas->setTextFloat(guideX + 10, guideY - 30, posString);
+ m_canvas->updateContents();
+
+ } else {
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item not selected\n";
+ }
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void SegmentSelector::setContextHelpFor(QPoint p, bool ctrlPressed)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ CompositionItem item = m_canvas->getFirstItemAt(p);
+
+ if (!item) {
+ setContextHelp(i18n("Click and drag to select segments; middle-click and drag to draw an empty segment"));
+
+ } else {
+
+ // Same logic as in handleMouseButtonPress to establish
+ // whether we'd be moving or resizing
+
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+ bool start = false;
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, p,
+ threshold, start)) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to resize a segment; hold Ctrl as well to rescale its contents"));
+ } else {
+ setContextHelp(i18n("Click and drag to rescale segment"));
+ }
+ } else {
+ if (m_canvas->getModel()->haveMultipleSelection()) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segments; hold Ctrl as well to copy them"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segments"));
+ }
+ } else {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segment; hold Ctrl as well to copy it; double-click to edit"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segment"));
+ }
+ }
+ }
+ }
+}
+
+const QString SegmentSelector::ToolName = "segmentselector";
+
+}
+#include "SegmentSelector.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.h b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
new file mode 100644
index 0000000..a6d8d9c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
@@ -0,0 +1,109 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTSELECTOR_H_
+#define _RG_SEGMENTSELECTOR_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSelector : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentTool;
+
+public:
+
+ virtual ~SegmentSelector();
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // These two alter the behaviour of the selection mode
+ //
+ // - SegmentAdd (usually when SHIFT is held down) allows
+ // multiple selections of Segments.
+ //
+ // - SegmentCopy (usually CONTROL) allows draw and drop
+ // copying of Segments - it's a quick shortcut
+ //
+ void setSegmentAdd(const bool &value) { m_segmentAddMode = value; }
+ void setSegmentCopy(const bool &value) { m_segmentCopyMode = value; }
+
+ bool isSegmentAdding() const { return m_segmentAddMode; }
+ bool isSegmentCopying() const { return m_segmentCopyMode; }
+
+ // Return the SegmentItem list for other tools to use
+ //
+ SegmentItemList* getSegmentItemList() { return &m_selectedItems; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentSelector(CompositionView*, RosegardenGUIDoc*);
+
+ void setContextHelpFor(QPoint p, bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ SegmentItemList m_selectedItems;
+
+ bool m_segmentAddMode;
+ bool m_segmentCopyMode;
+ QPoint m_clickPoint;
+ bool m_segmentQuickCopyDone;
+ bool m_passedInertiaEdge;
+ bool m_buttonPressed;
+ bool m_selectionMoveStarted;
+
+ SegmentTool *m_dispatchTool;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
new file mode 100644
index 0000000..4fd48c3
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
@@ -0,0 +1,175 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentSplitter.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/AudioSegmentSplitCommand.h"
+#include "commands/segment/SegmentSplitCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSplitter::SegmentSplitter(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_prevX(0),
+ m_prevY(0)
+{
+ RG_DEBUG << "SegmentSplitter()\n";
+}
+
+SegmentSplitter::~SegmentSplitter()
+{}
+
+void SegmentSplitter::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ setBasicContextHelp();
+}
+
+void
+SegmentSplitter::handleMouseButtonPress(QMouseEvent *e)
+{
+ // Remove cursor and replace with line on a SegmentItem
+ // at where the cut will be made
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->viewport()->setCursor(Qt::blankCursor);
+ m_prevX = item->rect().x();
+ m_prevX = item->rect().y();
+ drawSplitLine(e);
+ delete item;
+ }
+
+}
+
+void
+SegmentSplitter::handleMouseButtonRelease(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->setSnapGrain(true);
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ if (segment->getType() == Segment::Audio) {
+ AudioSegmentSplitCommand *command =
+ new AudioSegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ } else {
+ SegmentSplitCommand *command =
+ new SegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ }
+
+ m_canvas->updateContents(item->rect());
+ delete item;
+ }
+
+ // Reinstate the cursor
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+}
+
+int
+SegmentSplitter::handleMouseMove(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+// m_canvas->viewport()->setCursor(Qt::blankCursor);
+ drawSplitLine(e);
+ delete item;
+ return RosegardenCanvasView::FollowHorizontal;
+ } else {
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+ return RosegardenCanvasView::NoFollow;
+ }
+}
+
+void
+SegmentSplitter::drawSplitLine(QMouseEvent *e)
+{
+ m_canvas->setSnapGrain(true);
+
+ // Turn the real X into a snapped X
+ //
+ timeT xT = m_canvas->grid().snapX(e->pos().x());
+ int x = (int)(m_canvas->grid().getRulerScale()->getXForTime(xT));
+
+ // Need to watch y doesn't leak over the edges of the
+ // current Segment.
+ //
+ int y = m_canvas->grid().snapY(e->pos().y());
+
+ m_canvas->slotShowSplitLine(x, y);
+
+ QRect updateRect(std::max(0, std::min(x, m_prevX) - 5), y,
+ std::max(m_prevX, x) + 5, m_prevY + m_canvas->grid().getYSnap());
+ m_canvas->updateContents(updateRect);
+ m_prevX = x;
+ m_prevY = y;
+}
+
+void
+SegmentSplitter::contentsMouseDoubleClickEvent(QMouseEvent*)
+{
+ // DO NOTHING
+}
+
+void
+SegmentSplitter::setBasicContextHelp()
+{
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Click on a segment to split it in two; hold Shift to avoid snapping to beat grid"));
+ } else {
+ setContextHelp(i18n("Click on a segment to split it in two"));
+ }
+}
+
+const QString SegmentSplitter::ToolName = "segmentsplitter";
+
+}
+#include "SegmentSplitter.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
new file mode 100644
index 0000000..06201ec
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTSPLITTER_H_
+#define _RG_SEGMENTSPLITTER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSplitter : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentSplitter();
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentSplitter(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ void drawSplitLine(QMouseEvent*);
+ void splitSegment(Segment *segment,
+ timeT &splitTime);
+
+ //--------------- Data members ---------------------------------
+ int m_prevX;
+ int m_prevY;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
new file mode 100644
index 0000000..9bd7e69
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
@@ -0,0 +1,115 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentTool.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/BaseTool.h"
+#include "SegmentToolBox.h"
+#include <kcommand.h>
+#include <kmainwindow.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+
+
+namespace Rosegarden
+{
+
+SegmentTool::SegmentTool(CompositionView* canvas, RosegardenGUIDoc *doc)
+ : BaseTool("segment_tool_menu", dynamic_cast<KMainWindow*>(doc->parent())->factory(), canvas),
+ m_canvas(canvas),
+ m_doc(doc),
+ m_changeMade(false)
+{}
+
+SegmentTool::~SegmentTool()
+{}
+
+
+void SegmentTool::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+}
+
+void
+SegmentTool::handleRightButtonPress(QMouseEvent *e)
+{
+ if (m_currentItem) // mouse button is pressed for some tool
+ return ;
+
+ RG_DEBUG << "SegmentTool::handleRightButtonPress()\n";
+
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+
+ if (m_currentItem) {
+ if (!m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(m_currentItem);
+ m_canvas->getModel()->signalSelection();
+ }
+ }
+
+ showMenu();
+ setCurrentItem(CompositionItem());
+}
+
+void
+SegmentTool::createMenu()
+{
+ RG_DEBUG << "SegmentTool::createMenu()\n";
+
+ RosegardenGUIApp *app =
+ dynamic_cast<RosegardenGUIApp*>(m_doc->parent());
+
+ if (app) {
+ m_menu = static_cast<QPopupMenu*>
+ //(app->factory()->container("segment_tool_menu", app));
+ (m_parentFactory->container("segment_tool_menu", app));
+
+ if (!m_menu) {
+ RG_DEBUG << "SegmentTool::createMenu() failed\n";
+ }
+ } else {
+ RG_DEBUG << "SegmentTool::createMenu() failed: !app\n";
+ }
+}
+
+void
+SegmentTool::addCommandToHistory(KCommand *command)
+{
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+SegmentToolBox* SegmentTool::getToolBox()
+{
+ return m_canvas->getToolBox();
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.h b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
new file mode 100644
index 0000000..90ed961
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
@@ -0,0 +1,105 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTTOOL_H_
+#define _RG_SEGMENTTOOL_H_
+
+#include "gui/general/BaseTool.h"
+#include "CompositionItem.h"
+#include <qpoint.h>
+#include <utility>
+#include <vector>
+
+
+class QMouseEvent;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class SegmentToolBox;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////////////////////////////////////////////
+
+class SegmentToolBox;
+class SegmentSelector;
+
+// Allow the tools to share the Selector tool's selection
+// through these.
+//
+typedef std::pair<QPoint, CompositionItem> SegmentItemPair;
+typedef std::vector<SegmentItemPair> SegmentItemList;
+
+class SegmentTool : public BaseTool
+{
+public:
+ friend class SegmentToolBox;
+
+ virtual ~SegmentTool();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) when
+ * the tool is set as current.
+ * Add any setup here
+ */
+ virtual void ready();
+
+ virtual void handleRightButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonPress(QMouseEvent*) = 0;
+ virtual void handleMouseButtonRelease(QMouseEvent*) = 0;
+ virtual int handleMouseMove(QMouseEvent*) = 0;
+
+ void addCommandToHistory(KCommand *command);
+
+protected:
+ SegmentTool(CompositionView*, RosegardenGUIDoc *doc);
+
+ virtual void createMenu();
+ virtual bool hasMenu() { return true; }
+
+ void setCurrentItem(CompositionItem item) { if (item != m_currentItem) { delete m_currentItem; m_currentItem = item; } }
+
+ SegmentToolBox* getToolBox();
+
+ bool changeMade() { return m_changeMade; }
+ void setChangeMade(bool c) { m_changeMade = c; }
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ CompositionItem m_currentItem;
+ RosegardenGUIDoc* m_doc;
+ QPoint m_origPos;
+ bool m_changeMade;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
new file mode 100644
index 0000000..cb0dec9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentToolBox.h"
+
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+#include "SegmentSelector.h"
+#include "SegmentEraser.h"
+#include "SegmentJoiner.h"
+#include "SegmentMover.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentSplitter.h"
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+SegmentToolBox::SegmentToolBox(CompositionView* parent, RosegardenGUIDoc* doc)
+ : BaseToolBox(parent),
+ m_canvas(parent),
+ m_doc(doc)
+{}
+
+SegmentTool* SegmentToolBox::createTool(const QString& toolName)
+{
+ SegmentTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == SegmentPencil::ToolName)
+
+ tool = new SegmentPencil(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentEraser::ToolName)
+
+ tool = new SegmentEraser(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentMover::ToolName)
+
+ tool = new SegmentMover(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentResizer::ToolName)
+
+ tool = new SegmentResizer(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSelector::ToolName)
+
+ tool = new SegmentSelector(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSplitter::ToolName)
+
+ tool = new SegmentSplitter(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentJoiner::ToolName)
+
+ tool = new SegmentJoiner(m_canvas, m_doc);
+
+ else {
+ KMessageBox::error(0, QString("SegmentToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+}
+
+SegmentTool* SegmentToolBox::getTool(const QString& toolName)
+{
+ return dynamic_cast<SegmentTool*>(BaseToolBox::getTool(toolName));
+}
+
+}
+#include "SegmentToolBox.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
new file mode 100644
index 0000000..7a46fb6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
@@ -0,0 +1,63 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTTOOLBOX_H_
+#define _RG_SEGMENTTOOLBOX_H_
+
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentToolBox : public BaseToolBox
+{
+ Q_OBJECT
+public:
+ SegmentToolBox(CompositionView* parent, RosegardenGUIDoc*);
+
+ virtual SegmentTool* getTool(const QString& toolName);
+
+protected:
+ virtual SegmentTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ RosegardenGUIDoc* m_doc;
+};
+
+
+}
+
+#endif