1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-23 01:55:48 -05:00

Add buttons to stack MDI windows vertically and put in tabs.

Use right click to auto-stack sub-windows, rather than having a
dedicated button.
Allow maximize button to make window full screen, if already maximized.
Add title to device windows, for when displayed in tabs.
Add menu button to workspace toolbar, for Android only, to avoid having
menu bar, which takes up a lot of space.
Add configuration presets button to workspace toolbar.
Add icons for window arangement
This commit is contained in:
Jon Beniston 2022-12-20 16:10:11 +00:00
parent 7f720a369e
commit d1c67c971e
10 changed files with 464 additions and 78 deletions

View File

@ -65,6 +65,13 @@ QByteArray Configuration::serialize() const
s.writeBool(301 + i, m_workspaceAutoStackOptions[i]); s.writeBool(301 + i, m_workspaceAutoStackOptions[i]);
} }
nitems = m_workspaceTabSubWindowsOptions.size() < 99 ? m_workspaceTabSubWindowsOptions.size() : 99;
s.writeS32(400, nitems);
for (int i = 0; i < nitems; i++) {
s.writeBool(401 + i, m_workspaceTabSubWindowsOptions[i]);
}
return s.final(); return s.final();
} }
@ -113,6 +120,14 @@ bool Configuration::deserialize(const QByteArray& data)
d.readBool(301 + i, &m_workspaceAutoStackOptions.back()); d.readBool(301 + i, &m_workspaceAutoStackOptions.back());
} }
d.readS32(400, &nitems, 0);
for (int i = 0; i < nitems; i++)
{
m_workspaceTabSubWindowsOptions.push_back(true);
d.readBool(401 + i, &m_workspaceTabSubWindowsOptions.back());
}
return true; return true;
} }
else else
@ -128,4 +143,5 @@ void Configuration::clearData()
m_featureSetPreset.clearFeatures(); m_featureSetPreset.clearFeatures();
m_workspaceGeometries.clear(); m_workspaceGeometries.clear();
m_workspaceAutoStackOptions.clear(); m_workspaceAutoStackOptions.clear();
m_workspaceTabSubWindowsOptions.clear();
} }

View File

@ -52,6 +52,8 @@ public:
const QList<QByteArray>& getWorkspaceGeometries() const { return m_workspaceGeometries; } const QList<QByteArray>& getWorkspaceGeometries() const { return m_workspaceGeometries; }
QList<bool>& getWorkspaceAutoStackOptions() { return m_workspaceAutoStackOptions; } QList<bool>& getWorkspaceAutoStackOptions() { return m_workspaceAutoStackOptions; }
const QList<bool>& getWorkspaceAutoStackOptions() const { return m_workspaceAutoStackOptions; } const QList<bool>& getWorkspaceAutoStackOptions() const { return m_workspaceAutoStackOptions; }
QList<bool>& getWorkspaceTabSubWindowsOptions() { return m_workspaceTabSubWindowsOptions; }
const QList<bool>& getWorkspaceTabSubWindowsOptions() const { return m_workspaceTabSubWindowsOptions; }
FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; } FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; }
const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; } const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; }
QList<Preset>& getDeviceSetPresets() { return m_deviceSetPresets; } QList<Preset>& getDeviceSetPresets() { return m_deviceSetPresets; }
@ -76,6 +78,7 @@ private:
QString m_description; QString m_description;
QList<QByteArray> m_workspaceGeometries; QList<QByteArray> m_workspaceGeometries;
QList<bool> m_workspaceAutoStackOptions; QList<bool> m_workspaceAutoStackOptions;
QList<bool> m_workspaceTabSubWindowsOptions;
FeatureSetPreset m_featureSetPreset; FeatureSetPreset m_featureSetPreset;
QList<Preset> m_deviceSetPresets; QList<Preset> m_deviceSetPresets;
}; };

View File

