2026-04-19 10:41:30 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Copyright (C) 2026 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
|
|
|
// Some code by Copilot //
|
|
|
|
|
// //
|
|
|
|
|
// 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 <http://www.gnu.org/licenses/>. //
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2026-04-19 10:34:01 +00:00
|
|
|
#include <QColorDialog>
|
2026-04-19 10:09:10 +00:00
|
|
|
#include <QContextMenuEvent>
|
2026-04-18 15:13:46 +00:00
|
|
|
#include <QFont>
|
2026-04-19 10:09:10 +00:00
|
|
|
#include <QLabel>
|
2026-04-18 15:15:03 +00:00
|
|
|
#include <QLocale>
|
2026-04-19 10:09:10 +00:00
|
|
|
#include <QMenu>
|
2026-04-19 10:34:01 +00:00
|
|
|
#include <QPixmap>
|
|
|
|
|
#include <QPushButton>
|
2026-04-18 15:13:46 +00:00
|
|
|
#include <QResizeEvent>
|
2026-04-19 09:28:13 +00:00
|
|
|
#include <QSpinBox>
|
2026-04-18 20:54:57 +00:00
|
|
|
#include <QTimer>
|
2026-04-19 10:09:10 +00:00
|
|
|
#include <QVBoxLayout>
|
2026-04-18 15:13:46 +00:00
|
|
|
|
|
|
|
|
#include "channel/channelwebapiutils.h"
|
2026-04-18 16:28:15 +00:00
|
|
|
#include "gui/buttonswitch.h"
|
2026-04-18 15:13:46 +00:00
|
|
|
|
|
|
|
|
#include "feature/featureuiset.h"
|
|
|
|
|
|
|
|
|
|
#include "ui_freqdisplaygui.h"
|
|
|
|
|
#include "freqdisplay.h"
|
|
|
|
|
#include "freqdisplaygui.h"
|
|
|
|
|
|
2026-04-18 15:15:43 +00:00
|
|
|
namespace {
|
2026-04-18 15:19:24 +00:00
|
|
|
constexpr const char* rxTxChannelKinds = "RT";
|
2026-04-18 15:18:12 +00:00
|
|
|
constexpr int pollIntervalMs = 1000;
|
|
|
|
|
constexpr int minimumFrequencyFontPointSize = 10;
|
2026-04-18 17:01:41 +00:00
|
|
|
// Reference point size used when probing text metrics in updateFrequencyFont().
|
|
|
|
|
// Large enough that integer rounding in QFontMetrics is negligible.
|
|
|
|
|
constexpr int fontProbePointSize = 200;
|
2026-04-19 09:28:13 +00:00
|
|
|
constexpr double kHzDivisor = 1e3;
|
|
|
|
|
constexpr double MHzDivisor = 1e6;
|
|
|
|
|
constexpr double GHzDivisor = 1e9;
|
2026-04-19 09:16:40 +00:00
|
|
|
|
|
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
// Expand display-text unit abbreviations to full spoken words so that TTS
|
|
|
|
|
// engines read them naturally rather than letter-by-letter.
|
|
|
|
|
QString textForSpeech(const QString& displayText)
|
|
|
|
|
{
|
|
|
|
|
QString s = displayText;
|
|
|
|
|
// Order matters: longer unit strings must be replaced before shorter ones
|
|
|
|
|
// that are sub-strings of them (e.g. "GHz" before "Hz").
|
|
|
|
|
s.replace(QLatin1String(" GHz"), QLatin1String(" gigahertz"));
|
|
|
|
|
s.replace(QLatin1String(" MHz"), QLatin1String(" megahertz"));
|
|
|
|
|
s.replace(QLatin1String(" kHz"), QLatin1String(" kilohertz"));
|
|
|
|
|
s.replace(QLatin1String(" Hz"), QLatin1String(" hertz"));
|
|
|
|
|
s.replace(QLatin1String(" dB"), QLatin1String(" decibels"));
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2026-04-19 10:09:10 +00:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
// --- FreqDisplayOverlay ---------------------------------------------------
|
|
|
|
|
|
|
|
|
|
FreqDisplayOverlay::FreqDisplayOverlay(QWidget* parent)
|
|
|
|
|
: QWidget(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool),
|
|
|
|
|
m_dragging(false)
|
|
|
|
|
{
|
|
|
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, false);
|
|
|
|
|
|
|
|
|
|
m_label = new QLabel(this);
|
|
|
|
|
m_label->setAlignment(Qt::AlignCenter);
|
|
|
|
|
m_label->setWordWrap(true);
|
|
|
|
|
|
|
|
|
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
layout->addWidget(m_label);
|
|
|
|
|
setLayout(layout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayOverlay::contextMenuEvent(QContextMenuEvent* event)
|
|
|
|
|
{
|
|
|
|
|
QMenu menu(this);
|
|
|
|
|
QAction* exitAction = menu.addAction(tr("Exit transparent mode"));
|
|
|
|
|
if (menu.exec(event->globalPos()) == exitAction) {
|
|
|
|
|
emit exitTransparentMode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayOverlay::resizeEvent(QResizeEvent* event)
|
|
|
|
|
{
|
|
|
|
|
QWidget::resizeEvent(event);
|
|
|
|
|
emit resized();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayOverlay::mousePressEvent(QMouseEvent* event)
|
|
|
|
|
{
|
|
|
|
|
if (event->button() == Qt::LeftButton)
|
|
|
|
|
{
|
|
|
|
|
m_dragging = true;
|
|
|
|
|
m_dragStartPos = event->globalPos() - pos();
|
|
|
|
|
}
|
|
|
|
|
QWidget::mousePressEvent(event);
|
2026-04-18 15:15:43 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-19 10:09:10 +00:00
|
|
|
void FreqDisplayOverlay::mouseMoveEvent(QMouseEvent* event)
|
|
|
|
|
{
|
|
|
|
|
if (m_dragging && (event->buttons() & Qt::LeftButton)) {
|
|
|
|
|
move(event->globalPos() - m_dragStartPos);
|
|
|
|
|
}
|
|
|
|
|
QWidget::mouseMoveEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayOverlay::mouseReleaseEvent(QMouseEvent* event)
|
|
|
|
|
{
|
|
|
|
|
m_dragging = false;
|
|
|
|
|
QWidget::mouseReleaseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- FreqDisplayGUI -------------------------------------------------------
|
|
|
|
|
|
2026-04-18 15:13:46 +00:00
|
|
|
FreqDisplayGUI* FreqDisplayGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
|
|
|
|
{
|
|
|
|
|
return new FreqDisplayGUI(pluginAPI, featureUISet, feature);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::destroy()
|
|
|
|
|
{
|
|
|
|
|
delete this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::resetToDefaults()
|
|
|
|
|
{
|
|
|
|
|
m_settings.resetToDefaults();
|
|
|
|
|
displaySettings();
|
|
|
|
|
applySettings(true);
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray FreqDisplayGUI::serialize() const
|
|
|
|
|
{
|
|
|
|
|
return m_settings.serialize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FreqDisplayGUI::deserialize(const QByteArray& data)
|
|
|
|
|
{
|
|
|
|
|
if (m_settings.deserialize(data))
|
|
|
|
|
{
|
|
|
|
|
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
|
|
|
|
displaySettings();
|
|
|
|
|
applySettings(true);
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resetToDefaults();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FreqDisplayGUI::FreqDisplayGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
|
|
|
|
FeatureGUI(parent),
|
|
|
|
|
ui(new Ui::FreqDisplayGUI),
|
|
|
|
|
m_freqDisplay(reinterpret_cast<FreqDisplay*>(feature)),
|
2026-04-18 15:19:24 +00:00
|
|
|
m_availableChannelOrFeatureHandler(QStringList(), rxTxChannelKinds),
|
2026-04-18 20:54:57 +00:00
|
|
|
m_doApplySettings(true),
|
2026-04-19 10:09:10 +00:00
|
|
|
m_overlay(nullptr)
|
2026-04-18 15:13:46 +00:00
|
|
|
{
|
2026-04-18 15:15:03 +00:00
|
|
|
(void) pluginAPI;
|
|
|
|
|
(void) featureUISet;
|
2026-04-18 15:13:46 +00:00
|
|
|
|
|
|
|
|
m_feature = feature;
|
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
|
|
|
|
|
|
|
|
RollupContents *rollupContents = getRollupContents();
|
|
|
|
|
ui->setupUi(rollupContents);
|
2026-04-18 17:14:34 +00:00
|
|
|
ui->frequencyValue->setWordWrap(true);
|
2026-04-18 15:13:46 +00:00
|
|
|
|
|
|
|
|
m_freqDisplay->setMessageQueueToGUI(&m_inputMessageQueue);
|
|
|
|
|
m_settings = m_freqDisplay->getSettings();
|
|
|
|
|
|
|
|
|
|
connect(
|
|
|
|
|
&m_availableChannelOrFeatureHandler,
|
|
|
|
|
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
|
|
|
|
this,
|
|
|
|
|
&FreqDisplayGUI::channelsOrFeaturesChanged
|
|
|
|
|
);
|
|
|
|
|
m_availableChannelOrFeatureHandler.scanAvailableChannelsAndFeatures();
|
|
|
|
|
|
|
|
|
|
connect(ui->channels, qOverload<int>(&QComboBox::currentIndexChanged), this, &FreqDisplayGUI::on_channels_currentIndexChanged);
|
2026-04-18 17:14:34 +00:00
|
|
|
connect(ui->displayMode, qOverload<int>(&QComboBox::currentIndexChanged), this, &FreqDisplayGUI::on_displayMode_currentIndexChanged);
|
2026-04-18 19:07:03 +00:00
|
|
|
connect(ui->speech, &ButtonSwitch::toggled, this, &FreqDisplayGUI::on_speech_toggled);
|
2026-04-18 16:28:15 +00:00
|
|
|
connect(ui->fontFamily, &QFontComboBox::currentFontChanged, this, &FreqDisplayGUI::on_fontFamily_currentFontChanged);
|
|
|
|
|
connect(ui->transparentBackground, &ButtonSwitch::toggled, this, &FreqDisplayGUI::on_transparentBackground_toggled);
|
2026-04-19 09:05:21 +00:00
|
|
|
connect(ui->frequencyUnits, qOverload<int>(&QComboBox::currentIndexChanged), this, &FreqDisplayGUI::on_frequencyUnits_currentIndexChanged);
|
|
|
|
|
connect(ui->showUnits, &ButtonSwitch::toggled, this, &FreqDisplayGUI::on_showUnits_toggled);
|
2026-04-19 09:28:13 +00:00
|
|
|
connect(ui->freqDecimalPlaces, qOverload<int>(&QSpinBox::valueChanged), this, &FreqDisplayGUI::on_freqDecimalPlaces_valueChanged);
|
|
|
|
|
connect(ui->powerDecimalPlaces, qOverload<int>(&QSpinBox::valueChanged), this, &FreqDisplayGUI::on_powerDecimalPlaces_valueChanged);
|
2026-04-19 11:52:39 +01:00
|
|
|
connect(ui->textColor, &QPushButton::clicked, this, &FreqDisplayGUI::on_textColor_clicked);
|
2026-04-18 15:13:46 +00:00
|
|
|
connect(&m_pollTimer, &QTimer::timeout, this, &FreqDisplayGUI::pollSelectedChannel);
|
2026-04-18 15:18:12 +00:00
|
|
|
m_pollTimer.start(pollIntervalMs);
|
2026-04-18 15:13:46 +00:00
|
|
|
|
2026-04-19 11:52:39 +01:00
|
|
|
#ifndef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
ui->speech->setVisible(false);
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-04-18 15:13:46 +00:00
|
|
|
displaySettings();
|
|
|
|
|
updateFrequencyText();
|
2026-04-19 09:50:05 +01:00
|
|
|
m_resizer.enableChildMouseTracking();
|
2026-04-18 15:13:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FreqDisplayGUI::~FreqDisplayGUI()
|
|
|
|
|
{
|
2026-04-19 10:31:51 +01:00
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
if (m_speech)
|
|
|
|
|
{
|
|
|
|
|
disconnect(m_speech, &QTextToSpeech::stateChanged, this, &FreqDisplayGUI::speechStateChanged);
|
|
|
|
|
delete m_speech;
|
|
|
|
|
m_speech = nullptr;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2026-04-19 10:09:10 +00:00
|
|
|
delete m_overlay;
|
2026-04-18 15:13:46 +00:00
|
|
|
delete ui;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::setWorkspaceIndex(int index)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_workspaceIndex = index;
|
|
|
|
|
m_feature->setWorkspaceIndex(index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::displaySettings()
|
|
|
|
|
{
|
|
|
|
|
setWindowTitle(m_settings.m_title);
|
|
|
|
|
setTitle(m_settings.m_title);
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
// Populate font combo box with the saved font (or system default if empty)
|
|
|
|
|
ui->fontFamily->blockSignals(true);
|
|
|
|
|
if (!m_settings.m_fontName.isEmpty()) {
|
|
|
|
|
ui->fontFamily->setCurrentFont(QFont(m_settings.m_fontName));
|
|
|
|
|
}
|
|
|
|
|
ui->fontFamily->blockSignals(false);
|
|
|
|
|
|
2026-04-18 17:14:34 +00:00
|
|
|
ui->displayMode->blockSignals(true);
|
|
|
|
|
ui->displayMode->setCurrentIndex(static_cast<int>(m_settings.m_displayMode));
|
|
|
|
|
ui->displayMode->blockSignals(false);
|
|
|
|
|
|
2026-04-18 19:07:03 +00:00
|
|
|
ui->speech->blockSignals(true);
|
|
|
|
|
ui->speech->setChecked(m_settings.m_speechEnabled);
|
|
|
|
|
ui->speech->blockSignals(false);
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
ui->transparentBackground->blockSignals(true);
|
|
|
|
|
ui->transparentBackground->setChecked(m_settings.m_transparentBackground);
|
|
|
|
|
ui->transparentBackground->blockSignals(false);
|
|
|
|
|
|
2026-04-19 09:05:21 +00:00
|
|
|
ui->frequencyUnits->blockSignals(true);
|
|
|
|
|
ui->frequencyUnits->setCurrentIndex(static_cast<int>(m_settings.m_frequencyUnits));
|
|
|
|
|
ui->frequencyUnits->blockSignals(false);
|
|
|
|
|
|
|
|
|
|
ui->showUnits->blockSignals(true);
|
|
|
|
|
ui->showUnits->setChecked(m_settings.m_showUnits);
|
|
|
|
|
ui->showUnits->blockSignals(false);
|
|
|
|
|
|
2026-04-19 09:28:13 +00:00
|
|
|
ui->powerDecimalPlaces->blockSignals(true);
|
|
|
|
|
ui->powerDecimalPlaces->setValue(m_settings.m_powerDecimalPlaces);
|
|
|
|
|
ui->powerDecimalPlaces->blockSignals(false);
|
|
|
|
|
|
|
|
|
|
// Must come after frequencyUnits is set so the range/enabled state is correct
|
|
|
|
|
updateFreqDecimalSpinbox();
|
|
|
|
|
|
2026-04-19 10:34:01 +00:00
|
|
|
updateTextColorButton();
|
2026-04-18 16:28:15 +00:00
|
|
|
applyTransparency();
|
2026-04-19 10:34:01 +00:00
|
|
|
applyTextColor();
|
2026-04-19 09:50:38 +01:00
|
|
|
applySpeech();
|
2026-04-18 15:13:46 +00:00
|
|
|
updateChannelList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::applySettings(bool force)
|
|
|
|
|
{
|
|
|
|
|
if (!m_doApplySettings) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList settingsKeys;
|
|
|
|
|
settingsKeys.append("title");
|
|
|
|
|
settingsKeys.append("selectedChannel");
|
|
|
|
|
settingsKeys.append("workspaceIndex");
|
|
|
|
|
settingsKeys.append("geometryBytes");
|
2026-04-18 16:28:15 +00:00
|
|
|
settingsKeys.append("fontName");
|
|
|
|
|
settingsKeys.append("transparentBackground");
|
2026-04-18 17:14:34 +00:00
|
|
|
settingsKeys.append("displayMode");
|
2026-04-18 19:07:03 +00:00
|
|
|
settingsKeys.append("speechEnabled");
|
2026-04-19 09:05:21 +00:00
|
|
|
settingsKeys.append("frequencyUnits");
|
|
|
|
|
settingsKeys.append("showUnits");
|
2026-04-19 09:28:13 +00:00
|
|
|
settingsKeys.append("freqDecimalPlaces");
|
|
|
|
|
settingsKeys.append("powerDecimalPlaces");
|
2026-04-19 10:34:01 +00:00
|
|
|
settingsKeys.append("textColor");
|
2026-04-18 15:13:46 +00:00
|
|
|
m_freqDisplay->applySettings(m_settings, settingsKeys, force);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::updateChannelList()
|
|
|
|
|
{
|
|
|
|
|
m_availableChannels = m_availableChannelOrFeatureHandler.getAvailableChannelOrFeatureList();
|
|
|
|
|
|
|
|
|
|
ui->channels->blockSignals(true);
|
|
|
|
|
ui->channels->clear();
|
|
|
|
|
|
|
|
|
|
int selectedIndex = -1;
|
|
|
|
|
|
2026-04-18 15:15:43 +00:00
|
|
|
for (int i = 0; i < m_availableChannels.size(); ++i)
|
2026-04-18 15:13:46 +00:00
|
|
|
{
|
2026-04-18 15:15:43 +00:00
|
|
|
const QString longId = m_availableChannels.at(i).getLongId();
|
2026-04-18 15:13:46 +00:00
|
|
|
ui->channels->addItem(longId);
|
|
|
|
|
|
|
|
|
|
if (longId == m_settings.m_selectedChannel) {
|
|
|
|
|
selectedIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((selectedIndex < 0) && (m_availableChannels.size() > 0)) {
|
|
|
|
|
selectedIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedIndex >= 0)
|
|
|
|
|
{
|
|
|
|
|
ui->channels->setCurrentIndex(selectedIndex);
|
|
|
|
|
m_settings.m_selectedChannel = m_availableChannels.at(selectedIndex).getLongId();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_selectedChannel.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->channels->blockSignals(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo, const QStringList& removed, const QStringList& added)
|
|
|
|
|
{
|
|
|
|
|
(void) removed;
|
|
|
|
|
(void) added;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < renameFrom.size() && i < renameTo.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (m_settings.m_selectedChannel == renameFrom.at(i)) {
|
|
|
|
|
m_settings.m_selectedChannel = renameTo.at(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateChannelList();
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::on_channels_currentIndexChanged(int index)
|
|
|
|
|
{
|
|
|
|
|
if ((index >= 0) && (index < m_availableChannels.size())) {
|
|
|
|
|
m_settings.m_selectedChannel = m_availableChannels.at(index).getLongId();
|
|
|
|
|
} else {
|
|
|
|
|
m_settings.m_selectedChannel.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::pollSelectedChannel()
|
|
|
|
|
{
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::updateFrequencyText()
|
|
|
|
|
{
|
2026-04-18 19:07:03 +00:00
|
|
|
auto setLabelText = [this](const QString& text) {
|
|
|
|
|
ui->frequencyValue->setText(text);
|
2026-04-19 10:09:10 +00:00
|
|
|
if (m_overlay) {
|
|
|
|
|
m_overlay->label()->setText(text);
|
|
|
|
|
}
|
2026-04-18 19:07:03 +00:00
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
2026-04-18 21:03:17 +00:00
|
|
|
if (m_settings.m_speechEnabled && m_speech && (text != m_previousDisplayText))
|
|
|
|
|
{
|
2026-04-19 09:16:40 +00:00
|
|
|
const QString speechText = textForSpeech(text);
|
2026-04-18 21:03:17 +00:00
|
|
|
if (m_speech->state() == QTextToSpeech::Speaking)
|
|
|
|
|
{
|
|
|
|
|
// Engine is busy — save the latest text so the stateChanged
|
|
|
|
|
// slot can say it once the current utterance finishes.
|
2026-04-19 09:16:40 +00:00
|
|
|
m_pendingSpeechText = speechText;
|
2026-04-18 21:03:17 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_pendingSpeechText.clear();
|
2026-04-19 09:16:40 +00:00
|
|
|
m_speech->say(speechText);
|
2026-04-18 21:03:17 +00:00
|
|
|
}
|
2026-04-18 19:07:03 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
m_previousDisplayText = text;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-18 15:13:46 +00:00
|
|
|
if (m_settings.m_selectedChannel.isEmpty())
|
|
|
|
|
{
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(tr("No channel selected"));
|
2026-04-18 15:13:46 +00:00
|
|
|
updateFrequencyFont();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int channelListIndex = m_availableChannels.indexOfLongId(m_settings.m_selectedChannel);
|
|
|
|
|
|
|
|
|
|
if (channelListIndex < 0)
|
|
|
|
|
{
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(tr("Selected channel unavailable"));
|
2026-04-18 15:13:46 +00:00
|
|
|
updateFrequencyFont();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& selectedChannel = m_availableChannels.at(channelListIndex);
|
2026-04-18 17:14:34 +00:00
|
|
|
const FreqDisplaySettings::DisplayMode mode = m_settings.m_displayMode;
|
2026-04-18 15:13:46 +00:00
|
|
|
|
2026-04-18 17:14:34 +00:00
|
|
|
// --- Frequency ---
|
|
|
|
|
QString freqText;
|
|
|
|
|
if (mode == FreqDisplaySettings::Frequency || mode == FreqDisplaySettings::Both)
|
2026-04-18 15:13:46 +00:00
|
|
|
{
|
2026-04-18 17:14:34 +00:00
|
|
|
double centerFrequencyHz = 0.0;
|
|
|
|
|
int offsetHz = 0;
|
|
|
|
|
|
|
|
|
|
if (!ChannelWebAPIUtils::getCenterFrequency(selectedChannel.m_superIndex, centerFrequencyHz))
|
|
|
|
|
{
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(tr("Frequency unavailable"));
|
2026-04-18 17:14:34 +00:00
|
|
|
updateFrequencyFont();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!ChannelWebAPIUtils::getFrequencyOffset(selectedChannel.m_superIndex, selectedChannel.m_index, offsetHz))
|
|
|
|
|
{
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(tr("Offset unavailable"));
|
2026-04-18 17:14:34 +00:00
|
|
|
updateFrequencyFont();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const qint64 absoluteFrequency = qRound64(centerFrequencyHz) + static_cast<qint64>(offsetHz);
|
2026-04-19 09:05:21 +00:00
|
|
|
freqText = formatFrequency(absoluteFrequency);
|
2026-04-18 15:13:46 +00:00
|
|
|
}
|
2026-04-18 17:14:34 +00:00
|
|
|
|
|
|
|
|
// --- Power ---
|
|
|
|
|
QString powerText;
|
|
|
|
|
if (mode == FreqDisplaySettings::Power || mode == FreqDisplaySettings::Both)
|
2026-04-18 15:18:50 +00:00
|
|
|
{
|
2026-04-18 17:14:34 +00:00
|
|
|
double power = 0.0;
|
|
|
|
|
if (!ChannelWebAPIUtils::getChannelReportValue(selectedChannel.m_superIndex, selectedChannel.m_index, "channelPowerDB", power))
|
|
|
|
|
{
|
2026-04-19 09:05:21 +00:00
|
|
|
if (mode == FreqDisplaySettings::Power)
|
|
|
|
|
{
|
|
|
|
|
// Power-only mode: nothing else to show
|
|
|
|
|
setLabelText(tr("Power unavailable"));
|
|
|
|
|
updateFrequencyFont();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Both mode: power unavailable but frequency is valid — show frequency only
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
powerText = m_settings.m_showUnits
|
2026-04-19 09:28:13 +00:00
|
|
|
? QString("%1 dB").arg(power, 0, 'f', m_settings.m_powerDecimalPlaces)
|
|
|
|
|
: QString("%1").arg(power, 0, 'f', m_settings.m_powerDecimalPlaces);
|
2026-04-18 17:14:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Compose display text ---
|
|
|
|
|
if (mode == FreqDisplaySettings::Frequency) {
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(freqText);
|
2026-04-18 17:14:34 +00:00
|
|
|
} else if (mode == FreqDisplaySettings::Power) {
|
2026-04-18 19:07:03 +00:00
|
|
|
setLabelText(powerText);
|
2026-04-18 17:14:34 +00:00
|
|
|
} else {
|
2026-04-19 09:05:21 +00:00
|
|
|
// Both: show frequency alone when power is unavailable
|
|
|
|
|
setLabelText(powerText.isEmpty() ? freqText : freqText + "\n" + powerText);
|
2026-04-18 15:18:50 +00:00
|
|
|
}
|
2026-04-18 15:13:46 +00:00
|
|
|
|
|
|
|
|
updateFrequencyFont();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::updateFrequencyFont()
|
|
|
|
|
{
|
2026-04-19 10:09:10 +00:00
|
|
|
QLabel* label = m_overlay ? m_overlay->label() : ui->frequencyValue;
|
|
|
|
|
const int availableWidth = label->width();
|
|
|
|
|
const int availableHeight = label->height();
|
2026-04-18 15:45:48 +00:00
|
|
|
if (availableWidth <= 0 || availableHeight <= 0) {
|
2026-04-18 15:18:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2026-04-18 15:13:46 +00:00
|
|
|
|
2026-04-19 10:09:10 +00:00
|
|
|
const QString text = label->text();
|
2026-04-18 15:45:48 +00:00
|
|
|
if (text.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
// Build a font with the user-chosen family (or the widget's current family if none saved)
|
2026-04-19 10:09:10 +00:00
|
|
|
QFont font = label->font();
|
2026-04-18 16:28:15 +00:00
|
|
|
if (!m_settings.m_fontName.isEmpty()) {
|
|
|
|
|
font.setFamily(m_settings.m_fontName);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 15:45:48 +00:00
|
|
|
// Probe at a large reference size to get accurate text dimensions, then
|
|
|
|
|
// scale linearly to find the largest point size that fits in both directions.
|
2026-04-18 17:01:41 +00:00
|
|
|
font.setPointSize(fontProbePointSize);
|
2026-04-18 15:45:48 +00:00
|
|
|
const QFontMetrics fm(font);
|
|
|
|
|
|
2026-04-18 17:14:34 +00:00
|
|
|
// For multi-line text (Both mode) find the widest line; divide available
|
|
|
|
|
// height equally across lines so each line receives the same font size.
|
|
|
|
|
const QStringList lines = text.split('\n');
|
|
|
|
|
const int numLines = lines.size();
|
|
|
|
|
int maxLineWidth = 0;
|
|
|
|
|
for (const QString& line : lines) {
|
|
|
|
|
maxLineWidth = qMax(maxLineWidth, fm.horizontalAdvance(line));
|
|
|
|
|
}
|
2026-04-19 10:58:06 +00:00
|
|
|
const int lineHeight = fm.lineSpacing();
|
2026-04-18 17:14:34 +00:00
|
|
|
|
|
|
|
|
if (maxLineWidth <= 0 || lineHeight <= 0) {
|
2026-04-18 15:45:48 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 10:46:27 +01:00
|
|
|
maxLineWidth += 5; // Add some space for a border
|
|
|
|
|
|
2026-04-18 17:14:34 +00:00
|
|
|
const int maxFromWidth = fontProbePointSize * availableWidth / maxLineWidth;
|
2026-04-19 10:58:06 +00:00
|
|
|
const int maxFromHeight = fontProbePointSize * availableHeight / (lineHeight * numLines);
|
2026-04-19 08:44:14 +00:00
|
|
|
int pointSize = qMax(minimumFrequencyFontPointSize, qMin(maxFromWidth, maxFromHeight));
|
2026-04-18 15:13:46 +00:00
|
|
|
font.setPointSize(pointSize);
|
2026-04-19 08:44:14 +00:00
|
|
|
|
|
|
|
|
// Verify the text actually fits at the calculated point size. The linear
|
|
|
|
|
// interpolation from fontProbePointSize can be slightly inaccurate due to
|
|
|
|
|
// font hinting or non-linear glyph metrics at the target size; if the text
|
2026-04-19 10:58:06 +00:00
|
|
|
// would overflow either horizontally or vertically, reduce by one point at a
|
|
|
|
|
// time until it fits.
|
|
|
|
|
while (pointSize > minimumFrequencyFontPointSize)
|
2026-04-19 08:44:14 +00:00
|
|
|
{
|
|
|
|
|
const QFontMetrics verifyFm(font);
|
2026-04-19 10:58:06 +00:00
|
|
|
bool overflow = false;
|
2026-04-19 08:44:14 +00:00
|
|
|
for (const QString& line : lines)
|
|
|
|
|
{
|
|
|
|
|
if (verifyFm.horizontalAdvance(line) > availableWidth)
|
|
|
|
|
{
|
2026-04-19 10:58:06 +00:00
|
|
|
overflow = true;
|
2026-04-19 08:44:14 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-19 10:58:06 +00:00
|
|
|
if (!overflow && (verifyFm.lineSpacing() * numLines > availableHeight)) {
|
|
|
|
|
overflow = true;
|
|
|
|
|
}
|
|
|
|
|
if (!overflow) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
font.setPointSize(--pointSize);
|
2026-04-19 08:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-19 10:09:10 +00:00
|
|
|
label->setFont(font);
|
2026-04-18 15:13:46 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
void FreqDisplayGUI::applyTransparency()
|
|
|
|
|
{
|
2026-04-18 20:34:00 +00:00
|
|
|
if (m_settings.m_transparentBackground)
|
|
|
|
|
{
|
2026-04-19 10:09:10 +00:00
|
|
|
// Only create the overlay if this widget is already visible on screen.
|
|
|
|
|
// When called at startup (before the widget has been shown and placed by
|
|
|
|
|
// the workspace), skip overlay creation so that the window appears at its
|
|
|
|
|
// saved MDI position rather than at (0,0).
|
|
|
|
|
if (!m_overlay && isVisible())
|
2026-04-18 20:54:57 +00:00
|
|
|
{
|
2026-04-19 10:09:10 +00:00
|
|
|
m_overlay = new FreqDisplayOverlay();
|
|
|
|
|
m_overlay->label()->setText(ui->frequencyValue->text());
|
|
|
|
|
connect(m_overlay, &FreqDisplayOverlay::exitTransparentMode,
|
|
|
|
|
this, &FreqDisplayGUI::onExitTransparentMode);
|
|
|
|
|
connect(m_overlay, &FreqDisplayOverlay::resized,
|
|
|
|
|
this, &FreqDisplayGUI::updateFrequencyFont);
|
|
|
|
|
// Position the overlay at the current screen position of FreqDisplayGUI.
|
|
|
|
|
m_overlay->move(mapToGlobal(QPoint(0, 0)));
|
|
|
|
|
m_overlay->resize(size());
|
2026-04-19 10:34:01 +00:00
|
|
|
applyTextColor();
|
2026-04-19 10:09:10 +00:00
|
|
|
m_overlay->show();
|
2026-04-18 20:54:57 +00:00
|
|
|
}
|
2026-04-19 10:09:10 +00:00
|
|
|
hide();
|
2026-04-18 20:34:00 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-04-19 10:09:10 +00:00
|
|
|
if (m_overlay)
|
2026-04-18 20:54:57 +00:00
|
|
|
{
|
2026-04-19 10:16:13 +00:00
|
|
|
// Hide immediately so the overlay disappears at once, then schedule
|
|
|
|
|
// deletion for after the current event-loop iteration. Plain delete
|
|
|
|
|
// would crash here because onExitTransparentMode() is invoked via a
|
|
|
|
|
// signal emitted from inside the overlay's contextMenuEvent(), which
|
|
|
|
|
// is still on the call stack when we reach this point.
|
|
|
|
|
m_overlay->hide();
|
|
|
|
|
m_overlay->deleteLater();
|
2026-04-19 10:09:10 +00:00
|
|
|
m_overlay = nullptr;
|
2026-04-18 20:54:57 +00:00
|
|
|
}
|
2026-04-19 10:09:10 +00:00
|
|
|
show();
|
2026-04-19 10:34:01 +00:00
|
|
|
applyTextColor();
|
2026-04-19 10:09:10 +00:00
|
|
|
updateFrequencyFont();
|
2026-04-18 20:34:00 +00:00
|
|
|
}
|
2026-04-18 16:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-19 09:50:38 +01:00
|
|
|
void FreqDisplayGUI::applySpeech()
|
|
|
|
|
{
|
|
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
if (m_settings.m_speechEnabled && !m_speech)
|
|
|
|
|
{
|
|
|
|
|
m_speech = new QTextToSpeech(this);
|
2026-04-19 11:52:39 +01:00
|
|
|
if (m_speech)
|
|
|
|
|
{
|
|
|
|
|
connect(m_speech, &QTextToSpeech::stateChanged, this, &FreqDisplayGUI::speechStateChanged);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ui->speech->setChecked(false);
|
|
|
|
|
ui->speech->setEnabled(false);
|
|
|
|
|
}
|
2026-04-19 09:50:38 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 10:34:01 +00:00
|
|
|
void FreqDisplayGUI::applyTextColor()
|
|
|
|
|
{
|
|
|
|
|
const QString styleSheet = QString("color: %1;").arg(m_settings.m_textColor.name());
|
|
|
|
|
if (m_overlay) {
|
|
|
|
|
m_overlay->label()->setStyleSheet(styleSheet);
|
|
|
|
|
} else {
|
|
|
|
|
ui->frequencyValue->setStyleSheet(styleSheet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::updateTextColorButton()
|
|
|
|
|
{
|
|
|
|
|
QPixmap pm(16, 16);
|
|
|
|
|
pm.fill(m_settings.m_textColor);
|
|
|
|
|
ui->textColor->setIcon(pm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::on_textColor_clicked()
|
|
|
|
|
{
|
|
|
|
|
const QColor color = QColorDialog::getColor(
|
|
|
|
|
m_settings.m_textColor, this, tr("Select text color"), QColorDialog::DontUseNativeDialog);
|
|
|
|
|
if (color.isValid())
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_textColor = color;
|
|
|
|
|
updateTextColorButton();
|
|
|
|
|
applyTextColor();
|
|
|
|
|
applySettings();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 17:14:34 +00:00
|
|
|
void FreqDisplayGUI::on_displayMode_currentIndexChanged(int index)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_displayMode = static_cast<FreqDisplaySettings::DisplayMode>(index);
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 19:07:03 +00:00
|
|
|
void FreqDisplayGUI::on_speech_toggled(bool checked)
|
|
|
|
|
{
|
2026-04-19 11:52:39 +01:00
|
|
|
bool hadSpeech = m_settings.m_speechEnabled;
|
2026-04-18 19:07:03 +00:00
|
|
|
m_settings.m_speechEnabled = checked;
|
2026-04-19 09:50:38 +01:00
|
|
|
applySpeech();
|
2026-04-18 19:07:03 +00:00
|
|
|
applySettings();
|
2026-04-19 11:52:39 +01:00
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
if (checked && !hadSpeech && m_speech)
|
|
|
|
|
{
|
|
|
|
|
// Just enabled speech: speak the current text immediately
|
|
|
|
|
const QString speechText = textForSpeech(ui->frequencyValue->text());
|
|
|
|
|
m_speech->say(speechText);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2026-04-18 19:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
void FreqDisplayGUI::on_fontFamily_currentFontChanged(const QFont& font)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_fontName = font.family();
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyFont();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 09:05:21 +00:00
|
|
|
void FreqDisplayGUI::on_frequencyUnits_currentIndexChanged(int index)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_frequencyUnits = static_cast<FreqDisplaySettings::FrequencyUnits>(index);
|
2026-04-19 09:28:13 +00:00
|
|
|
updateFreqDecimalSpinbox();
|
2026-04-19 09:05:21 +00:00
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::on_showUnits_toggled(bool checked)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_showUnits = checked;
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 09:28:13 +00:00
|
|
|
void FreqDisplayGUI::updateFreqDecimalSpinbox()
|
|
|
|
|
{
|
|
|
|
|
const FreqDisplaySettings::FrequencyUnits units = m_settings.m_frequencyUnits;
|
|
|
|
|
const bool enabled = (units != FreqDisplaySettings::Hz);
|
|
|
|
|
ui->freqDecimalPlaces->setEnabled(enabled);
|
|
|
|
|
ui->freqDecimalPlacesLabel->setEnabled(enabled);
|
|
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
|
{
|
|
|
|
|
int maxDecimals = 3; // kHz
|
|
|
|
|
if (units == FreqDisplaySettings::MHz) {
|
|
|
|
|
maxDecimals = 6;
|
|
|
|
|
} else if (units == FreqDisplaySettings::GHz) {
|
|
|
|
|
maxDecimals = 9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->freqDecimalPlaces->blockSignals(true);
|
|
|
|
|
ui->freqDecimalPlaces->setMaximum(maxDecimals);
|
|
|
|
|
// Clamp stored value to new maximum so the spinbox and setting stay in sync
|
|
|
|
|
m_settings.m_freqDecimalPlaces = qMin(m_settings.m_freqDecimalPlaces, maxDecimals);
|
|
|
|
|
ui->freqDecimalPlaces->setValue(m_settings.m_freqDecimalPlaces);
|
|
|
|
|
ui->freqDecimalPlaces->blockSignals(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::on_freqDecimalPlaces_valueChanged(int value)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_freqDecimalPlaces = value;
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreqDisplayGUI::on_powerDecimalPlaces_valueChanged(int value)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_powerDecimalPlaces = value;
|
|
|
|
|
applySettings();
|
|
|
|
|
updateFrequencyText();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 09:05:21 +00:00
|
|
|
QString FreqDisplayGUI::formatFrequency(qint64 frequencyHz) const
|
|
|
|
|
{
|
|
|
|
|
const QLocale locale;
|
|
|
|
|
const bool showUnits = m_settings.m_showUnits;
|
2026-04-19 09:28:13 +00:00
|
|
|
const int dp = m_settings.m_freqDecimalPlaces;
|
2026-04-19 09:05:21 +00:00
|
|
|
|
|
|
|
|
switch (m_settings.m_frequencyUnits)
|
|
|
|
|
{
|
|
|
|
|
case FreqDisplaySettings::kHz: {
|
2026-04-19 09:28:13 +00:00
|
|
|
const QString s = locale.toString(frequencyHz / kHzDivisor, 'f', dp);
|
2026-04-19 09:05:21 +00:00
|
|
|
return showUnits ? s + tr(" kHz") : s;
|
|
|
|
|
}
|
|
|
|
|
case FreqDisplaySettings::MHz: {
|
2026-04-19 09:28:13 +00:00
|
|
|
const QString s = locale.toString(frequencyHz / MHzDivisor, 'f', dp);
|
2026-04-19 09:05:21 +00:00
|
|
|
return showUnits ? s + tr(" MHz") : s;
|
|
|
|
|
}
|
|
|
|
|
case FreqDisplaySettings::GHz: {
|
2026-04-19 09:28:13 +00:00
|
|
|
const QString s = locale.toString(frequencyHz / GHzDivisor, 'f', dp);
|
2026-04-19 09:05:21 +00:00
|
|
|
return showUnits ? s + tr(" GHz") : s;
|
|
|
|
|
}
|
|
|
|
|
case FreqDisplaySettings::Hz:
|
|
|
|
|
default: {
|
|
|
|
|
const QString s = locale.toString(frequencyHz);
|
|
|
|
|
return showUnits ? s + tr(" Hz") : s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 16:28:15 +00:00
|
|
|
void FreqDisplayGUI::on_transparentBackground_toggled(bool checked)
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_transparentBackground = checked;
|
|
|
|
|
applyTransparency();
|
|
|
|
|
applySettings();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 10:09:10 +00:00
|
|
|
void FreqDisplayGUI::onExitTransparentMode()
|
|
|
|
|
{
|
|
|
|
|
m_settings.m_transparentBackground = false;
|
|
|
|
|
ui->transparentBackground->blockSignals(true);
|
|
|
|
|
ui->transparentBackground->setChecked(false);
|
|
|
|
|
ui->transparentBackground->blockSignals(false);
|
|
|
|
|
applyTransparency();
|
|
|
|
|
applySettings();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 15:13:46 +00:00
|
|
|
void FreqDisplayGUI::resizeEvent(QResizeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
FeatureGUI::resizeEvent(event);
|
|
|
|
|
updateFrequencyFont();
|
|
|
|
|
}
|
2026-04-18 21:03:17 +00:00
|
|
|
|
|
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
|
|
|
void FreqDisplayGUI::speechStateChanged(QTextToSpeech::State state)
|
|
|
|
|
{
|
|
|
|
|
if (state == QTextToSpeech::Ready && !m_pendingSpeechText.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
const QString text = m_pendingSpeechText;
|
|
|
|
|
m_pendingSpeechText.clear();
|
|
|
|
|
m_speech->say(text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|