///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 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 //
// (at your option) any later version. //
// //
// 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mainwindow.h"
#include "gui/workspaceselectiondialog.h"
#include "gui/samplingdevicedialog.h"
#include "device/deviceuiset.h"
#include "device/deviceapi.h"
#include "devicegui.h"
DeviceGUI::DeviceGUI(QWidget *parent) :
QMdiSubWindow(parent),
m_deviceUISet(nullptr),
m_deviceType(DeviceRx),
m_deviceSetIndex(0),
m_contextMenuType(ContextMenuNone),
m_drag(false),
m_currentDeviceIndex(-1),
m_resizer(this)
{
qDebug("DeviceGUI::DeviceGUI: %p", parent);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
setObjectName("DeviceGUI");
setStyleSheet(QString(tr("#DeviceGUI { border: 1px solid %1; background-color: %2; }")
.arg(palette().highlight().color().darker(115).name()))
.arg(palette().dark().color().darker(115).name()));
m_indexLabel = new QLabel();
m_indexLabel->setFixedSize(32, 16);
m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }");
m_indexLabel->setText(tr("X:%1").arg(m_deviceSetIndex));
m_indexLabel->setToolTip("Device type and set index");
m_settingsButton = new QPushButton();
m_settingsButton->setFixedSize(20, 20);
QIcon settingsIcon(":/gear.png");
m_settingsButton->setIcon(settingsIcon);
m_settingsButton->setToolTip("Common settings");
m_changeDeviceButton = new QPushButton();
m_changeDeviceButton->setFixedSize(20, 20);
QIcon changeDeviceIcon(":/swap.png");
m_changeDeviceButton->setIcon(changeDeviceIcon);
m_changeDeviceButton->setToolTip("Change device");
m_reloadDeviceButton = new QPushButton();
m_reloadDeviceButton->setFixedSize(20, 20);
QIcon reloadDeviceIcon(":/recycle.png");
m_reloadDeviceButton->setIcon(reloadDeviceIcon);
m_reloadDeviceButton->setToolTip("Reload device");
m_addChannelsButton = new QPushButton();
m_addChannelsButton->setFixedSize(20, 20);
QIcon addChannelsIcon(":/channels_add.png");
m_addChannelsButton->setIcon(addChannelsIcon);
m_addChannelsButton->setToolTip("Add channels");
m_deviceSetPresetsButton = new QPushButton();
m_deviceSetPresetsButton->setFixedSize(20, 20);
QIcon deviceSetPresetsIcon(":/star.png");
m_deviceSetPresetsButton->setIcon(deviceSetPresetsIcon);
m_deviceSetPresetsButton->setToolTip("Device set presets");
m_titleLabel = new QLabel();
m_titleLabel->setText("Device");
m_titleLabel->setToolTip("Device identification");
m_titleLabel->setFixedHeight(20);
m_titleLabel->setMinimumWidth(20);
m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
m_helpButton = new QPushButton();
m_helpButton->setFixedSize(20, 20);
QIcon helpIcon(":/help.png");
m_helpButton->setIcon(helpIcon);
m_helpButton->setToolTip("Show device documentation in browser");
m_moveButton = new QPushButton();
m_moveButton->setFixedSize(20, 20);
QIcon moveIcon(":/exit.png");
m_moveButton->setIcon(moveIcon);
m_moveButton->setToolTip("Move to another workspace");
m_shrinkButton = new QPushButton();
m_shrinkButton->setFixedSize(20, 20);
QIcon shrinkIcon(":/shrink.png");
m_shrinkButton->setIcon(shrinkIcon);
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton = new QPushButton();
m_maximizeButton->setFixedSize(20, 20);
QIcon maximizeIcon(":/maximize.png");
m_maximizeButton->setIcon(maximizeIcon);
m_maximizeButton->setToolTip("Adjust window to maximum size");
m_closeButton = new QPushButton();
m_closeButton->setFixedSize(20, 20);
QIcon closeIcon(":/cross.png");
m_closeButton->setIcon(closeIcon);
m_closeButton->setToolTip("Close device");
m_statusLabel = new QLabel();
// m_statusLabel->setText("OK"); // for future use
m_statusLabel->setFixedHeight(20);
m_statusLabel->setMinimumWidth(20);
m_statusLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
m_statusLabel->setToolTip("Device status");
m_showSpectrumButton = new QPushButton();
m_showSpectrumButton->setFixedSize(20, 20);
QIcon showSpectrumIcon(":/dsb.png");
m_showSpectrumButton->setIcon(showSpectrumIcon);
m_showSpectrumButton->setToolTip("Show main spectrum");
m_showAllChannelsButton = new QPushButton();
m_showAllChannelsButton->setFixedSize(20, 20);
QIcon showAllChannelsIcon(":/channels.png");
m_showAllChannelsButton->setIcon(showAllChannelsIcon);
m_showAllChannelsButton->setToolTip("Show all channels");
m_layouts = new QVBoxLayout();
m_layouts->setContentsMargins(m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize);
m_layouts->setSpacing(0);
m_topLayout = new QHBoxLayout();
m_topLayout->setContentsMargins(0, 0, 0, 0);
m_topLayout->addWidget(m_indexLabel);
m_topLayout->addWidget(m_settingsButton);
m_topLayout->addWidget(m_changeDeviceButton);
m_topLayout->addWidget(m_reloadDeviceButton);
m_topLayout->addWidget(m_deviceSetPresetsButton);
m_topLayout->addWidget(m_addChannelsButton);
m_topLayout->addWidget(m_titleLabel);
// m_topLayout->addStretch(1);
m_topLayout->addWidget(m_helpButton);
m_topLayout->addWidget(m_moveButton);
m_topLayout->addWidget(m_shrinkButton);
m_topLayout->addWidget(m_maximizeButton);
m_topLayout->addWidget(m_closeButton);
m_centerLayout = new QVBoxLayout();
m_centerLayout->setContentsMargins(0, 0, 0, 0);
m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui"
m_centerLayout->addWidget(m_contents);
m_bottomLayout = new QHBoxLayout();
m_bottomLayout->setContentsMargins(0, 0, 0, 0);
m_bottomLayout->addWidget(m_showSpectrumButton);
m_bottomLayout->addWidget(m_showAllChannelsButton);
m_bottomLayout->addWidget(m_statusLabel);
m_sizeGripBottomRight = new QSizeGrip(this);
m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// m_bottomLayout->addStretch(1);
m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight);
m_layouts->addLayout(m_topLayout);
m_layouts->addLayout(m_centerLayout);
m_layouts->addLayout(m_bottomLayout);
QObjectCleanupHandler().add(layout());
setLayout(m_layouts);
connect(m_settingsButton, SIGNAL(clicked()), this, SLOT(activateSettingsDialog()));
connect(m_changeDeviceButton, SIGNAL(clicked()), this, SLOT(openChangeDeviceDialog()));
connect(m_reloadDeviceButton, SIGNAL(clicked()), this, SLOT(deviceReload()));
connect(m_addChannelsButton, SIGNAL(clicked()), this, SLOT(openAddChannelsDialog()));
connect(m_deviceSetPresetsButton, SIGNAL(clicked()), this, SLOT(deviceSetPresetsDialog()));
connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp()));
connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog()));
connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow()));
connect(m_maximizeButton, SIGNAL(clicked()), this, SLOT(maximizeWindow()));
connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow()));
connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close()));
connect(m_showSpectrumButton, SIGNAL(clicked()), this, SLOT(showSpectrumHandler()));
connect(m_showAllChannelsButton, SIGNAL(clicked()), this, SLOT(showAllChannelsHandler()));
QObject::connect(
&m_channelAddDialog,
&ChannelAddDialog::addChannel,
this,
&DeviceGUI::addChannelEmitted
);
m_resizer.enableChildMouseTracking();
}
DeviceGUI::~DeviceGUI()
{
qDebug("DeviceGUI::~DeviceGUI");
delete m_sizeGripBottomRight;
delete m_bottomLayout;
delete m_centerLayout;
delete m_topLayout;
delete m_layouts;
delete m_showAllChannelsButton;
delete m_showSpectrumButton;
delete m_statusLabel;
delete m_closeButton;
delete m_shrinkButton;
delete m_maximizeButton;
delete m_moveButton;
delete m_helpButton;
delete m_titleLabel;
delete m_deviceSetPresetsButton;
delete m_addChannelsButton;
delete m_reloadDeviceButton;
delete m_changeDeviceButton;
delete m_settingsButton;
delete m_indexLabel;
qDebug("DeviceGUI::~DeviceGUI: end");
}
// Size the window according to the size of the m_contents widget
// This allows the window min/max size and size policy to be set by the .ui file
void DeviceGUI::sizeToContents()
{
// Set window size policy to the size policy of m_contents widget
QSizePolicy policy = getContents()->sizePolicy();
setSizePolicy(policy);
// If size policy is fixed, hide widgets that resize the window
if ((policy.verticalPolicy() == QSizePolicy::Fixed) && (policy.horizontalPolicy() == QSizePolicy::Fixed))
{
m_shrinkButton->hide();
m_maximizeButton->hide();
// The resize grip can magically reappear after being maximized, so delete it, to prevent this
delete m_sizeGripBottomRight;
m_sizeGripBottomRight = nullptr;
}
// Calculate min/max size for window. This is min/max size of contents, plus
// extra needed for window frame and title bar
QSize size;
size = getContents()->maximumSize();
size.setHeight(std::min(size.height() + getAdditionalHeight(), QWIDGETSIZE_MAX));
size.setWidth(std::min(size.width() + m_resizer.m_gripSize * 2, QWIDGETSIZE_MAX));
setMaximumSize(size);
size = getContents()->minimumSize();
size.setHeight(std::min(size.height() + getAdditionalHeight(), QWIDGETSIZE_MAX));
size.setWidth(std::min(size.width() + m_resizer.m_gripSize * 2, QWIDGETSIZE_MAX));
setMinimumSize(size);
// Adjust to minimum size needed for widgets
adjustSize();
}
void DeviceGUI::setWorkspaceIndex(int index)
{
m_workspaceIndex = index;
if (m_deviceUISet) {
m_deviceUISet->m_deviceAPI->setWorkspaceIndex(index);
}
}
void DeviceGUI::closeEvent(QCloseEvent *event)
{
qDebug("DeviceGUI::closeEvent");
emit closing();
event->accept();
}
void DeviceGUI::mousePressEvent(QMouseEvent* event)
{
if ((event->button() == Qt::LeftButton) && isOnMovingPad())
{
m_drag = true;
m_DragPosition = event->globalPos() - pos();
event->accept();
}
else
{
m_resizer.mousePressEvent(event);
}
}
void DeviceGUI::mouseReleaseEvent(QMouseEvent* event)
{
m_resizer.mouseReleaseEvent(event);
}
void DeviceGUI::mouseMoveEvent(QMouseEvent* event)
{
if ((event->buttons() & Qt::LeftButton) && isOnMovingPad())
{
move(event->globalPos() - m_DragPosition);
event->accept();
}
else
{
m_resizer.mouseMoveEvent(event);
}
}
void DeviceGUI::leaveEvent(QEvent* event)
{
m_resizer.leaveEvent(event);
QMdiSubWindow::leaveEvent(event);
}
void DeviceGUI::openChangeDeviceDialog()
{
SamplingDeviceDialog dialog((int) m_deviceType, this);
if (dialog.exec() == QDialog::Accepted)
{
m_currentDeviceIndex = dialog.getSelectedDeviceIndex();
dialog.setParent(nullptr);
emit deviceChange(m_currentDeviceIndex);
}
}
void DeviceGUI::deviceReload()
{
if (m_currentDeviceIndex >= 0) {
emit deviceChange(m_currentDeviceIndex);
}
}
void DeviceGUI::activateSettingsDialog()
{
QPoint p = QCursor::pos();
m_contextMenuType = ContextMenuDeviceSettings;
emit customContextMenuRequested(p);
}
void DeviceGUI::showHelp()
{
if (m_helpURL.isEmpty()) {
return;
}
QString url;
if (m_helpURL.startsWith("http")) {
url = m_helpURL;
} else {
url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md"
}
QDesktopServices::openUrl(QUrl(url));
}
void DeviceGUI::openMoveToWorkspaceDialog()
{
int numberOfWorkspaces = MainWindow::getInstance()->getNumberOfWorkspaces();
WorkspaceSelectionDialog dialog(numberOfWorkspaces, this);
dialog.exec();
if (dialog.hasChanged()) {
emit moveToWorkspace(dialog.getSelectedIndex());
}
}
void DeviceGUI::openAddChannelsDialog()
{
m_channelAddDialog.exec();
}
void DeviceGUI::showSpectrumHandler()
{
emit showSpectrum(m_deviceSetIndex);
}
void DeviceGUI::showAllChannelsHandler()
{
emit showAllChannels(m_deviceSetIndex);
}
void DeviceGUI::shrinkWindow()
{
qDebug("DeviceGUI::shrinkWindow");
showNormal(); // In case it had been maximized
adjustSize();
}
void DeviceGUI::maximizeWindow()
{
showMaximized();
}
void DeviceGUI::deviceSetPresetsDialog()
{
QPoint p = mapFromGlobal(QCursor::pos());
emit deviceSetPresetsDialogRequested(p, this);
}
void DeviceGUI::setTitle(const QString& title)
{
setWindowTitle(title + " Device");
m_titleLabel->setText(title);
}
QString DeviceGUI::getTitle() const
{
return m_titleLabel->text();
}
bool DeviceGUI::isOnMovingPad()
{
return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusLabel->underMouse();
}
void DeviceGUI::setIndex(int index)
{
m_deviceSetIndex = index;
m_indexLabel->setText(tr("%1:%2").arg(getDeviceTypeTag()).arg(m_deviceSetIndex));
}
void DeviceGUI::setDeviceType(DeviceType type)
{
m_deviceType = type;
m_indexLabel->setStyleSheet(tr("QLabel { background-color: %1; qproperty-alignment: AlignCenter; }").arg(getDeviceTypeColor()));
}
void DeviceGUI::setToolTip(const QString& tooltip)
{
m_titleLabel->setToolTip(tooltip);
}
QString DeviceGUI::getDeviceTypeColor()
{
switch(m_deviceType)
{
case DeviceRx:
return "rgb(0, 128, 0)";
case DeviceTx:
return "rgb(204, 0, 0)";
case DeviceMIMO:
return "rgb(0, 0, 192)";
default:
return "rgb(128, 128, 128)";
}
}
QString DeviceGUI::getDeviceTypeTag()
{
switch(m_deviceType)
{
case DeviceRx:
return "R";
case DeviceTx:
return "T";
case DeviceMIMO:
return "M";
default:
return "X";
}
}