diff options
Diffstat (limited to 'src/gui/editors/segment')
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 |