You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
12 KiB
405 lines
12 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
|
|
#include "DeviceEditorDialog.h"
|
|
|
|
#include <tdelocale.h>
|
|
#include "misc/Debug.h"
|
|
#include "misc/Strings.h"
|
|
#include "base/Device.h"
|
|
#include "base/MidiDevice.h"
|
|
#include "base/Studio.h"
|
|
#include "commands/studio/CreateOrDeleteDeviceCommand.h"
|
|
#include "commands/studio/ReconnectDeviceCommand.h"
|
|
#include "commands/studio/RenameDeviceCommand.h"
|
|
#include "document/RosegardenGUIDoc.h"
|
|
#include "document/MultiViewCommandHistory.h"
|
|
#include "gui/application/RosegardenApplication.h"
|
|
#include <kdialogbase.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tqcstring.h>
|
|
#include <tqdatastream.h>
|
|
#include <tqhbox.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqregexp.h>
|
|
#include <tqstring.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtable.h>
|
|
#include <tqvbox.h>
|
|
#include <tqwidget.h>
|
|
#include <algorithm>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
DeviceEditorDialog::DeviceEditorDialog(TQWidget *parent,
|
|
RosegardenGUIDoc *document) :
|
|
KDialogBase(parent, "deviceeditordialog", true,
|
|
i18n("Manage MIDI Devices"), Ok | Apply | Close, Ok, true),
|
|
m_document(document),
|
|
m_studio(&document->getStudio()),
|
|
m_modified(false)
|
|
{
|
|
TQVBox *mainBox = makeVBoxMainWidget();
|
|
|
|
m_table = new TQTable(0, 4, mainBox);
|
|
m_table->setSorting(false);
|
|
m_table->setRowMovingEnabled(false);
|
|
m_table->setColumnMovingEnabled(false);
|
|
m_table->setShowGrid(false);
|
|
m_table->horizontalHeader()->setLabel(0, i18n("Device"));
|
|
m_table->horizontalHeader()->setLabel(1, i18n("Name"));
|
|
m_table->horizontalHeader()->setLabel(2, i18n("Type"));
|
|
m_table->horizontalHeader()->setLabel(3, i18n("Connection"));
|
|
m_table->horizontalHeader()->show();
|
|
m_table->verticalHeader()->hide();
|
|
m_table->setLeftMargin(0);
|
|
m_table->setSelectionMode(TQTable::SingleRow);
|
|
m_table->setColumnReadOnly(0, true);
|
|
m_table->setColumnReadOnly(2, true);
|
|
|
|
makeConnectionList((unsigned int)MidiDevice::Play,
|
|
m_playConnections);
|
|
makeConnectionList((unsigned int)MidiDevice::Record,
|
|
m_recordConnections);
|
|
|
|
populate();
|
|
|
|
TQHBox *hbox = new TQHBox(mainBox);
|
|
TQPushButton *addButton = new TQPushButton(i18n("Add Play Device"), hbox);
|
|
TQPushButton *addRButton = new TQPushButton(i18n("Add Record Device"), hbox);
|
|
TQPushButton *deleteButton = new TQPushButton(i18n("Delete Device"), hbox);
|
|
connect(addButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotAddPlayDevice()));
|
|
connect(addRButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotAddRecordDevice()));
|
|
connect(deleteButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotDeleteDevice()));
|
|
connect(m_table, TQ_SIGNAL(valueChanged(int, int)),
|
|
this, TQ_SLOT(slotValueChanged (int, int)));
|
|
|
|
setMinimumHeight(250);
|
|
|
|
enableButtonOK(false);
|
|
enableButtonApply(false);
|
|
}
|
|
|
|
DeviceEditorDialog::~DeviceEditorDialog()
|
|
{
|
|
// nothing -- don't need to clear device list (the devices were
|
|
// just aliases for those in the studio)
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::populate()
|
|
{
|
|
DeviceList *devices = m_studio->getDevices();
|
|
DeviceListIterator it;
|
|
m_devices.clear();
|
|
|
|
for (it = devices->begin(); it != devices->end(); ++it) {
|
|
if ((*it)->getType() == Device::Midi) {
|
|
MidiDevice *md =
|
|
dynamic_cast<MidiDevice *>(*it);
|
|
if (md)
|
|
m_devices.push_back(md);
|
|
}
|
|
}
|
|
|
|
while (m_table->numRows() > 0) {
|
|
m_table->removeRow(m_table->numRows() - 1);
|
|
}
|
|
|
|
int deviceCount = 0;
|
|
|
|
#define NAME_COL 0
|
|
#define LABEL_COL 1
|
|
#define DIRECTION_COL 2
|
|
#define CONNECTION_COL 3
|
|
|
|
for (it = m_devices.begin(); it != m_devices.end(); ++it) {
|
|
|
|
m_table->insertRows(deviceCount, 1);
|
|
|
|
// we know we only put MidiDevices in m_devices
|
|
MidiDevice *md = static_cast<MidiDevice *>(*it);
|
|
|
|
// if you change this string ("Device %1"), change test in slotApply
|
|
TQString deviceName = i18n("Device %1").arg(md->getId() + 1);
|
|
TQString deviceLabel = strtoqstr(md->getName());
|
|
TQString connectionName = strtoqstr(md->getConnection());
|
|
|
|
m_table->setText(deviceCount, NAME_COL, deviceName);
|
|
m_table->setText(deviceCount, LABEL_COL, deviceLabel);
|
|
m_table->setText(deviceCount, DIRECTION_COL,
|
|
(md->getDirection() == MidiDevice::Play ?
|
|
i18n("Play") : i18n("Record")));
|
|
|
|
TQStringList &list(md->getDirection() == MidiDevice::Play ?
|
|
m_playConnections : m_recordConnections);
|
|
int currentConnectionIndex = list.size() - 1;
|
|
for (unsigned int i = 0; i < list.size(); ++i) {
|
|
if (list[i] == connectionName)
|
|
currentConnectionIndex = i;
|
|
}
|
|
|
|
TQComboTableItem *item = new TQComboTableItem(m_table, list, false);
|
|
item->setCurrentItem(currentConnectionIndex);
|
|
m_table->setItem(deviceCount, CONNECTION_COL, item);
|
|
|
|
m_table->adjustRow(deviceCount);
|
|
++deviceCount;
|
|
}
|
|
|
|
int minColumnWidths[] = { 80, 120, 100, 250 };
|
|
for (int i = 0; i < 4; ++i) {
|
|
m_table->adjustColumn(i);
|
|
if (m_table->columnWidth(i) < minColumnWidths[i])
|
|
m_table->setColumnWidth(i, minColumnWidths[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::makeConnectionList(unsigned int direction,
|
|
TQStringList &list)
|
|
{
|
|
TQByteArray data;
|
|
TQByteArray replyData;
|
|
TQCString replyType;
|
|
TQDataStream arg(data, IO_WriteOnly);
|
|
arg << (int)Device::Midi;
|
|
arg << direction;
|
|
|
|
if (!rgapp->sequencerCall("getConnections(int, unsigned int)", replyType, replyData, data)) {
|
|
RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
|
|
list.append(i18n("No connection"));
|
|
return ;
|
|
}
|
|
|
|
TQDataStream reply(replyData, IO_ReadOnly);
|
|
unsigned int connections = 0;
|
|
if (replyType == "unsigned int")
|
|
reply >> connections;
|
|
|
|
for (unsigned int i = 0; i < connections; ++i) {
|
|
|
|
TQByteArray data;
|
|
TQByteArray replyData;
|
|
TQCString replyType;
|
|
TQDataStream arg(data, IO_WriteOnly);
|
|
arg << (int)Device::Midi;
|
|
arg << direction;
|
|
arg << i;
|
|
|
|
|
|
if (!rgapp->sequencerCall("getConnection(int, unsigned int, unsigned int)",
|
|
replyType, replyData, data)) {
|
|
RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
|
|
list.append(i18n("No connection"));
|
|
return ;
|
|
}
|
|
|
|
TQDataStream reply(replyData, IO_ReadOnly);
|
|
TQString connection;
|
|
if (replyType == "TQString") {
|
|
reply >> connection;
|
|
list.append(connection);
|
|
}
|
|
}
|
|
|
|
list.append(i18n("No connection"));
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::setModified(bool m)
|
|
{
|
|
if (m_modified == m)
|
|
return ;
|
|
enableButtonOK(m);
|
|
enableButtonApply(m);
|
|
m_modified = m;
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotOk()
|
|
{
|
|
slotApply();
|
|
accept();
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotClose()
|
|
{
|
|
if (m_modified) {
|
|
|
|
int reply = KMessageBox::questionYesNo(this,
|
|
i18n("Apply pending changes?"));
|
|
|
|
if (reply == KMessageBox::Yes)
|
|
slotApply();
|
|
}
|
|
|
|
reject();
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotApply()
|
|
{
|
|
KMacroCommand *command = new KMacroCommand("Edit Devices");
|
|
|
|
// first delete deleted devices, in reverse order of id (so that
|
|
// if we undo this command we'll get the original ids back... probably)
|
|
|
|
std::vector<DeviceId> ids;
|
|
|
|
for (DeviceListIterator i = m_devices.begin();
|
|
i != m_devices.end(); ++i) {
|
|
if (m_deletedDevices.find((*i)->getId()) != m_deletedDevices.end()) {
|
|
ids.push_back((*i)->getId());
|
|
}
|
|
}
|
|
|
|
std::sort(ids.begin(), ids.end());
|
|
|
|
for (int i = ids.size() - 1; i >= 0; --i) {
|
|
command->addCommand(new CreateOrDeleteDeviceCommand(m_studio, ids[i]));
|
|
}
|
|
|
|
// create the new devices, and rename and/or set connections for
|
|
// any others that have changed
|
|
|
|
for (int i = 0; i < m_table->numRows(); ++i) {
|
|
int deviceId = getDeviceIdAt(i);
|
|
if (deviceId < 0) { // new device
|
|
command->addCommand(new CreateOrDeleteDeviceCommand
|
|
(m_studio,
|
|
qstrtostr(m_table->text(i, LABEL_COL)),
|
|
Device::Midi,
|
|
m_table->text(i, DIRECTION_COL) == "Play" ?
|
|
MidiDevice::Play :
|
|
MidiDevice::Record,
|
|
qstrtostr(m_table->text(i, CONNECTION_COL))));
|
|
} else { // existing device
|
|
Device *device = m_studio->getDevice(deviceId);
|
|
if (!device) {
|
|
/*
|
|
std::cerr <<
|
|
"WARNING: DeviceEditorDialog::slotApply(): device at row "
|
|
<< i << " (id " << deviceId
|
|
<< ") claims not to be new, but isn't in the studio"
|
|
<< std::endl;
|
|
*/
|
|
} else {
|
|
std::string name = qstrtostr(m_table->text(i, LABEL_COL));
|
|
std::string conn = qstrtostr(m_table->text(i, CONNECTION_COL));
|
|
if (device->getName() != name) {
|
|
command->addCommand(new RenameDeviceCommand
|
|
(m_studio, deviceId, name));
|
|
}
|
|
if (device->getConnection() != conn) {
|
|
command->addCommand(new ReconnectDeviceCommand
|
|
(m_studio, deviceId, conn));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_document->getCommandHistory()->addCommand(command);
|
|
|
|
m_deletedDevices.clear();
|
|
|
|
populate();
|
|
setModified(false);
|
|
}
|
|
|
|
int
|
|
DeviceEditorDialog::getDeviceIdAt(int row) // -1 for new device w/o an id yet
|
|
{
|
|
TQString t(m_table->text(row, 0));
|
|
|
|
TQRegExp re("^.*(\\d+).*$");
|
|
re.search(t);
|
|
|
|
TQString number = re.cap(1);
|
|
int id = -1;
|
|
|
|
if (!number.isNull() && number != "")
|
|
{
|
|
id = number.toInt() - 1; // displayed device numbers are 1-based
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotAddPlayDevice()
|
|
{
|
|
int n = m_table->numRows();
|
|
m_table->insertRows(n, 1);
|
|
m_table->setText(n, 0, i18n("<new device>"));
|
|
m_table->setText(n, 1, i18n("New Device"));
|
|
m_table->setText(n, 2, i18n("Play"));
|
|
|
|
TQComboTableItem *item =
|
|
new TQComboTableItem(m_table, m_playConnections, false);
|
|
item->setCurrentItem(m_playConnections.size() - 1);
|
|
m_table->setItem(n, 3, item);
|
|
m_table->adjustRow(n);
|
|
|
|
setModified(true);
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotAddRecordDevice()
|
|
{
|
|
int n = m_table->numRows();
|
|
m_table->insertRows(n, 1);
|
|
m_table->setText(n, 0, i18n("<new device>"));
|
|
m_table->setText(n, 1, i18n("New Device"));
|
|
m_table->setText(n, 2, i18n("Record"));
|
|
|
|
TQComboTableItem *item =
|
|
new TQComboTableItem(m_table, m_recordConnections, false);
|
|
item->setCurrentItem(m_recordConnections.size() - 1);
|
|
m_table->setItem(n, 3, item);
|
|
m_table->adjustRow(n);
|
|
|
|
setModified(true);
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotDeleteDevice()
|
|
{
|
|
int n = m_table->currentRow();
|
|
m_deletedDevices.insert(getDeviceIdAt(n));
|
|
m_table->removeRow(n);
|
|
setModified(true);
|
|
}
|
|
|
|
void
|
|
DeviceEditorDialog::slotValueChanged(int, int)
|
|
{
|
|
setModified(true);
|
|
}
|
|
|
|
}
|
|
#include "DeviceEditorDialog.moc"
|