diff --git a/doc/img/bin.xcf b/doc/img/bin.xcf new file mode 100644 index 000000000..bcf620583 Binary files /dev/null and b/doc/img/bin.xcf differ diff --git a/doc/img/keyboard.xcf b/doc/img/keyboard.xcf new file mode 100644 index 000000000..69f97d565 Binary files /dev/null and b/doc/img/keyboard.xcf differ diff --git a/doc/img/load.xcf b/doc/img/load.xcf new file mode 100644 index 000000000..aefc3e946 Binary files /dev/null and b/doc/img/load.xcf differ diff --git a/doc/img/save.xcf b/doc/img/save.xcf new file mode 100644 index 000000000..7582c32e5 Binary files /dev/null and b/doc/img/save.xcf differ diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 51a407ad0..9a47d4625 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -8,6 +8,7 @@ set(sdrbase_SOURCES channel/channelsinkapi.cpp channel/channelsourceapi.cpp + commands/command.cpp dsp/afsquelch.cpp dsp/agc.cpp @@ -85,6 +86,7 @@ set(sdrbase_HEADERS channel/channelsinkapi.h channel/channelsourceapi.h + commands/command.h dsp/afsquelch.h dsp/downchannelizer.h diff --git a/sdrgui/commands/command.cpp b/sdrbase/commands/command.cpp similarity index 71% rename from sdrgui/commands/command.cpp rename to sdrbase/commands/command.cpp index ab848b14a..a510d0de0 100644 --- a/sdrgui/commands/command.cpp +++ b/sdrbase/commands/command.cpp @@ -17,6 +17,8 @@ #include "command.h" #include "util/simpleserializer.h" +#include + Command::Command() { resetToDefaults(); @@ -31,8 +33,9 @@ void Command::resetToDefaults() m_description = "no name"; m_command = ""; m_argString = ""; - m_pressKey = static_cast(0); - m_releaseKey = static_cast(0); + m_key = static_cast(0); + m_associateKey = false; + m_release = false; } QByteArray Command::serialize() const @@ -43,8 +46,10 @@ QByteArray Command::serialize() const s.writeString(2, m_description); s.writeString(3, m_command); s.writeString(4, m_argString); - s.writeS32(5, (int) m_pressKey); - s.writeS32(6, (int) m_releaseKey); + s.writeS32(5, (int) m_key); + s.writeS32(6, (int) m_keyModifiers); + s.writeBool(7, m_associateKey); + s.writeBool(8, m_release); return s.final(); } @@ -69,9 +74,11 @@ bool Command::deserialize(const QByteArray& data) d.readString(3, &m_command, ""); d.readString(4, &m_argString, ""); d.readS32(5, &tmpInt, 0); - m_pressKey = static_cast(tmpInt); + m_key = static_cast(tmpInt); d.readS32(6, &tmpInt, 0); - m_releaseKey = static_cast(tmpInt); + m_keyModifiers = static_cast(tmpInt); + d.readBool(7, &m_associateKey, false); + d.readBool(8, &m_release, false); return true; } @@ -81,3 +88,21 @@ bool Command::deserialize(const QByteArray& data) return false; } } + +QString Command::getKeyLabel() const +{ + if (m_key == 0) + { + return ""; + } + else if (m_keyModifiers != Qt::NoModifier) + { + QString altGrStr = m_keyModifiers & Qt::GroupSwitchModifier ? "Gr " : ""; + int maskedModifiers = (m_keyModifiers & 0x3FFFFFFF) + ((m_keyModifiers & 0x40000000)>>3); + return altGrStr + QKeySequence(maskedModifiers, m_key).toString(); + } + else + { + return QKeySequence(m_key).toString(); + } +} diff --git a/sdrgui/commands/command.h b/sdrbase/commands/command.h similarity index 63% rename from sdrgui/commands/command.h rename to sdrbase/commands/command.h index cca9e0bc7..76fc21047 100644 --- a/sdrgui/commands/command.h +++ b/sdrbase/commands/command.h @@ -20,6 +20,7 @@ #include #include #include +#include class Command { @@ -39,20 +40,51 @@ public: const QString& getGroup() const { return m_group; } void setDescription(const QString& description) { m_description = description; } const QString& getDescription() const { return m_description; } - void setPressKey(Qt::Key key) { m_pressKey = key; } - Qt::Key getPressKey() const { return m_pressKey; } - void setReleaseKey(Qt::Key key) { m_releaseKey = key; } - Qt::Key getReleaseKey() const { return m_releaseKey; } + void setKey(Qt::Key key) { m_key = key; } + Qt::Key getKey() const { return m_key; } + void setKeyModifiers(Qt::KeyboardModifiers keyModifiers) { m_keyModifiers = keyModifiers; } + Qt::KeyboardModifiers getKeyModifiers() const { return m_keyModifiers; } + void setAssociateKey(bool associate) { m_associateKey = associate; } + bool getAssociateKey() const { return m_associateKey; } + void setRelease(bool release) { m_release = release; } + bool getRelease() const { return m_release; } + + QString getKeyLabel() const; + + static bool commandCompare(const Command *c1, Command *c2) + { + if (c1->m_group != c2->m_group) + { + return c1->m_group < c2->m_group; + } + else + { + if (c1->m_description != c2->m_description) { + return c1->m_description < c2->m_description; + } + else + { + if (c1->m_key != c2->m_key) { + return c1->m_key < c2->m_key; + } else { + return c1->m_release; + } + } + } + } private: QString m_command; QString m_argString; QString m_group; QString m_description; - Qt::Key m_pressKey; - Qt::Key m_releaseKey; + Qt::Key m_key; + Qt::KeyboardModifiers m_keyModifiers; + bool m_associateKey; + bool m_release; }; - +Q_DECLARE_METATYPE(const Command*); +Q_DECLARE_METATYPE(Command*); #endif /* SDRBASE_COMMANDS_COMMAND_H_ */ diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index 20ed349e9..ecef3b911 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -53,6 +53,7 @@ SOURCES += audio/audiodeviceinfo.cpp\ audio/audioinput.cpp\ channel/channelsinkapi.cpp\ channel/channelsourceapi.cpp\ + commands/command.cpp\ device/devicesourceapi.cpp\ device/devicesinkapi.cpp\ device/deviceenumerator.cpp\ @@ -119,7 +120,8 @@ HEADERS += audio/audiodeviceinfo.h\ audio/audiooutput.h\ audio/audioinput.h\ channel/channelsinkapi.h\ - channel/channelsourceapi.h\ + channel/channelsourceapi.h\ + commands/command.h\ device/devicesourceapi.h\ device/devicesinkapi.h\ device/deviceenumerator.h\ diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index 75d8ab5e2..7e4a15a37 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -2,6 +2,7 @@ #include #include "settings/mainsettings.h" +#include "commands/command.h" MainSettings::MainSettings() { @@ -14,6 +15,11 @@ MainSettings::~MainSettings() { delete m_presets[i]; } + + for(int i = 0; i < m_commands.count(); ++i) + { + delete m_commands[i]; + } } void MainSettings::load() @@ -32,7 +38,7 @@ void MainSettings::load() for(int i = 0; i < groups.size(); ++i) { - if(groups[i].startsWith("preset")) + if (groups[i].startsWith("preset")) { s.beginGroup(groups[i]); Preset* preset = new Preset; @@ -48,6 +54,22 @@ void MainSettings::load() s.endGroup(); } + else if (groups[i].startsWith("command")) + { + s.beginGroup(groups[i]); + Command* command = new Command; + + if(command->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) + { + m_commands.append(command); + } + else + { + delete command; + } + + s.endGroup(); + } } } @@ -67,19 +89,27 @@ void MainSettings::save() const for(int i = 0; i < groups.size(); ++i) { - if(groups[i].startsWith("preset")) + if ((groups[i].startsWith("preset")) || (groups[i].startsWith("command"))) { s.remove(groups[i]); } } - for(int i = 0; i < m_presets.count(); ++i) + for (int i = 0; i < m_presets.count(); ++i) { QString group = QString("preset-%1").arg(i + 1); s.beginGroup(group); s.setValue("data", qCompress(m_presets[i]->serialize()).toBase64()); s.endGroup(); } + + for (int i = 0; i < m_commands.count(); ++i) + { + QString group = QString("command-%1").arg(i + 1); + s.beginGroup(group); + s.setValue("data", qCompress(m_commands[i]->serialize()).toBase64()); + s.endGroup(); + } } void MainSettings::resetToDefaults() @@ -124,3 +154,35 @@ const Preset* MainSettings::getPreset(const QString& groupName, quint64 centerFr return 0; } + +void MainSettings::addCommand(Command *command) +{ + m_commands.append(command); +} + +void MainSettings::deleteCommand(const Command* command) +{ + m_commands.removeAll((Command*)command); + delete (Command*)command; +} + +void MainSettings::sortCommands() +{ + qSort(m_commands.begin(), m_commands.end(), Command::commandCompare); +} + +const Command* MainSettings::getCommand(const QString& groupName, const QString& description) const +{ + int nbCommands = getCommandCount(); + + for (int i = 0; i < nbCommands; i++) + { + if ((getCommand(i)->getGroup() == groupName) && + (getCommand(i)->getDescription() == description)) + { + return getCommand(i); + } + } + + return 0; +} diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index da82e3563..6edb0c98a 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -6,6 +6,8 @@ #include "preset.h" #include "audio/audiodeviceinfo.h" +class Command; + class MainSettings { public: MainSettings(); @@ -23,6 +25,13 @@ public: const Preset* getPreset(const QString& groupName, quint64 centerFrequency, const QString& description) const; void sortPresets(); + void addCommand(Command *command); + void deleteCommand(const Command* command); + int getCommandCount() const { return m_commands.count(); } + const Command* getCommand(int index) const { return m_commands[index]; } + const Command* getCommand(const QString& groupName, const QString& description) const; + void sortCommands(); + Preset* getWorkingPreset() { return &m_workingPreset; } int getSourceIndex() const { return m_preferences.getSourceIndex(); } void setSourceIndex(int value) { m_preferences.setSourceIndex(value); } @@ -52,6 +61,8 @@ protected: Preset m_workingPreset; typedef QList Presets; Presets m_presets; + typedef QList Commands; + Commands m_commands; }; #endif // INCLUDE_SETTINGS_H diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index d907d9480..cc10f3c5b 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -9,7 +9,9 @@ set(sdrgui_SOURCES gui/channelwindow.cpp gui/clickablelabel.cpp gui/colormapper.cpp + gui/commanditem.cpp gui/cwkeyergui.cpp + gui/editcommanddialog.cpp gui/externalclockbutton.cpp gui/externalclockdialog.cpp gui/glscope.cpp @@ -40,8 +42,6 @@ set(sdrgui_SOURCES gui/valuedial.cpp gui/valuedialz.cpp - commands/command.cpp - dsp/scopevis.cpp dsp/scopevisng.cpp dsp/scopevismulti.cpp @@ -62,7 +62,9 @@ set(sdrgui_HEADERS gui/buttonswitch.h gui/channelwindow.h gui/colormapper.h + gui/commanditem.h gui/cwkeyergui.h + gui/editcommanddialog.h gui/externalclockbutton.h gui/externalclockdialog.h gui/glscope.h @@ -94,8 +96,6 @@ set(sdrgui_HEADERS gui/valuedial.h gui/valuedialz.h - commands/command.h - dsp/scopevis.h dsp/scopevisng.h dsp/scopevismulti.h @@ -119,6 +119,7 @@ set(sdrgui_FORMS gui/addpresetdialog.ui gui/basicchannelsettingsdialog.ui gui/cwkeyergui.ui + gui/editcommanddialog.ui gui/externalclockdialog.ui gui/glscopegui.ui gui/glscopenggui.ui diff --git a/sdrgui/gui/commanditem.cpp b/sdrgui/gui/commanditem.cpp new file mode 100644 index 000000000..f35920a05 --- /dev/null +++ b/sdrgui/gui/commanditem.cpp @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "commanditem.h" + +CommandItem::CommandItem(QTreeWidgetItem* parent, const QStringList& strings, const QString& description, int type) : + QTreeWidgetItem(parent, strings, type), + m_description(description) +{ +} + +bool CommandItem::operator<(const QTreeWidgetItem& other) const +{ + return m_description < ((const CommandItem&)other).m_description; +} diff --git a/sdrgui/gui/commanditem.h b/sdrgui/gui/commanditem.h new file mode 100644 index 000000000..1ab492858 --- /dev/null +++ b/sdrgui/gui/commanditem.h @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +class CommandItem : public QTreeWidgetItem { +public: + CommandItem(QTreeWidgetItem* parent, const QStringList& strings, const QString& description, int type); + bool operator<(const QTreeWidgetItem& other) const; + +private: + QString m_description; +}; diff --git a/sdrgui/gui/editcommanddialog.cpp b/sdrgui/gui/editcommanddialog.cpp new file mode 100644 index 000000000..e35c64869 --- /dev/null +++ b/sdrgui/gui/editcommanddialog.cpp @@ -0,0 +1,252 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "editcommanddialog.h" +#include "ui_editcommanddialog.h" +#include "commands/command.h" + +#include +#include +#include +#include + +const std::vector EditCommandDialog::m_composeKeys = {Qt::Key_Shift, Qt::Key_Control, Qt::Key_Meta, Qt::Key_Alt, Qt::Key_AltGr}; + +EditCommandDialog::EditCommandDialog(const QStringList& groups, const QString& group, QWidget* parent) : + QDialog(parent), + ui(new Ui::EditCommandDialog), + m_key(static_cast(0)) +{ + ui->setupUi(this); + ui->group->addItems(groups); + ui->group->lineEdit()->setText(group); + setKeyAssociate(); + setKeyLabel(); +} + +EditCommandDialog::~EditCommandDialog() +{ + delete ui; +} + +QString EditCommandDialog::getGroup() const +{ + return ui->group->lineEdit()->text(); +} + +QString EditCommandDialog::getDescription() const +{ + return ui->description->text(); +} + +void EditCommandDialog::setGroup(const QString& group) +{ + ui->group->lineEdit()->setText(group); +} + +void EditCommandDialog::setDescription(const QString& description) +{ + ui->description->setText(description); +} + +QString EditCommandDialog::getCommand() const +{ + return ui->command->text(); +} + +void EditCommandDialog::setCommand(const QString& command) +{ + ui->command->setText(command); +} + +QString EditCommandDialog::getArguments() const +{ + return ui->args->text(); +} + +void EditCommandDialog::setArguments(const QString& arguments) +{ + ui->args->setText(arguments); +} + +Qt::Key EditCommandDialog::getKey() const +{ + return m_key; +} + +Qt::KeyboardModifiers EditCommandDialog::getKeyModifiers() const +{ + return m_keyModifiers; +} + +void EditCommandDialog::setKey(Qt::Key key, Qt::KeyboardModifiers modifiers) +{ + m_key = key; + m_keyModifiers = modifiers; + setKeyAssociate(); + setKeyLabel(); +} + +bool EditCommandDialog::getAssociateKey() const +{ + return ui->keyAssociate->isChecked(); +} + +void EditCommandDialog::setAssociateKey(bool release) +{ + ui->keyAssociate->setChecked(release); +} + +bool EditCommandDialog::getRelease() const +{ + return ui->keyRelease->isChecked(); +} + +void EditCommandDialog::setRelease(bool release) +{ + ui->keyRelease->setChecked(release); +} + +void EditCommandDialog::on_showFileDialog_clicked(bool checked __attribute__((unused))) +{ + QString commandFileName = ui->command->text(); + QFileInfo commandFileInfo(commandFileName); + QString commandFolderName = commandFileInfo.baseName(); + QFileInfo commandDirInfo(commandFolderName); + QString dirStr; + + if (commandFileInfo.exists()) { + dirStr = commandFileName; + } else if (commandDirInfo.exists()) { + dirStr = commandFolderName; + } else { + dirStr = "."; + } + + QString fileName = QFileDialog::getOpenFileName( + this, + tr("Select command"), + dirStr, + tr("Python (*.py);;Shell (*.sh *.bat);;Binary (*.bin *.exe);;All (*.*)")); + + if (fileName != "") { + ui->command->setText(fileName); + } +} + +void EditCommandDialog::on_keyCapture_toggled(bool checked) +{ + if (checked) + { + ui->keyCapture->setFocus(); + ui->keyCapture->setFocusPolicy(Qt::StrongFocus); + } + else + { + ui->keyCapture->setFocusPolicy(Qt::NoFocus); + ui->keyCapture->clearFocus(); + } +} + +void EditCommandDialog::keyPressEvent(QKeyEvent *e) +{ + if (ui->keyCapture->isChecked() && (!isComposeKey(static_cast(e->key())))) + { + m_key = static_cast(e->key()); + + if (e->modifiers()) + { + //qDebug("EditCommandDialog::keyPressEvent: has modifiers: %x", QFlags::Int(e->modifiers())); + m_keyModifiers = e->modifiers(); + } + else + { + m_keyModifiers = Qt::NoModifier; + } + + setKeyAssociate(); + setKeyLabel(); + //qDebug("EditCommandDialog::keyPressEvent: key: %x", m_key); + + ui->keyCapture->setChecked(false); + ui->keyCapture->setFocusPolicy(Qt::NoFocus); + ui->keyCapture->clearFocus(); + } +} + +void EditCommandDialog::toCommand(Command& command) const +{ + command.setGroup(ui->group->currentText()); + command.setDescription(ui->description->text()); + command.setCommand(ui->command->text()); + command.setArgString(ui->args->text()); + command.setAssociateKey(ui->keyAssociate->isChecked()); + command.setKey(m_key); + command.setKeyModifiers(m_keyModifiers); + command.setRelease(ui->keyRelease->isChecked()); +} + +void EditCommandDialog::fromCommand(const Command& command) +{ + ui->group->lineEdit()->setText(command.getGroup()); + ui->description->setText(command.getDescription()); + ui->command->setText(command.getCommand()); + ui->args->setText(command.getArgString()); + ui->keyAssociate->setChecked(command.getAssociateKey()); + m_key = command.getKey(); + m_keyModifiers = command.getKeyModifiers(); + setKeyAssociate(); + setKeyLabel(); + ui->keyRelease->setChecked(command.getRelease()); +} + +bool EditCommandDialog::isComposeKey(Qt::Key key) +{ + auto it = std::find(m_composeKeys.begin(), m_composeKeys.end(), key); + return it != m_composeKeys.end(); +} + +void EditCommandDialog::setKeyLabel() +{ + if (m_key == 0) + { + ui->keyLabel->setText(""); + } + else if (m_keyModifiers != Qt::NoModifier) + { + QString altGrStr = m_keyModifiers & Qt::GroupSwitchModifier ? "Gr " : ""; + int maskedModifiers = (m_keyModifiers & 0x3FFFFFFF) + ((m_keyModifiers & 0x40000000)>>3); + ui->keyLabel->setText(altGrStr + QKeySequence(maskedModifiers, m_key).toString()); + } + else + { + ui->keyLabel->setText(QKeySequence(m_key).toString()); + } +} + +void EditCommandDialog::setKeyAssociate() +{ + if (m_key == 0) + { + ui->keyAssociate->setChecked(false); + ui->keyAssociate->setEnabled(false); + } + else + { + ui->keyAssociate->setEnabled(true); + } +} + diff --git a/sdrgui/gui/editcommanddialog.h b/sdrgui/gui/editcommanddialog.h new file mode 100644 index 000000000..6f6184844 --- /dev/null +++ b/sdrgui/gui/editcommanddialog.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_EDITCOMMANDDIALOG_H_ +#define SDRGUI_GUI_EDITCOMMANDDIALOG_H_ + +#include +#include + +namespace Ui { + class EditCommandDialog; +} + +class Command; + +class EditCommandDialog : public QDialog { + Q_OBJECT + +public: + explicit EditCommandDialog(const QStringList& groups, const QString& group, QWidget* parent = 0); + ~EditCommandDialog(); + + QString getGroup() const; + void setGroup(const QString& group); + QString getDescription() const; + void setDescription(const QString& description); + QString getCommand() const; + void setCommand(const QString& command); + QString getArguments() const; + void setArguments(const QString& arguments); + Qt::Key getKey() const; + Qt::KeyboardModifiers getKeyModifiers() const; + void setKey(Qt::Key key, Qt::KeyboardModifiers modifiers); + bool getAssociateKey() const; + void setAssociateKey(bool associate); + bool getRelease() const; + void setRelease(bool release); + + void toCommand(Command& command) const; + void fromCommand(const Command& command); + +private: + Ui::EditCommandDialog* ui; + Qt::Key m_key; + Qt::KeyboardModifiers m_keyModifiers; + + virtual void keyPressEvent(QKeyEvent *e); + bool isComposeKey(Qt::Key key); + void setKeyLabel(); + void setKeyAssociate(); + + static const std::vector m_composeKeys; + +private slots: + void on_showFileDialog_clicked(bool checked); + void on_keyCapture_toggled(bool checked); + +}; + + + +#endif /* SDRGUI_GUI_EDITCOMMANDDIALOG_H_ */ diff --git a/sdrgui/gui/editcommanddialog.ui b/sdrgui/gui/editcommanddialog.ui new file mode 100644 index 000000000..6a052b533 --- /dev/null +++ b/sdrgui/gui/editcommanddialog.ui @@ -0,0 +1,270 @@ + + + EditCommandDialog + + + + 0 + 0 + 324 + 239 + + + + + Sans Serif + 9 + + + + Edit command + + + + + + Command Description + + + + + + &Group + + + group + + + + + + + Choose or create new group + + + true + + + + + + + &Description + + + description + + + + + + + Edit description + + + + + + + + + + + + + 0 + 0 + + + + Cmd + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Choose command + + + + + + + :/load.png:/load.png + + + + + + + ... + + + + + + + + + + + Arg + + + + + + + Edit arguments + + + + + + + + + 6 + + + 6 + + + + + Associate key event to run the command + + + + + + + + + + Enter key to associate + + + Key + + + true + + + + + + + + 0 + 0 + + + + Associated key or composed key + + + .. + + + + + + + Run command on key release + + + Release + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + description + buttonBox + group + + + + + + + buttonBox + accepted() + EditCommandDialog + accept() + + + 257 + 194 + + + 157 + 203 + + + + + buttonBox + rejected() + EditCommandDialog + reject() + + + 314 + 194 + + + 286 + 203 + + + + +
diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 2456dcf55..df81b5f5c 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -34,7 +34,9 @@ #include "audio/audiodeviceinfo.h" #include "gui/indicator.h" #include "gui/presetitem.h" +#include "gui/commanditem.h" #include "gui/addpresetdialog.h" +#include "gui/editcommanddialog.h" #include "gui/pluginsdialog.h" #include "gui/aboutdialog.h" #include "gui/rollupwidget.h" @@ -55,6 +57,7 @@ #include "webapi/webapirequestmapper.h" #include "webapi/webapiserver.h" #include "webapi/webapiadaptergui.h" +#include "commands/command.h" #include "mainwindow.h" #include "ui_mainwindow.h" @@ -514,6 +517,13 @@ void MainWindow::loadSettings() } } + m_settings.sortCommands(); + + for (int i = 0; i < m_settings.getCommandCount(); ++i) + { + treeItem = addCommandToTree(m_settings.getCommand(i)); + } + setLoggingOptions(); } @@ -646,6 +656,44 @@ QTreeWidgetItem* MainWindow::addPresetToTree(const Preset* preset) return item; } +QTreeWidgetItem* MainWindow::addCommandToTree(const Command* command) +{ + QTreeWidgetItem* group = 0; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) + { + if(ui->commandTree->topLevelItem(i)->text(0) == command->getGroup()) + { + group = ui->commandTree->topLevelItem(i); + break; + } + } + + if(group == 0) + { + QStringList sl; + sl.append(command->getGroup()); + group = new QTreeWidgetItem(ui->commandTree, sl, PGroup); + group->setFirstColumnSpanned(true); + group->setExpanded(true); + ui->commandTree->sortByColumn(0, Qt::AscendingOrder); + } + + QStringList sl; + sl.append(QString("%1").arg(command->getDescription())); // Descriptions column + sl.append(QString("%1").arg(command->getKeyLabel())); // key column + sl.append(QString("%1").arg(command->getAssociateKey() ? command->getRelease() ? "R" : "P" : "")); // key press/release column + CommandItem* item = new CommandItem(group, sl, command->getDescription(), PItem); + item->setData(0, Qt::UserRole, qVariantFromValue(command)); + item->setTextAlignment(0, Qt::AlignLeft); + ui->presetTree->resizeColumnToContents(0); // Resize description column to minimum + ui->presetTree->resizeColumnToContents(1); // Resize key column to minimum + ui->presetTree->resizeColumnToContents(2); // Resize key press/release column to minimum + + //updatePresetControls(); + return item; +} + void MainWindow::applySettings() { } @@ -772,48 +820,170 @@ void MainWindow::on_action_View_Fullscreen_toggled(bool checked) else showNormal(); } +void MainWindow::on_commandNew_clicked() +{ + QStringList groups; + QString group = ""; + QString description = ""; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { + groups.append(ui->commandTree->topLevelItem(i)->text(0)); + } + + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if(item != 0) + { + if(item->type() == PGroup) { + group = item->text(0); + } else if(item->type() == PItem) { + group = item->parent()->text(0); + description = item->text(0); + } + } + + Command *command = new Command(); + command->setGroup(group); + command->setDescription(description); + EditCommandDialog editCommandDialog(groups, group, this); + editCommandDialog.fromCommand(*command); + + if (editCommandDialog.exec() == QDialog::Accepted) + { + editCommandDialog.toCommand(*command); + m_settings.addCommand(command); + ui->commandTree->setCurrentItem(addCommandToTree(command)); + m_settings.sortCommands(); + } +} + +void MainWindow::on_commandDuplicate_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + Command *commandCopy = new Command(*command); + m_settings.addCommand(commandCopy); + ui->commandTree->setCurrentItem(addCommandToTree(commandCopy)); + m_settings.sortCommands(); +} + +void MainWindow::on_commandEdit_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if(item != 0) + { + if(item->type() == PItem) + { + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + + if (command != 0) + { + QStringList groups; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { + groups.append(ui->commandTree->topLevelItem(i)->text(0)); + } + + EditCommandDialog editCommandDialog(groups, command->getGroup(), this); + editCommandDialog.fromCommand(*command); + + if (editCommandDialog.exec() == QDialog::Accepted) + { + Command* command_mod = const_cast(command); + editCommandDialog.toCommand(*command_mod); + m_settings.sortCommands(); + + ui->commandTree->clear(); + + for (int i = 0; i < m_settings.getCommandCount(); ++i) + { + QTreeWidgetItem *item_x = addCommandToTree(m_settings.getCommand(i)); + const Command* command_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + if (command_x == command_mod) { + ui->commandTree->setCurrentItem(item_x); + } + } + } + } + } + } +} + +void MainWindow::on_commandDelete_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if(item == 0) { + return; + } + + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + + if(command == 0) { + return; + } + + if (QMessageBox::question(this, + tr("Delete command"), + tr("Do you want to delete command '%1'?") + .arg(command->getDescription()), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + delete item; + m_settings.deleteCommand(command); + } +} + void MainWindow::on_presetSave_clicked() { - QStringList groups; - QString group; - QString description = ""; - for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) - groups.append(ui->presetTree->topLevelItem(i)->text(0)); + QStringList groups; + QString group; + QString description = ""; - QTreeWidgetItem* item = ui->presetTree->currentItem(); - if(item != 0) { - if(item->type() == PGroup) - group = item->text(0); - else if(item->type() == PItem) { - group = item->parent()->text(0); - description = item->text(0); - } - } + for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) { + groups.append(ui->presetTree->topLevelItem(i)->text(0)); + } - AddPresetDialog dlg(groups, group, this); + QTreeWidgetItem* item = ui->presetTree->currentItem(); - if (description.length() > 0) { - dlg.setDescription(description); - } + if(item != 0) + { + if(item->type() == PGroup) { + group = item->text(0); + } else if(item->type() == PItem) { + group = item->parent()->text(0); + description = item->text(0); + } + } - if(dlg.exec() == QDialog::Accepted) { - Preset* preset = m_settings.newPreset(dlg.group(), dlg.description()); - savePresetSettings(preset, ui->tabInputsView->currentIndex()); + AddPresetDialog dlg(groups, group, this); - ui->presetTree->setCurrentItem(addPresetToTree(preset)); - } + if (description.length() > 0) { + dlg.setDescription(description); + } - m_settings.sortPresets(); + if(dlg.exec() == QDialog::Accepted) { + Preset* preset = m_settings.newPreset(dlg.group(), dlg.description()); + savePresetSettings(preset, ui->tabInputsView->currentIndex()); + + ui->presetTree->setCurrentItem(addPresetToTree(preset)); + } + + m_settings.sortPresets(); } void MainWindow::on_presetUpdate_clicked() { QTreeWidgetItem* item = ui->presetTree->currentItem(); - if(item != 0) { - if(item->type() == PItem) { + if(item != 0) + { + if(item->type() == PItem) + { const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - if (preset != 0) { + + if (preset != 0) + { Preset* preset_mod = const_cast(preset); savePresetSettings(preset_mod, ui->tabInputsView->currentIndex()); } diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 414b77046..b92e1e85e 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -54,6 +54,8 @@ class QWidget; class WebAPIRequestMapper; class WebAPIServer; class WebAPIAdapterGUI; +class Preset; +class Command; namespace qtwebapp { class LoggerWithFile; @@ -317,6 +319,7 @@ private: void closeEvent(QCloseEvent*); void updatePresetControls(); QTreeWidgetItem* addPresetToTree(const Preset* preset); + QTreeWidgetItem* addCommandToTree(const Command* command); void applySettings(); void addSourceDevice(int deviceIndex); @@ -341,6 +344,10 @@ private slots: void on_presetDelete_clicked(); void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); + void on_commandNew_clicked(); + void on_commandDuplicate_clicked(); + void on_commandEdit_clicked(); + void on_commandDelete_clicked(); void on_action_Audio_triggered(); void on_action_Logging_triggered(); void on_action_DV_Serial_triggered(bool checked); diff --git a/sdrgui/mainwindow.ui b/sdrgui/mainwindow.ui index 5d82b3bb3..aebc9b6da 100644 --- a/sdrgui/mainwindow.ui +++ b/sdrgui/mainwindow.ui @@ -318,173 +318,6 @@ 3 - - - - Save current settings as new preset - - - ... - - - - :/preset-save.png:/preset-save.png - - - - 16 - 16 - - - - - - - - Load selected preset - - - ... - - - - :/preset-load.png:/preset-load.png - - - - 16 - 16 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Delete selected preset - - - ... - - - - :/preset-delete.png:/preset-delete.png - - - - 16 - 16 - - - - - - - - Update selected preset with current settings - - - ... - - - - :/preset-update.png:/preset-update.png - - - - 16 - 16 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Save the current settings (inc. presets) - - - ... - - - - :/preset-last.png:/preset-last.png - - - - 16 - 16 - - - - - - - - Export current preset to file - - - - - - - :/export.png:/export.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Import preset from file into current group - - - - - - - :/import.png:/import.png - - - @@ -519,6 +352,160 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save current settings as new preset + + + ... + + + + :/create.png:/create.png + + + + 16 + 16 + + + + + + + + Delete selected preset + + + ... + + + + :/bin.png:/bin.png + + + + 16 + 16 + + + + + + + + Update selected preset with current settings + + + ... + + + + :/edit.png:/edit.png + + + + 16 + 16 + + + + + + + + Import preset from file into current group + + + + + + + :/import.png:/import.png + + + + + + + Export current preset to file + + + + + + + :/export.png:/export.png + + + + + + + Save the current settings (inc. presets) + + + ... + + + + :/save.png:/save.png + + + + 16 + 16 + + + + + + + + Load selected preset + + + ... + + + + :/load.png:/load.png + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -584,7 +571,7 @@ 2 - + 5 @@ -604,12 +591,12 @@ - Key press + Key - Key release + P/R @@ -642,6 +629,20 @@ + + + + Duplicate command + + + + + + + :/duplicate.png:/duplicate.png + + + @@ -694,7 +695,34 @@ - :/preset-last.png:/preset-last.png + :/save.png:/save.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Toggle keyboard to command connection + + + + + + + :/keyboard.png:/keyboard.png @@ -721,7 +749,7 @@ - :/preset-delete.png:/preset-delete.png + :/bin.png:/bin.png @@ -873,11 +901,17 @@ commandsDock + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
presetTree presetSave presetDelete - presetLoad diff --git a/sdrgui/resources/bin.png b/sdrgui/resources/bin.png new file mode 100644 index 000000000..b20e354d6 Binary files /dev/null and b/sdrgui/resources/bin.png differ diff --git a/sdrgui/resources/duplicate.png b/sdrgui/resources/duplicate.png new file mode 100644 index 000000000..377d24db3 Binary files /dev/null and b/sdrgui/resources/duplicate.png differ diff --git a/sdrgui/resources/keyboard.png b/sdrgui/resources/keyboard.png new file mode 100644 index 000000000..dcf2489ae Binary files /dev/null and b/sdrgui/resources/keyboard.png differ diff --git a/sdrgui/resources/load.png b/sdrgui/resources/load.png new file mode 100644 index 000000000..80b4e7515 Binary files /dev/null and b/sdrgui/resources/load.png differ diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index f72d85d5a..27eaec90d 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -85,5 +85,10 @@ edit.png listing.png create.png + duplicate.png + bin.png + save.png + load.png + keyboard.png diff --git a/sdrgui/resources/save.png b/sdrgui/resources/save.png new file mode 100644 index 000000000..ced55b2ed Binary files /dev/null and b/sdrgui/resources/save.png differ diff --git a/sdrgui/sdrgui.pro b/sdrgui/sdrgui.pro index 7368cb303..9210a5db0 100644 --- a/sdrgui/sdrgui.pro +++ b/sdrgui/sdrgui.pro @@ -50,7 +50,9 @@ SOURCES += mainwindow.cpp\ gui/channelwindow.cpp\ gui/clickablelabel.cpp\ gui/colormapper.cpp\ + gui/commanditem.cpp\ gui/cwkeyergui.cpp\ + gui/editcommanddialog.cpp\ gui/externalclockbutton.cpp\ gui/externalclockdialog.cpp\ gui/glscope.cpp\ @@ -97,7 +99,9 @@ HEADERS += mainwindow.h\ gui/channelwindow.h\ gui/clickablelabel.h\ gui/colormapper.h\ + gui/commanditem.h\ gui/cwkeyergui.h\ + gui/editcommanddialog.h\ gui/externalclockbutton.h\ gui/externalclockdialog.h\ gui/glscope.h\ @@ -132,6 +136,7 @@ FORMS += mainwindow.ui\ gui/addpresetdialog.ui\ gui/basicchannelsettingsdialog.ui\ gui/cwkeyergui.ui\ + gui/editcommanddialog.ui\ gui/externalclockdialog.ui\ gui/audiodialog.ui\ gui/glscopegui.ui\