@ -26,11 +26,13 @@
#include <QObjectCleanupHandler> #include <QObjectCleanupHandler>
#include <QDesktopServices> #include <QDesktopServices>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QMdiArea>
#include "mainwindow.h" #include "mainwindow.h"
#include "gui/workspaceselectiondialog.h" #include "gui/workspaceselectiondialog.h"
#include "gui/devicesetselectiondialog.h" #include "gui/devicesetselectiondialog.h"
#include "gui/rollupcontents.h" #include "gui/rollupcontents.h"
#include "gui/dialogpositioner.h"
#include "channelgui.h" #include "channelgui.h"
@ -42,7 +44,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
m_contextMenuType(ContextMenuNone), m_contextMenuType(ContextMenuNone),
m_drag(false), m_drag(false),
m_resizer(this), m_resizer(this),
m_disableResize(false) m_disableResize(false),
m_mdi(nullptr)
{ {
qDebug("ChannelGUI::ChannelGUI"); qDebug("ChannelGUI::ChannelGUI");
setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@ -92,7 +95,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
m_maximizeButton->setFixedSize(20, 20); m_maximizeButton->setFixedSize(20, 20);
QIcon maximizeIcon(":/maximize.png"); QIcon maximizeIcon(":/maximize.png");
m_maximizeButton->setIcon(maximizeIcon); m_maximizeButton->setIcon(maximizeIcon);
m_maximizeButton->setToolTip("Adjust window to maximum size"); m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_hideButton = new QPushButton(); m_hideButton = new QPushButton();
m_hideButton->setFixedSize(20, 20); m_hideButton->setFixedSize(20, 20);
@ -311,7 +314,9 @@ void ChannelGUI::onWidgetRolled(QWidget *widget, bool show)
// onWidgetRolled being called twice. // onWidgetRolled being called twice.
// We need to make sure we don't save widget heights while this occurs. The // We need to make sure we don't save widget heights while this occurs. The
// window manager will take care of maximizing/restoring the window size. // window manager will take care of maximizing/restoring the window size.
if (!m_disableResize) // We do need to resize when a widget is rolled up, but we also need to avoid
// resizing when a window is maximized when first shown in tabbed layout
if (!m_disableResize && !isMaximized())
{ {
if (show) if (show)
{ {
@ -379,6 +384,9 @@ void ChannelGUI::sizeToContents()
size.setHeight(size.height() + getAdditionalHeight()); size.setHeight(size.height() + getAdditionalHeight());
size.setWidth(size.width() + m_resizer.m_gripSize * 2); size.setWidth(size.width() + m_resizer.m_gripSize * 2);
setMinimumSize(size); setMinimumSize(size);
// Restrict size of window to size of desktop
DialogPositioner::sizeToDesktop(this);
} }
void ChannelGUI::duplicateChannel() void ChannelGUI::duplicateChannel()
@ -398,24 +406,54 @@ void ChannelGUI::openMoveToDeviceSetDialog()
void ChannelGUI::maximizeWindow() void ChannelGUI::maximizeWindow()
{ {
m_disableResize = true; // If maximize is pressed when maximized, go full screen
showMaximized(); if (isMaximized())
m_disableResize = false; {
// QOpenGLWidget widgets don't always paint properly first time after being maximized, m_mdi = mdiArea();
// so force an update. Should really fix why they aren't painted properly in the first place if (m_mdi) {
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>(); m_mdi->removeSubWindow(this);
for (auto widget : widgets) { }
widget->update(); showNormal(); // If we don't go back to normal first, window doesn't get bigger
showFullScreen();
m_shrinkButton->setToolTip("Adjust window to maximum size in workspace");
}
else
{
m_disableResize = true;
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_maximizeButton->setToolTip("Make window full screen");
m_disableResize = false;
// QOpenGLWidget widgets don't always paint properly first time after being maximized,
// so force an update. Should really fix why they aren't painted properly in the first place
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
widget->update();
}
} }
} }
void ChannelGUI::shrinkWindow() void ChannelGUI::shrinkWindow()
{ {
qDebug("ChannelGUI::shrinkWindow"); qDebug("ChannelGUI::shrinkWindow");
if (isMaximized()) // If m_normalParentWidget, window was made full screen
if (m_mdi)
{ {
m_disableResize = true; m_disableResize = true;
showNormal(); showNormal();
m_mdi->addSubWindow(this);
show();
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_disableResize = false;
m_mdi = nullptr;
}
else if (isMaximized())
{
m_disableResize = true;
showNormal();
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_disableResize = false; m_disableResize = false;
} }
else else

View File

@ -141,6 +141,7 @@ private:
QMap<QWidget*, int> m_heightsMap; QMap<QWidget*, int> m_heightsMap;
FramelessWindowResizer m_resizer; FramelessWindowResizer m_resizer;
bool m_disableResize; bool m_disableResize;
QMdiArea *m_mdi; // Saved pointer to MDI when in full screen mode
private slots: private slots:
void activateSettingsDialog(); void activateSettingsDialog();

View File

@ -162,7 +162,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
m_topLayout->addWidget(m_maximizeButton); m_topLayout->addWidget(m_maximizeButton);
m_topLayout->addWidget(m_closeButton); m_topLayout->addWidget(m_closeButton);
m_centerLayout = new QHBoxLayout(); m_centerLayout = new QVBoxLayout();
m_centerLayout->setContentsMargins(0, 0, 0, 0); m_centerLayout->setContentsMargins(0, 0, 0, 0);
m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui" m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui"
m_centerLayout->addWidget(m_contents); m_centerLayout->addWidget(m_contents);
@ -412,6 +412,7 @@ void DeviceGUI::deviceSetPresetsDialog()
void DeviceGUI::setTitle(const QString& title) void DeviceGUI::setTitle(const QString& title)
{ {
setWindowTitle(title + " Device");
m_titleLabel->setText(title); m_titleLabel->setText(title);
} }

View File

@ -24,6 +24,7 @@
#include <QString> #include <QString>
#include <QByteArray> #include <QByteArray>
#include <QWidget> #include <QWidget>
#include <QLabel>
#include "gui/channeladddialog.h" #include "gui/channeladddialog.h"
#include "gui/framelesswindowresizer.h" #include "gui/framelesswindowresizer.h"
@ -32,7 +33,6 @@
class QCloseEvent; class QCloseEvent;
class Message; class Message;
class MessageQueue; class MessageQueue;
class QLabel;
class QPushButton; class QPushButton;
class QVBoxLayout; class QVBoxLayout;
class QHBoxLayout; class QHBoxLayout;
@ -87,6 +87,7 @@ protected:
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void resetContextMenuType() { m_contextMenuType = ContextMenuNone; }
int getAdditionalHeight() const { return 22 + 22; } int getAdditionalHeight() const { return 22 + 22; }
void setStatus(const QString &status) { m_statusLabel->setText(status); }
DeviceUISet* m_deviceUISet; DeviceUISet* m_deviceUISet;
DeviceType m_deviceType; DeviceType m_deviceType;
@ -122,7 +123,7 @@ private:
QLabel *m_statusLabel; QLabel *m_statusLabel;
QVBoxLayout *m_layouts; QVBoxLayout *m_layouts;
QHBoxLayout *m_topLayout; QHBoxLayout *m_topLayout;
QHBoxLayout *m_centerLayout; QVBoxLayout *m_centerLayout;
QHBoxLayout *m_bottomLayout; QHBoxLayout *m_bottomLayout;
QSizeGrip *m_sizeGripBottomRight; QSizeGrip *m_sizeGripBottomRight;
bool m_drag; bool m_drag;

View File

@ -26,6 +26,7 @@
#include <QObjectCleanupHandler> #include <QObjectCleanupHandler>
#include <QDesktopServices> #include <QDesktopServices>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QMdiArea>
#include "mainwindow.h" #include "mainwindow.h"
#include "gui/workspaceselectiondialog.h" #include "gui/workspaceselectiondialog.h"
@ -37,7 +38,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
m_contextMenuType(ContextMenuNone), m_contextMenuType(ContextMenuNone),
m_drag(false), m_drag(false),
m_resizer(this), m_resizer(this),
m_disableResize(false) m_disableResize(false),
m_mdi(nullptr)
{ {
qDebug("FeatureGUI::FeatureGUI"); qDebug("FeatureGUI::FeatureGUI");
setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@ -87,7 +89,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
m_maximizeButton->setFixedSize(20, 20); m_maximizeButton->setFixedSize(20, 20);
QIcon maximizeIcon(":/maximize.png"); QIcon maximizeIcon(":/maximize.png");
m_maximizeButton->setIcon(maximizeIcon); m_maximizeButton->setIcon(maximizeIcon);
m_maximizeButton->setToolTip("Adjust window to maximum size"); m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_closeButton = new QPushButton(); m_closeButton = new QPushButton();
m_closeButton->setFixedSize(20, 20); m_closeButton->setFixedSize(20, 20);
@ -264,7 +266,9 @@ void FeatureGUI::onWidgetRolled(QWidget *widget, bool show)
// onWidgetRolled being called twice. // onWidgetRolled being called twice.
// We need to make sure we don't save widget heights while this occurs. The // We need to make sure we don't save widget heights while this occurs. The
// window manager will take care of maximizing/restoring the window size. // window manager will take care of maximizing/restoring the window size.
if (!m_disableResize) // We do need to resize when a widget is rolled up, but we also need to avoid
// resizing when a window is maximized when first shown in tabbed layout
if (!m_disableResize && !isMaximized())
{ {
if (show) if (show)
{ {
@ -336,24 +340,53 @@ void FeatureGUI::sizeToContents()
void FeatureGUI::maximizeWindow() void FeatureGUI::maximizeWindow()
{ {
m_disableResize = true; // If maximize is pressed when maximized, go full screen
showMaximized(); if (isMaximized())
m_disableResize = false; {
// QOpenGLWidget widgets don't always paint properly first time after being maximized, m_mdi = mdiArea();
// so force an update. Should really fix why they aren't painted properly in the first place if (m_mdi) {
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>(); m_mdi->removeSubWindow(this);
for (auto widget : widgets) { }
widget->update(); showNormal(); // If we don't go back to normal first, window doesn't get bigger
showFullScreen();
m_shrinkButton->setToolTip("Adjust window to maximum size in workspace");
}
else
{
m_disableResize = true;
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_maximizeButton->setToolTip("Make window full screen");
m_disableResize = false;
// QOpenGLWidget widgets don't always paint properly first time after being maximized,
// so force an update. Should really fix why they aren't painted properly in the first place
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
widget->update();
}
} }
} }
void FeatureGUI::shrinkWindow() void FeatureGUI::shrinkWindow()
{ {
qDebug("FeatureGUI::shrinkWindow"); qDebug("FeatureGUI::shrinkWindow");
if (isMaximized()) if (m_mdi)
{ {
m_disableResize = true; m_disableResize = true;
showNormal(); showNormal();
m_mdi->addSubWindow(this);
show();
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_disableResize = false;
m_mdi = nullptr;
}
else if (isMaximized())
{
m_disableResize = true;
showNormal();
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_disableResize = false; m_disableResize = false;
} }
else else

View File

@ -110,6 +110,7 @@ private:
QMap<QWidget*, int> m_heightsMap; QMap<QWidget*, int> m_heightsMap;
FramelessWindowResizer m_resizer; FramelessWindowResizer m_resizer;
bool m_disableResize; bool m_disableResize;
QMdiArea *m_mdi; // Saved pointer to MDI when in full screen mode
private slots: private slots:
void activateSettingsDialog(); void activateSettingsDialog();

View File

@ -27,10 +27,13 @@
#include <QFrame> #include <QFrame>
#include <QDebug> #include <QDebug>
#include <QApplication> #include <QApplication>
#include <QMenu>
#include <QAction>
#include "gui/samplingdevicedialog.h" #include "gui/samplingdevicedialog.h"
#include "gui/rollupcontents.h" #include "gui/rollupcontents.h"
#include "gui/buttonswitch.h" #include "gui/buttonswitch.h"
#include "gui/crightclickenabler.h"
#include "channel/channelgui.h" #include "channel/channelgui.h"
#include "feature/featuregui.h" #include "feature/featuregui.h"
#include "device/devicegui.h" #include "device/devicegui.h"
@ -42,8 +45,10 @@
Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
QDockWidget(parent, flags), QDockWidget(parent, flags),
m_index(index), m_index(index),
m_menuButton(nullptr),
m_featureAddDialog(this), m_featureAddDialog(this),
m_stacking(false), m_stacking(false),
m_autoStack(false),
m_userChannelMinWidth(0) m_userChannelMinWidth(0)
{ {
m_mdi = new QMdiArea(this); m_mdi = new QMdiArea(this);
@ -64,6 +69,29 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_titleLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); m_titleLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }");
m_titleLabel->setText(windowTitle()); m_titleLabel->setText(windowTitle());
#ifdef ANDROID
m_menuButton = new QToolButton();
QIcon menuIcon(":/listing.png");
m_menuButton->setIcon(menuIcon);
m_menuButton->setFixedSize(20, 20);
m_menuButton->setPopupMode(QToolButton::InstantPopup);
#endif
m_configurationPresetsButton = new QPushButton();
QIcon configurationPresetsIcon(":/star.png");
m_configurationPresetsButton->setIcon(configurationPresetsIcon);
m_configurationPresetsButton->setToolTip("Configuration presets");
m_configurationPresetsButton->setFixedSize(20, 20);
m_startStopButton = new ButtonSwitch();
m_startStopButton->setCheckable(true);
updateStartStopButton(false);
m_startStopButton->setFixedSize(20, 20);
m_vline1 = new QFrame();
m_vline1->setFrameShape(QFrame::VLine);
m_vline1->setFrameShadow(QFrame::Sunken);
m_addRxDeviceButton = new QPushButton(); m_addRxDeviceButton = new QPushButton();
QIcon addRxIcon(":/rx.png"); QIcon addRxIcon(":/rx.png");
m_addRxDeviceButton->setIcon(addRxIcon); m_addRxDeviceButton->setIcon(addRxIcon);
@ -82,14 +110,9 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_addMIMODeviceButton->setToolTip("Add MIMO device"); m_addMIMODeviceButton->setToolTip("Add MIMO device");
m_addMIMODeviceButton->setFixedSize(20, 20); m_addMIMODeviceButton->setFixedSize(20, 20);
m_startStopButton = new ButtonSwitch(); m_vline2 = new QFrame();
m_startStopButton->setCheckable(true); m_vline2->setFrameShape(QFrame::VLine);
updateStartStopButton(false); m_vline2->setFrameShadow(QFrame::Sunken);
m_startStopButton->setFixedSize(20, 20);
m_vline1 = new QFrame();
m_vline1->setFrameShape(QFrame::VLine);
m_vline1->setFrameShadow(QFrame::Sunken);
m_addFeatureButton = new QPushButton(); m_addFeatureButton = new QPushButton();
QIcon addFeatureIcon(":/tool_add.png"); QIcon addFeatureIcon(":/tool_add.png");
@ -103,9 +126,9 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_featurePresetsButton->setToolTip("Feature presets"); m_featurePresetsButton->setToolTip("Feature presets");
m_featurePresetsButton->setFixedSize(20, 20); m_featurePresetsButton->setFixedSize(20, 20);
m_vline2 = new QFrame(); m_vline3 = new QFrame();
m_vline2->setFrameShape(QFrame::VLine); m_vline3->setFrameShape(QFrame::VLine);
m_vline2->setFrameShadow(QFrame::Sunken); m_vline3->setFrameShadow(QFrame::Sunken);
m_cascadeSubWindows = new QPushButton(); m_cascadeSubWindows = new QPushButton();
QIcon cascadeSubWindowsIcon(":/cascade.png"); QIcon cascadeSubWindowsIcon(":/cascade.png");
@ -119,19 +142,26 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_tileSubWindows->setToolTip("Tile sub windows"); m_tileSubWindows->setToolTip("Tile sub windows");
m_tileSubWindows->setFixedSize(20, 20); m_tileSubWindows->setFixedSize(20, 20);
m_stackSubWindows = new QPushButton("S"); m_stackVerticalSubWindows = new QPushButton();
//QIcon stackSubWindowsIcon(":/stack.png"); // FIXME QIcon stackVerticalSubWindowsIcon(":/stackvertical.png");
//m_stackSubWindows->setIcon(stackSubWindowsIcon); m_stackVerticalSubWindows->setIcon(stackVerticalSubWindowsIcon);
m_stackSubWindows->setToolTip("Stack sub windows"); m_stackVerticalSubWindows->setToolTip("Stack sub windows vertically");
m_stackSubWindows->setFixedSize(20, 20); m_stackVerticalSubWindows->setFixedSize(20, 20);
m_autoStackSubWindows = new ButtonSwitch(); m_stackSubWindows = new QPushButton();
m_autoStackSubWindows->setText("AS"); QIcon stackSubWindowsIcon(":/stackcolumns.png");
m_autoStackSubWindows->setCheckable(true); m_stackSubWindows->setIcon(stackSubWindowsIcon);
//QIcon autoStackSubWindowsIcon(":/autostack.png"); // FIXME m_stackSubWindows->setToolTip("Stack sub windows in columns. Right click to stack automatically.");
//m_autoStackSubWindows->setIcon(autoStackSubWindowsIcon); m_stackSubWindows->setFixedSize(20, 20);
m_autoStackSubWindows->setToolTip("Automatically stack sub windows"); CRightClickEnabler *stackSubWindowsRightClickEnabler = new CRightClickEnabler(m_stackSubWindows);
m_autoStackSubWindows->setFixedSize(20, 20); connect(stackSubWindowsRightClickEnabler, &CRightClickEnabler::rightClick, this, &Workspace::autoStackSubWindows);
m_tabSubWindows = new ButtonSwitch();
QIcon tabSubWindowsIcon(":/tab.png");
m_tabSubWindows->setIcon(tabSubWindowsIcon);
m_tabSubWindows->setCheckable(true);
m_tabSubWindows->setToolTip("Display sub windows in tabs");
m_tabSubWindows->setFixedSize(20, 20);
m_normalButton = new QPushButton(); m_normalButton = new QPushButton();
QIcon normalIcon(":/dock.png"); QIcon normalIcon(":/dock.png");
@ -146,21 +176,34 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_closeButton->setFixedSize(20, 20); m_closeButton->setFixedSize(20, 20);
m_titleBarLayout->addWidget(m_titleLabel); m_titleBarLayout->addWidget(m_titleLabel);
if (m_menuButton) {
m_titleBarLayout->addWidget(m_menuButton);
}
m_titleBarLayout->addWidget(m_configurationPresetsButton);
m_titleBarLayout->addWidget(m_startStopButton);
m_titleBarLayout->addWidget(m_vline1);
m_titleBarLayout->addWidget(m_addRxDeviceButton); m_titleBarLayout->addWidget(m_addRxDeviceButton);
m_titleBarLayout->addWidget(m_addTxDeviceButton); m_titleBarLayout->addWidget(m_addTxDeviceButton);
m_titleBarLayout->addWidget(m_addMIMODeviceButton); m_titleBarLayout->addWidget(m_addMIMODeviceButton);
m_titleBarLayout->addWidget(m_startStopButton); m_titleBarLayout->addWidget(m_vline2);
m_titleBarLayout->addWidget(m_vline1);
m_titleBarLayout->addWidget(m_addFeatureButton); m_titleBarLayout->addWidget(m_addFeatureButton);
m_titleBarLayout->addWidget(m_featurePresetsButton); m_titleBarLayout->addWidget(m_featurePresetsButton);
m_titleBarLayout->addWidget(m_vline2); m_titleBarLayout->addWidget(m_vline3);
m_titleBarLayout->addWidget(m_cascadeSubWindows); m_titleBarLayout->addWidget(m_cascadeSubWindows);
m_titleBarLayout->addWidget(m_tileSubWindows); m_titleBarLayout->addWidget(m_tileSubWindows);
m_titleBarLayout->addWidget(m_stackVerticalSubWindows);
m_titleBarLayout->addWidget(m_stackSubWindows); m_titleBarLayout->addWidget(m_stackSubWindows);
m_titleBarLayout->addWidget(m_autoStackSubWindows); m_titleBarLayout->addWidget(m_tabSubWindows);
m_titleBarLayout->addStretch(1); m_titleBarLayout->addStretch(1);
#ifndef ANDROID
// Can't undock on Android, as windows don't have title bars to allow them to be moved
m_titleBarLayout->addWidget(m_normalButton); m_titleBarLayout->addWidget(m_normalButton);
// Don't allow workspaces to be hidden on Android, as if all are hidden, they'll
// be no way to redisplay them, as we currently don't have a main menu bar
m_titleBarLayout->addWidget(m_closeButton); m_titleBarLayout->addWidget(m_closeButton);
#else
setFeatures(QDockWidget::NoDockWidgetFeatures);
#endif
setTitleBarWidget(m_titleBar); setTitleBarWidget(m_titleBar);
QObject::connect( QObject::connect(
@ -198,6 +241,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::featurePresetsDialog &Workspace::featurePresetsDialog
); );
QObject::connect(
m_configurationPresetsButton,
&QPushButton::clicked,
this,
&Workspace::configurationPresetsDialog
);
QObject::connect( QObject::connect(
m_cascadeSubWindows, m_cascadeSubWindows,
&QPushButton::clicked, &QPushButton::clicked,
@ -212,6 +262,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::tileSubWindows &Workspace::tileSubWindows
); );
QObject::connect(
m_stackVerticalSubWindows,
&QPushButton::clicked,
this,
&Workspace::stackVerticalSubWindows
);
QObject::connect( QObject::connect(
m_stackSubWindows, m_stackSubWindows,
&QPushButton::clicked, &QPushButton::clicked,
@ -227,10 +284,10 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
); );
QObject::connect( QObject::connect(
m_autoStackSubWindows, m_tabSubWindows,
&QPushButton::clicked, &QPushButton::clicked,
this, this,
&Workspace::autoStackSubWindows &Workspace::tabSubWindows
); );
QObject::connect( QObject::connect(
@ -256,6 +313,17 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::deviceStateChanged &Workspace::deviceStateChanged
); );
QObject::connect(
m_mdi,
&QMdiArea::subWindowActivated,
this,
&Workspace::subWindowActivated
);
#ifdef ANDROID
m_tabSubWindows->setChecked(true);
tabSubWindows();
#endif
} }
Workspace::~Workspace() Workspace::~Workspace()
@ -263,13 +331,17 @@ Workspace::~Workspace()
qDebug("Workspace::~Workspace"); qDebug("Workspace::~Workspace");
delete m_closeButton; delete m_closeButton;
delete m_normalButton; delete m_normalButton;
delete m_autoStackSubWindows; delete m_tabSubWindows;
delete m_stackSubWindows; delete m_stackSubWindows;
delete m_stackVerticalSubWindows;
delete m_tileSubWindows; delete m_tileSubWindows;
delete m_cascadeSubWindows; delete m_cascadeSubWindows;
delete m_vline3;
delete m_vline2; delete m_vline2;
delete m_vline1; delete m_vline1;
delete m_startStopButton; delete m_startStopButton;
delete m_configurationPresetsButton;
delete m_menuButton;
delete m_addRxDeviceButton; delete m_addRxDeviceButton;
delete m_addTxDeviceButton; delete m_addTxDeviceButton;
delete m_addMIMODeviceButton; delete m_addMIMODeviceButton;
@ -346,18 +418,126 @@ void Workspace::featurePresetsDialog()
emit featurePresetsDialogRequested(p, this); emit featurePresetsDialogRequested(p, this);
} }
void Workspace::configurationPresetsDialog()
{
emit configurationPresetsDialogRequested();
}
void Workspace::cascadeSubWindows() void Workspace::cascadeSubWindows()
{ {
m_autoStackSubWindows->setChecked(false); setAutoStackOption(false);
m_tabSubWindows->setChecked(false);
m_mdi->setViewMode(QMdiArea::SubWindowView);
m_mdi->cascadeSubWindows(); m_mdi->cascadeSubWindows();
} }
void Workspace::tileSubWindows() void Workspace::tileSubWindows()
{ {
m_autoStackSubWindows->setChecked(false); setAutoStackOption(false);
m_tabSubWindows->setChecked(false);
m_mdi->setViewMode(QMdiArea::SubWindowView);
m_mdi->tileSubWindows(); m_mdi->tileSubWindows();
} }
void Workspace::stackVerticalSubWindows()
{
setAutoStackOption(false);
unmaximizeSubWindows();
m_mdi->setViewMode(QMdiArea::SubWindowView);
// Spacing between windows
const int spacing = 2;
// Categorise windows according to type and calculate min size needed
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
QList<DeviceGUI *> devices;
QList<MainSpectrumGUI *> spectrums;
QList<ChannelGUI *> channels;
QList<FeatureGUI *> features;
int minHeight = 0;
int minWidth = 0;
int nonFixedWindows = 0;
for (auto window : windows)
{
if (window->isVisible() && !window->isMaximized())
{
if (window->inherits("DeviceGUI")) {
devices.append(qobject_cast<DeviceGUI *>(window));
} else if (window->inherits("MainSpectrumGUI")) {
spectrums.append(qobject_cast<MainSpectrumGUI *>(window));
} else if (window->inherits("ChannelGUI")) {
channels.append(qobject_cast<ChannelGUI *>(window));
} else if (window->inherits("FeatureGUI")) {
features.append(qobject_cast<FeatureGUI *>(window));
}
minHeight += window->minimumSizeHint().height() + spacing;
minWidth = std::max(minWidth, window->minimumSizeHint().width());
if (window->sizePolicy().verticalPolicy() != QSizePolicy::Fixed) {
nonFixedWindows++;
}
}
}
// Order windows by device/feature/channel index
orderByIndex(devices);
orderByIndex(spectrums);
orderByIndex(channels);
orderByIndex(features);
// Will we need scroll bars?
QSize mdiSize = m_mdi->size();
bool requiresHScrollBar = minWidth > mdiSize.width();
bool requiresVScrollBar = minHeight > mdiSize.height();
// Reduce available size if scroll bars needed
int sbWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
if (requiresVScrollBar) {
mdiSize.setWidth(mdiSize.width() - sbWidth);
}
if (requiresHScrollBar) {
mdiSize.setHeight(mdiSize.height() - sbWidth);
}
// Calculate spare vertical space, to be shared between non-fixed windows
int spareSpacePerWindow;
if (requiresVScrollBar) {
spareSpacePerWindow = 0;
} else {
spareSpacePerWindow = (mdiSize.height() - minHeight) / nonFixedWindows;
}
// Now position the windows
int x = 0;
int y = 0;
for (auto window : devices)
{
window->move(x, y);
y += window->size().height() + spacing;
}
for (auto window : spectrums)
{
window->move(x, y);
window->resize(mdiSize.width(), window->minimumSizeHint().height() + spareSpacePerWindow);
y += window->size().height() + spacing;
}
for (auto window : channels)
{
window->move(x, y);
int extra = (window->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) ? 0 : spareSpacePerWindow;
window->resize(mdiSize.width(), window->minimumSizeHint().height() + extra);
y += window->size().height() + spacing;
}
for (auto window : features)
{
window->move(x, y);
int extra = (window->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) ? 0 : spareSpacePerWindow;
window->resize(mdiSize.width(), window->minimumSizeHint().height() + extra);
y += window->size().height() + spacing;
}
}
void Workspace::orderByIndex(QList<ChannelGUI *> &list) void Workspace::orderByIndex(QList<ChannelGUI *> &list)
{ {
std::sort(list.begin(), list.end(), std::sort(list.begin(), list.end(),
@ -398,16 +578,36 @@ void Workspace::orderByIndex(QList<MainSpectrumGUI *> &list)
}); });
} }
void Workspace::unmaximizeSubWindows()
{
if (m_tabSubWindows->isChecked())
{
m_tabSubWindows->setChecked(false);
// Unmaximize any maximized windows
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if (window->isMaximized()) {
window->showNormal();
}
}
}
}
// Try to arrange windows somewhat like in earlier versions of SDRangel // Try to arrange windows somewhat like in earlier versions of SDRangel
// Devices and fixed size features stacked on left // Devices and fixed size features stacked on left
// Spectrum and expandable features stacked in centre // Spectrum and expandable features stacked in centre
// Channels stacked on right // Channels stacked on right
void Workspace::stackSubWindows() void Workspace::stackSubWindows()
{ {
unmaximizeSubWindows();
// Set a flag so event handler knows if it's this code or the user that // Set a flag so event handler knows if it's this code or the user that
// resizes a window // resizes a window
m_stacking = true; m_stacking = true;
m_mdi->setViewMode(QMdiArea::SubWindowView);
// Categorise windows according to type // Categorise windows according to type
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder); QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
QList<DeviceGUI *> devices; QList<DeviceGUI *> devices;
@ -653,7 +853,54 @@ void Workspace::stackSubWindows()
void Workspace::autoStackSubWindows() void Workspace::autoStackSubWindows()
{ {
if (m_autoStackSubWindows->isChecked()) { setAutoStackOption(!m_autoStack);
}
void Workspace::tabSubWindows()
{
if (m_tabSubWindows->isChecked())
{
// Disable autostack
setAutoStackOption(false);
// Move sub windows out of view, so they can't be seen next to a non-expandible window
// Perhaps there's a better way to do this - showMinimized didn't work
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if ((window != m_mdi->activeSubWindow()) && ((window->x() != 5000) || (window->y() != 0))) {
window->move(5000, 0);
}
}
m_mdi->setViewMode(QMdiArea::TabbedView);
}
else
{
m_mdi->setViewMode(QMdiArea::SubWindowView);
}
}
void Workspace::subWindowActivated(QMdiSubWindow *activatedWindow)
{
if (activatedWindow && m_tabSubWindows->isChecked())
{
// Move other windows out of the way
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if ((window != activatedWindow) && ((window->x() != 5000) || (window->y() != 0))) {
window->move(5000, 0);
} else if ((window == activatedWindow) && ((window->x() != 0) || (window->y() != 0))) {
window->move(0, 0);
}
}
}
}
void Workspace::layoutSubWindows()
{
if (m_autoStack) {
stackSubWindows(); stackSubWindows();
} }
} }
@ -716,7 +963,7 @@ void Workspace::deviceStateChanged(int index, DeviceAPI *deviceAPI)
void Workspace::resizeEvent(QResizeEvent *event) void Workspace::resizeEvent(QResizeEvent *event)
{ {
QDockWidget::resizeEvent(event); QDockWidget::resizeEvent(event);
autoStackSubWindows(); layoutSubWindows();
} }
void Workspace::addToMdiArea(QMdiSubWindow *sub) void Workspace::addToMdiArea(QMdiSubWindow *sub)
@ -725,17 +972,20 @@ void Workspace::addToMdiArea(QMdiSubWindow *sub)
sub->installEventFilter(this); sub->installEventFilter(this);
// Can't use Close event, as it's before window is closed, so // Can't use Close event, as it's before window is closed, so
// catch sub-window destroyed signal instead // catch sub-window destroyed signal instead
connect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows); connect(sub, &QObject::destroyed, this, &Workspace::layoutSubWindows);
m_mdi->addSubWindow(sub); m_mdi->addSubWindow(sub);
sub->show(); sub->show();
// Auto-stack when sub-window's widgets are rolled up // Auto-stack when sub-window's widgets are rolled up
ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub); ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub);
if (channel) { if (channel) {
connect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); connect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
} }
FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub); FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub);
if (feature) { if (feature) {
connect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); connect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
}
if (m_tabSubWindows->isChecked()) {
sub->showMaximized();
} }
} }
@ -743,14 +993,14 @@ void Workspace::removeFromMdiArea(QMdiSubWindow *sub)
{ {
m_mdi->removeSubWindow(sub); m_mdi->removeSubWindow(sub);
sub->removeEventFilter(this); sub->removeEventFilter(this);
disconnect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows); disconnect(sub, &QObject::destroyed, this, &Workspace::layoutSubWindows);
ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub); ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub);
if (channel) { if (channel) {
disconnect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); disconnect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
} }
FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub); FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub);
if (feature) { if (feature) {
disconnect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); disconnect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
} }
} }
@ -760,19 +1010,19 @@ bool Workspace::eventFilter(QObject *obj, QEvent *event)
{ {
QWidget *widget = qobject_cast<QWidget *>(obj); QWidget *widget = qobject_cast<QWidget *>(obj);
if (!widget->isMaximized()) { if (!widget->isMaximized()) {
autoStackSubWindows(); layoutSubWindows();
} }
} }
else if (event->type() == QEvent::Hide) else if (event->type() == QEvent::Hide)
{ {
QWidget *widget = qobject_cast<QWidget *>(obj); QWidget *widget = qobject_cast<QWidget *>(obj);
if (!widget->isMaximized()) { if (!widget->isMaximized()) {
autoStackSubWindows(); layoutSubWindows();
} }
} }
else if (event->type() == QEvent::Resize) else if (event->type() == QEvent::Resize)
{ {
if (!m_stacking && m_autoStackSubWindows->isChecked()) if (!m_stacking && m_autoStack)
{ {
QWidget *widget = qobject_cast<QWidget *>(obj); QWidget *widget = qobject_cast<QWidget *>(obj);
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event); QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
@ -812,12 +1062,38 @@ void Workspace::restoreMdiGeometry(const QByteArray& blob)
bool Workspace::getAutoStackOption() const bool Workspace::getAutoStackOption() const
{ {
return m_autoStackSubWindows->isChecked(); return m_autoStack;
} }
void Workspace::setAutoStackOption(bool autoStack) void Workspace::setAutoStackOption(bool autoStack)
{ {
m_autoStackSubWindows->doToggle(autoStack); m_autoStack = autoStack;
if (!m_autoStack)
{
m_stackSubWindows->setStyleSheet(QString("QPushButton{ background-color: %1; }")
.arg(palette().button().color().name()));
}
else
{
m_stackSubWindows->setStyleSheet(QString("QPushButton{ background-color: %1; }")
.arg(palette().highlight().color().darker(150).name()));
stackSubWindows();
}
}
bool Workspace::getTabSubWindowsOption() const
{
return m_tabSubWindows->isChecked();
}
void Workspace::setTabSubWindowsOption(bool tab)
{
m_tabSubWindows->doToggle(tab);
if (tab) {
tabSubWindows();
} else {
m_mdi->setViewMode(QMdiArea::SubWindowView);
}
} }
void Workspace::adjustSubWindowsAfterRestore() void Workspace::adjustSubWindowsAfterRestore()

View File

@ -28,6 +28,7 @@
class QHBoxLayout; class QHBoxLayout;
class QLabel; class QLabel;
class QToolButton;
class QPushButton; class QPushButton;
class QMdiArea; class QMdiArea;
class QMdiSubWindow; class QMdiSubWindow;
@ -37,7 +38,6 @@ class ChannelGUI;
class FeatureGUI; class FeatureGUI;
class DeviceGUI; class DeviceGUI;
class MainSpectrumGUI; class MainSpectrumGUI;
class SDRGUI_API Workspace : public QDockWidget class SDRGUI_API Workspace : public QDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -56,6 +56,8 @@ public:
void restoreMdiGeometry(const QByteArray& blob); void restoreMdiGeometry(const QByteArray& blob);
bool getAutoStackOption() const; bool getAutoStackOption() const;
void setAutoStackOption(bool autoStack); void setAutoStackOption(bool autoStack);
bool getTabSubWindowsOption() const;
void setTabSubWindowsOption(bool tab);
QList<QMdiSubWindow *> getSubWindowList() const; QList<QMdiSubWindow *> getSubWindowList() const;
void orderByIndex(QList<ChannelGUI *> &list); void orderByIndex(QList<ChannelGUI *> &list);
void orderByIndex(QList<FeatureGUI *> &list); void orderByIndex(QList<FeatureGUI *> &list);
@ -63,21 +65,26 @@ public:
void orderByIndex(QList<MainSpectrumGUI *> &list); void orderByIndex(QList<MainSpectrumGUI *> &list);
void adjustSubWindowsAfterRestore(); void adjustSubWindowsAfterRestore();
void updateStartStopButton(bool checked); void updateStartStopButton(bool checked);
QToolButton *getMenuButton() const { return m_menuButton; }
private: private:
int m_index; int m_index;
QToolButton *m_menuButton;
QPushButton *m_configurationPresetsButton;
ButtonSwitch *m_startStopButton;
QFrame *m_vline1;
QPushButton *m_addRxDeviceButton; QPushButton *m_addRxDeviceButton;
QPushButton *m_addTxDeviceButton; QPushButton *m_addTxDeviceButton;
QPushButton *m_addMIMODeviceButton; QPushButton *m_addMIMODeviceButton;
ButtonSwitch *m_startStopButton; QFrame *m_vline2;
QFrame *m_vline1;
QPushButton *m_addFeatureButton; QPushButton *m_addFeatureButton;
QPushButton *m_featurePresetsButton; QPushButton *m_featurePresetsButton;
QFrame *m_vline2; QFrame *m_vline3;
QPushButton *m_cascadeSubWindows; QPushButton *m_cascadeSubWindows;
QPushButton *m_tileSubWindows; QPushButton *m_tileSubWindows;
QPushButton *m_stackVerticalSubWindows;
QPushButton *m_stackSubWindows; QPushButton *m_stackSubWindows;
ButtonSwitch *m_autoStackSubWindows; ButtonSwitch *m_tabSubWindows;
QWidget *m_titleBar; QWidget *m_titleBar;
QHBoxLayout *m_titleBarLayout; QHBoxLayout *m_titleBarLayout;
QLabel *m_titleLabel; QLabel *m_titleLabel;
@ -86,8 +93,11 @@ private:
FeatureAddDialog m_featureAddDialog; FeatureAddDialog m_featureAddDialog;
QMdiArea *m_mdi; QMdiArea *m_mdi;
bool m_stacking; // Set when stackSubWindows() is running bool m_stacking; // Set when stackSubWindows() is running
bool m_autoStack; // Automatically stack
int m_userChannelMinWidth; // Minimum width of channels column for stackSubWindows(), set by user resizing a channel window int m_userChannelMinWidth; // Minimum width of channels column for stackSubWindows(), set by user resizing a channel window
void unmaximizeSubWindows();
protected: protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
@ -98,14 +108,19 @@ private slots:
void addMIMODeviceClicked(); void addMIMODeviceClicked();
void addFeatureDialog(); void addFeatureDialog();
void featurePresetsDialog(); void featurePresetsDialog();
void configurationPresetsDialog();
void cascadeSubWindows(); void cascadeSubWindows();
void tileSubWindows(); void tileSubWindows();
void stackVerticalSubWindows();
void stackSubWindows(); void stackSubWindows();
void autoStackSubWindows(); void autoStackSubWindows();
void tabSubWindows();
void layoutSubWindows();
void startStopClicked(bool checked = false); void startStopClicked(bool checked = false);
void addFeatureEmitted(int featureIndex); void addFeatureEmitted(int featureIndex);
void toggleFloating(); void toggleFloating();
void deviceStateChanged(int index, DeviceAPI *deviceAPI); void deviceStateChanged(int index, DeviceAPI *deviceAPI);
void subWindowActivated(QMdiSubWindow *window);
signals: signals:
void addRxDevice(Workspace *inWorkspace, int deviceIndex); void addRxDevice(Workspace *inWorkspace, int deviceIndex);
@ -113,6 +128,7 @@ signals:
void addMIMODevice(Workspace *inWorkspace, int deviceIndex); void addMIMODevice(Workspace *inWorkspace, int deviceIndex);
void addFeature(Workspace*, int); void addFeature(Workspace*, int);
void featurePresetsDialogRequested(QPoint, Workspace*); void featurePresetsDialogRequested(QPoint, Workspace*);
void configurationPresetsDialogRequested();
void startAllDevices(Workspace *inWorkspace); void startAllDevices(Workspace *inWorkspace);
void stopAllDevices(Workspace *inWorkspace); void stopAllDevices(Workspace *inWorkspace);
}; };