398 lines
21 KiB
C++
398 lines
21 KiB
C++
#include <QCoreApplication>
|
||
#include "radiosettingspage.h"
|
||
#include "ptt_hotkey_manager.h"
|
||
#include "radio_audio_engine.h"
|
||
#include "../db/db_manager.h"
|
||
#include "../../lib/debug_config.h"
|
||
|
||
#include <QApplication>
|
||
#include <QVBoxLayout>
|
||
#include <QHBoxLayout>
|
||
#include <QLabel>
|
||
#include <QPushButton>
|
||
#include <QHideEvent>
|
||
#include <QShowEvent>
|
||
#include <QFormLayout>
|
||
#include <QSpinBox>
|
||
#include <QCheckBox>
|
||
#include <QButtonGroup>
|
||
#include <QPainter>
|
||
#include <algorithm>
|
||
#include <QKeyEvent>
|
||
#ifdef Q_OS_WIN
|
||
#include <qt_windows.h>
|
||
#endif
|
||
|
||
/* Шкала показывает именно RMS детектора; отметки соответствуют редактируемым порогам. */
|
||
class RadioLevelMeter : public QWidget {
|
||
public:
|
||
explicit RadioLevelMeter(QWidget* parent) : QWidget(parent) { setMinimumSize(200, 34); }
|
||
float level = -120;
|
||
int on = -35, off = -45;
|
||
bool valid = false, vad = false;
|
||
protected:
|
||
void paintEvent(QPaintEvent*) override {
|
||
QPainter painter(this);
|
||
QRect bar(1, 1, width() - 2, 16);
|
||
painter.fillRect(bar, palette().color(QPalette::Mid));
|
||
auto x = [&](float value) { return bar.left() + int(std::clamp(vad ? value / 100 : (value + 90) / 90, 0.0f, 1.0f) * bar.width()); };
|
||
if (valid) painter.fillRect(QRect(bar.left(), bar.top(), x(level) - bar.left(), bar.height()), QColor("#27ae60"));
|
||
painter.setPen(palette().color(QPalette::Text));
|
||
painter.drawText(QRect(0, 18, width(), 16), Qt::AlignLeft, vad ? QStringLiteral("0%") : QStringLiteral("−90 dBFS"));
|
||
painter.drawText(QRect(0, 18, width(), 16), Qt::AlignRight, vad ? QStringLiteral("100%") : QStringLiteral("0"));
|
||
for (int threshold : {off, on}) {
|
||
if (vad && threshold == off) continue;
|
||
painter.setPen(QPen(threshold == on ? QColor("#e67e22") : QColor("#3390ec"), 2));
|
||
painter.drawLine(x(threshold), 0, x(threshold), 18);
|
||
}
|
||
}
|
||
};
|
||
|
||
RadioActivationSettings::RadioActivationSettings(bool compact, QWidget* parent) : QWidget(parent), m_compact(compact) {
|
||
auto* form = new QFormLayout(this);
|
||
form->setContentsMargins(0, 0, 0, 8);
|
||
form->setRowWrapPolicy(QFormLayout::WrapLongRows);
|
||
form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||
auto add = [&](const QString& label, const char* name, int min, int max, const QString& suffix) {
|
||
auto* spin = new QSpinBox(this);
|
||
spin->setObjectName(QLatin1String(name)); spin->setRange(min, max); spin->setSuffix(suffix);
|
||
auto* text = new QLabel(label, this); text->setWordWrap(true);
|
||
form->addRow(text, spin);
|
||
connect(spin, QOverload<int>::of(&QSpinBox::valueChanged), this, &RadioActivationSettings::updateControls);
|
||
return spin;
|
||
};
|
||
auto label = [&](const char* name) {
|
||
auto* text = new QLabel(this); text->setObjectName(QLatin1String(name)); text->setWordWrap(true);
|
||
form->addRow(text); return text;
|
||
};
|
||
auto* modes = new QWidget(this);
|
||
auto* row = new QHBoxLayout(modes); row->setContentsMargins(0, 0, 0, 0); row->setSpacing(0);
|
||
m_mode = new QButtonGroup(this);
|
||
for (int mode : {1, 2}) {
|
||
auto* button = new QPushButton(mode == 1 ? QStringLiteral("VAD") : QCoreApplication::translate("ChatGui", "By signal level"), modes);
|
||
button->setObjectName(mode == 1 ? QStringLiteral("radioModeVad") : QStringLiteral("radioModeLevel"));
|
||
button->setCheckable(true); button->setMinimumHeight(36);
|
||
button->setStyleSheet("QPushButton:checked { background: #3390EC; color: white; }");
|
||
m_mode->addButton(button, mode); row->addWidget(button, 1);
|
||
connect(button, &QPushButton::clicked, this, [this, mode]() {
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "Radio settings: select mode=%d compact=%d (pending Apply)", mode, m_compact);
|
||
updateControls();
|
||
});
|
||
}
|
||
m_mode->button(1)->setChecked(true);
|
||
form->addRow(QCoreApplication::translate("ChatGui", "Automatic transmission:"), modes);
|
||
m_levelText = label("radioLevelText");
|
||
m_levelMeter = new RadioLevelMeter(this); m_levelMeter->setObjectName(QStringLiteral("radioLevelMeter"));
|
||
form->addRow(m_levelMeter);
|
||
m_levelOn = add(QCoreApplication::translate("ChatGui", "Start threshold (orange):"), "radioLevelOn", -90, 0, QStringLiteral(" dBFS"));
|
||
m_levelOff = add(QCoreApplication::translate("ChatGui", "Stop threshold (blue):"), "radioLevelOff", -90, 0, QStringLiteral(" dBFS"));
|
||
m_vadText = label("radioVadText");
|
||
m_vadMeter = new RadioLevelMeter(this); m_vadMeter->vad = true; m_vadMeter->setObjectName(QStringLiteral("radioVadMeter"));
|
||
form->addRow(m_vadMeter);
|
||
m_threshold = add(QCoreApplication::translate("ChatGui", "VAD speech threshold:"), "radioVadThreshold", 10, 95, QStringLiteral(" %"));
|
||
m_threshold->setToolTip(QCoreApplication::translate("ChatGui",
|
||
"Speech probability threshold: lower is more sensitive. This is not a volume threshold."));
|
||
m_gainText = label("radioVadGainText");
|
||
m_vadGain = add(QCoreApplication::translate("ChatGui", "VAD max gain:"), "radioVadGain", 1, 60, QCoreApplication::translate("ChatGui", " dB"));
|
||
m_hangover = add(QCoreApplication::translate("ChatGui", "Silence before ending transmission:"), "radioHangover", 50, 2000,
|
||
QCoreApplication::translate("ChatGui", " ms"));
|
||
if (!compact) {
|
||
m_burstFrames = add(QCoreApplication::translate("ChatGui", "Burst size (Opus frames):"), "radioBurstFrames", 1, 10, QString());
|
||
m_burstFrames->setToolTip(QCoreApplication::translate("ChatGui",
|
||
"Applies to the next transmission. Each frame is 20 ms; larger batches reduce overhead but add delay. The final partial batch is sent immediately."));
|
||
m_compressor = new QCheckBox(QCoreApplication::translate("ChatGui", "Transmission and VAD compressor"), this);
|
||
form->addRow(m_compressor);
|
||
connect(m_compressor, &QCheckBox::toggled, this, &RadioActivationSettings::updateControls);
|
||
m_txGain = add(QCoreApplication::translate("ChatGui", "Transmission max gain:"), "radioTxGain", 1, 60,
|
||
QCoreApplication::translate("ChatGui", " dB"));
|
||
label("radioActivationHint")->setText(QCoreApplication::translate("ChatGui",
|
||
"Enable automatic transmission using the radio panel button. Both modes include 0.6 s of prebuffer. "
|
||
"Signal level is measured after echo cancellation, before compression. Start requires 40 ms above the start threshold; "
|
||
"transmission ends after the configured silence below the stop threshold. The VAD compressor has a separate maximum gain."));
|
||
}
|
||
m_error = label("radioActivationError"); m_error->setStyleSheet(QStringLiteral("color: #e74c3c;"));
|
||
auto* engine = RadioAudioEngine::instance();
|
||
connect(engine, &RadioAudioEngine::stateChanged, this, [this, engine]() {
|
||
setEnabled(engine->settingsReady() && !engine->settingsBusy());
|
||
if (!m_loaded && engine->settingsReady()) loadSettings();
|
||
else updateControls();
|
||
});
|
||
connect(engine, &RadioAudioEngine::inputLevelChanged, this, &RadioActivationSettings::updateMeters);
|
||
loadSettings();
|
||
}
|
||
|
||
QMap<QString, int> RadioActivationSettings::values() const {
|
||
QMap<QString, int> result{{"radio_auto_mode", m_mode->checkedId()}, {"radio_level_on_dbfs", m_levelOn->value()},
|
||
{"radio_level_off_dbfs", m_levelOff->value()}, {"radio_vad_threshold", m_threshold->value()},
|
||
{"radio_auto_hangover_ms", m_hangover->value()}, {"radio_vad_compressor_max_gain_db", m_vadGain->value()}};
|
||
if (!m_compact) {
|
||
result.insert(QStringLiteral("radio_burst_frames"), m_burstFrames->value());
|
||
result.insert(QStringLiteral("radio_compressor_enabled"), m_compressor->isChecked() ? 1 : 0);
|
||
result.insert(QStringLiteral("radio_compressor_max_gain_db"), m_txGain->value());
|
||
}
|
||
return result;
|
||
}
|
||
|
||
bool RadioActivationSettings::canApply() const {
|
||
auto* engine = RadioAudioEngine::instance();
|
||
if (!m_loaded || !engine->settingsReady() || engine->settingsBusy() || m_levelOff->value() >= m_levelOn->value()) return false;
|
||
const auto draft = values();
|
||
for (auto it = draft.cbegin(); it != draft.cend(); ++it) if (engine->setting(it.key()) != it.value()) return true;
|
||
return false;
|
||
}
|
||
|
||
bool RadioActivationSettings::apply() {
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "Radio settings: apply compact=%d mode=%d on=%d off=%d vad=%d gain=%d hangover=%d",
|
||
m_compact, m_mode->checkedId(), m_levelOn->value(), m_levelOff->value(), m_threshold->value(),
|
||
m_vadGain->value(), m_hangover->value());
|
||
return RadioAudioEngine::instance()->applySettings(values());
|
||
}
|
||
|
||
void RadioActivationSettings::loadSettings() {
|
||
auto* engine = RadioAudioEngine::instance();
|
||
setEnabled(engine->settingsReady() && !engine->settingsBusy());
|
||
if (!engine->settingsReady()) return;
|
||
m_loaded = false;
|
||
m_mode->button(engine->autoMode())->setChecked(true);
|
||
m_levelOn->setValue(engine->setting(QStringLiteral("radio_level_on_dbfs")));
|
||
m_levelOff->setValue(engine->setting(QStringLiteral("radio_level_off_dbfs")));
|
||
m_threshold->setValue(engine->setting(QStringLiteral("radio_vad_threshold")));
|
||
m_hangover->setValue(engine->setting(QStringLiteral("radio_auto_hangover_ms")));
|
||
m_vadGain->setValue(engine->setting(QStringLiteral("radio_vad_compressor_max_gain_db")));
|
||
if (!m_compact) {
|
||
m_burstFrames->setValue(engine->setting(QStringLiteral("radio_burst_frames")));
|
||
m_compressor->setChecked(engine->setting(QStringLiteral("radio_compressor_enabled")) != 0);
|
||
m_txGain->setValue(engine->setting(QStringLiteral("radio_compressor_max_gain_db")));
|
||
}
|
||
m_loaded = true;
|
||
updateControls();
|
||
}
|
||
|
||
void RadioActivationSettings::updateControls() {
|
||
if (!m_loaded) return;
|
||
QString error = m_levelOff->value() >= m_levelOn->value()
|
||
? QCoreApplication::translate("ChatGui", "The stop threshold must be lower than the start threshold")
|
||
: RadioAudioEngine::instance()->settingsError();
|
||
m_error->setText(error); m_error->setVisible(!error.isEmpty());
|
||
m_levelMeter->on = m_levelOn->value(); m_levelMeter->off = m_levelOff->value();
|
||
m_vadMeter->on = m_threshold->value();
|
||
updateMeters();
|
||
emit changed();
|
||
}
|
||
|
||
void RadioActivationSettings::updateMeters() {
|
||
auto* engine = RadioAudioEngine::instance();
|
||
m_levelMeter->valid = engine->inputLevelValid(); m_levelMeter->level = engine->inputLevel(); m_levelMeter->update();
|
||
QString text;
|
||
if (!engine->isActive()) text = QCoreApplication::translate("ChatGui", "Enable radio to measure the microphone level");
|
||
else if (!engine->inputLevelValid()) text = QCoreApplication::translate("ChatGui", "Microphone level unavailable");
|
||
else text = QCoreApplication::translate("ChatGui", "Current level: %1 dBFS").arg(engine->inputLevel(), 0, 'f', 1);
|
||
m_levelText->setText(text); m_levelMeter->setAccessibleName(text);
|
||
m_vadMeter->valid = engine->vadValid(); m_vadMeter->level = engine->vadProbability() * 100; m_vadMeter->update();
|
||
if (engine->vadValid()) text = QCoreApplication::translate("ChatGui", "VAD speech probability: %1%").arg(engine->vadProbability() * 100, 0, 'f', 1);
|
||
else if (!engine->autoEnabled() || engine->autoMode() != 1)
|
||
text = QCoreApplication::translate("ChatGui", "Enable automatic transmission in VAD mode to see speech probability");
|
||
else if (engine->autoBlocked()) text = QCoreApplication::translate("ChatGui", "VAD measurement paused: channel busy");
|
||
else text = QCoreApplication::translate("ChatGui", "VAD speech probability unavailable");
|
||
m_vadText->setText(text); m_vadMeter->setAccessibleName(text);
|
||
if (!engine->vadValid()) text = QCoreApplication::translate("ChatGui", "Current VAD compressor gain: —");
|
||
else if (!engine->vadCompressorEnabled()) text = QCoreApplication::translate("ChatGui", "VAD compressor off (0 dB)");
|
||
else text = QCoreApplication::translate("ChatGui", "Current VAD compressor gain: %1 dB")
|
||
.arg((engine->vadGain() >= 0 ? QStringLiteral("+") : QString()) + QString::number(engine->vadGain(), 'f', 1));
|
||
m_gainText->setText(text);
|
||
}
|
||
|
||
RadioSettingsPage::RadioSettingsPage(DbManager* db, QWidget* parent) : QWidget(parent), m_db(db) {
|
||
auto* layout = new QVBoxLayout(this);
|
||
auto* title = new QLabel(QCoreApplication::translate("ChatGui", "Radio (PTT)"), this);
|
||
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
||
layout->addWidget(title);
|
||
m_activation = new RadioActivationSettings(false, this);
|
||
layout->addWidget(m_activation);
|
||
m_doublePress = new QCheckBox(QCoreApplication::translate("ChatGui", "Lock on double press"), this);
|
||
m_doublePress->setObjectName(QStringLiteral("radioPttDoublePress"));
|
||
m_doublePress->setToolTip(QCoreApplication::translate("ChatGui",
|
||
"Applies to the shortcut only. The PTT button always supports double-click locking."));
|
||
layout->addWidget(m_doublePress);
|
||
if (PttHotkeyManager::usesPortal()) {
|
||
auto* hint = new QLabel(QCoreApplication::translate("ChatGui",
|
||
"The global shortcut works from any application. Hold it to talk. When double-press locking is enabled, "
|
||
"press twice to lock transmission, then press again to release. Assign a regular key with Ctrl, Shift, Alt or "
|
||
"Win in the system dialog. Changes are saved by the system immediately. Keyboard "
|
||
"transmission is disabled during setup."), this);
|
||
hint->setWordWrap(true);
|
||
layout->addWidget(hint);
|
||
m_button = new QPushButton(QCoreApplication::translate("ChatGui", "Set PTT shortcut…"), this);
|
||
m_button->setObjectName(QStringLiteral("radioPttShortcut"));
|
||
layout->addWidget(m_button);
|
||
m_label = new QLabel(this);
|
||
m_label->setWordWrap(true); m_label->setTextFormat(Qt::PlainText);
|
||
layout->addWidget(m_label);
|
||
m_portalStatus = new QLabel(this);
|
||
m_portalStatus->setWordWrap(true);
|
||
m_portalStatus->setTextFormat(Qt::PlainText);
|
||
m_portalStatus->setStyleSheet("color: gray; font-size: 11px;");
|
||
layout->addWidget(m_portalStatus);
|
||
connect(m_button, &QPushButton::clicked, this, [this]() { PttHotkeyManager::instance()->configurePortal(this); });
|
||
connect(PttHotkeyManager::instance(), &PttHotkeyManager::portalChanged, this, &RadioSettingsPage::updatePortalLabels);
|
||
loadFromDb();
|
||
layout->addStretch(1);
|
||
return;
|
||
}
|
||
auto* hint = new QLabel(
|
||
QCoreApplication::translate("ChatGui",
|
||
"Hold a shortcut to transmit from any application, even when ChatGUI is not focused. "
|
||
"When double-press locking is enabled, press twice to lock transmission, then press again to release. "
|
||
"Channel radio must be enabled. To record a shortcut, hold the desired keys together and "
|
||
"release them. Left and right Ctrl, Shift, Alt and Win are distinct. Fn is available only "
|
||
"if the keyboard reports it to the OS."), this);
|
||
hint->setWordWrap(true);
|
||
hint->setStyleSheet("color: gray; font-size: 11px;");
|
||
layout->addWidget(hint);
|
||
if (!PttHotkeyManager::isSupported()) {
|
||
auto* warn = new QLabel(QCoreApplication::translate("ChatGui",
|
||
"Global shortcuts are available on Windows and Linux/X11. Shortcut recording is "
|
||
"unavailable on this platform."), this);
|
||
warn->setWordWrap(true);
|
||
warn->setStyleSheet("color: #e74c3c; font-size: 12px;");
|
||
layout->addWidget(warn);
|
||
}
|
||
auto* row = new QHBoxLayout();
|
||
m_button = new QPushButton(QCoreApplication::translate("ChatGui", "Set PTT shortcut…"), this);
|
||
m_button->setObjectName(QStringLiteral("radioPttShortcut"));
|
||
m_button->setEnabled(PttHotkeyManager::isSupported());
|
||
auto* clear = new QPushButton(QCoreApplication::translate("ChatGui", "Clear"), this);
|
||
clear->setObjectName(QStringLiteral("radioPttClear"));
|
||
row->addWidget(m_button); row->addWidget(clear);
|
||
layout->addLayout(row);
|
||
m_label = new QLabel(this);
|
||
m_label->setWordWrap(true);
|
||
m_label->setTextFormat(Qt::PlainText);
|
||
m_label->setStyleSheet("font-size: 13px; padding-bottom: 12px;");
|
||
layout->addWidget(m_label);
|
||
connect(m_button, &QPushButton::clicked, this, [this]() {
|
||
if (m_capturing) finishCapture(false); else startCapture();
|
||
});
|
||
connect(clear, &QPushButton::clicked, this, [this]() {
|
||
finishCapture(false);
|
||
m_chord.clear(); updateLabel();
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: shortcut draft cleared");
|
||
});
|
||
connect(PttHotkeyManager::instance(), &PttHotkeyManager::keysChanged, this, [this](const PttKeyChord& held) {
|
||
if (!m_capturing) return;
|
||
if (m_capture.observe(held)) finishCapture(true);
|
||
else if (!held.isEmpty())
|
||
m_label->setText(PttHotkeyManager::instance()->describe(m_capture.candidate));
|
||
});
|
||
layout->addStretch(1);
|
||
loadFromDb();
|
||
}
|
||
|
||
RadioSettingsPage::~RadioSettingsPage() {
|
||
finishCapture(false);
|
||
if (m_portalStatus && isVisible()) PttHotkeyManager::instance()->inhibitPortal(false);
|
||
}
|
||
|
||
void RadioSettingsPage::startCapture() {
|
||
finishCapture(false);
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: shortcut recording requested");
|
||
if (!PttHotkeyManager::instance()->beginCapture()) {
|
||
m_label->setText(QCoreApplication::translate("ChatGui", "Failed to start key recording. See the radio log for details."));
|
||
return;
|
||
}
|
||
m_capturing = true;
|
||
m_capture.candidate.clear();
|
||
m_button->setText(QCoreApplication::translate("ChatGui", "Cancel recording"));
|
||
m_label->setText(QCoreApplication::translate("ChatGui", "Press one or more keys together…"));
|
||
qApp->installEventFilter(this);
|
||
}
|
||
|
||
void RadioSettingsPage::finishCapture(bool save) {
|
||
if (!m_capturing) return;
|
||
if (save) {
|
||
m_chord = m_capture.candidate;
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: captured shortcut=[%s] codes=[%s]",
|
||
qPrintable(PttHotkeyManager::instance()->describe(m_capture.candidate)), qPrintable(pttEncodeChord(m_capture.candidate)));
|
||
} else {
|
||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: recording cancelled candidate=[%s]",
|
||
qPrintable(pttEncodeChord(m_capture.candidate)));
|
||
}
|
||
m_capturing = false;
|
||
qApp->removeEventFilter(this);
|
||
PttHotkeyManager::instance()->endCapture();
|
||
m_button->setText(QCoreApplication::translate("ChatGui", "Set PTT shortcut…"));
|
||
updateLabel();
|
||
}
|
||
|
||
bool RadioSettingsPage::eventFilter(QObject* object, QEvent* event) {
|
||
if (m_capturing) {
|
||
if (event->type() == QEvent::ShortcutOverride) { event->accept(); return true; }
|
||
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
|
||
auto* key = static_cast<QKeyEvent*>(event);
|
||
// Только ручная запись: сравниваем доставку событий окну с независимым опросом ОС.
|
||
if (!key->isAutoRepeat()) {
|
||
DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "PTT: capture window event=%s qt=0x%x native_vk=%u scan=%u modifiers=0x%x",
|
||
event->type() == QEvent::KeyPress ? "press" : "release",
|
||
unsigned(key->key()), unsigned(key->nativeVirtualKey()), unsigned(key->nativeScanCode()),
|
||
unsigned(key->nativeModifiers()));
|
||
#ifdef Q_OS_WIN
|
||
int vk = int(key->nativeVirtualKey());
|
||
if (vk > 0 && vk < 256) {
|
||
unsigned asyncState = static_cast<unsigned short>(GetAsyncKeyState(vk));
|
||
unsigned queueState = static_cast<unsigned short>(GetKeyState(vk));
|
||
DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "PTT: capture Windows vk=%d async=0x%04x queue=0x%04x remote_session=%d",
|
||
vk, asyncState, queueState, GetSystemMetrics(SM_REMOTESESSION));
|
||
}
|
||
#endif
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
return QWidget::eventFilter(object, event);
|
||
}
|
||
|
||
void RadioSettingsPage::showEvent(QShowEvent* event) {
|
||
if (m_portalStatus) PttHotkeyManager::instance()->inhibitPortal(true);
|
||
QWidget::showEvent(event);
|
||
}
|
||
|
||
void RadioSettingsPage::hideEvent(QHideEvent* event) {
|
||
if (m_portalStatus) PttHotkeyManager::instance()->inhibitPortal(false);
|
||
finishCapture(false);
|
||
QWidget::hideEvent(event);
|
||
}
|
||
|
||
void RadioSettingsPage::updateLabel() {
|
||
m_label->setText(PttHotkeyManager::instance()->describe(m_chord));
|
||
}
|
||
|
||
void RadioSettingsPage::loadFromDb() {
|
||
m_activation->loadSettings();
|
||
m_doublePress->setChecked(!m_db || !m_db->isOpen() || m_db->getUiState("radio_ptt_double_press") != "0");
|
||
if (m_portalStatus) { updatePortalLabels(); return; }
|
||
finishCapture(false);
|
||
m_chord = m_db && m_db->isOpen() ? pttDecodeChord(m_db->getUiState("radio_ptt_hotkey")) : PttKeyChord();
|
||
updateLabel();
|
||
}
|
||
|
||
QString RadioSettingsPage::hotkey() const { return pttEncodeChord(m_chord); }
|
||
bool RadioSettingsPage::doublePressEnabled() const { return m_doublePress->isChecked(); }
|
||
|
||
bool RadioSettingsPage::apply() {
|
||
if (!m_activation->apply()) return false;
|
||
PttHotkeyManager::instance()->setDoublePressEnabled(doublePressEnabled());
|
||
if (m_portalStatus) return true;
|
||
finishCapture(false);
|
||
PttHotkeyManager::instance()->setHotkey(hotkey());
|
||
return true;
|
||
}
|
||
|
||
void RadioSettingsPage::updatePortalLabels() {
|
||
auto* manager = PttHotkeyManager::instance();
|
||
QString text = manager->portalDescription();
|
||
m_label->setText(QCoreApplication::translate("ChatGui", "PTT shortcut: %1")
|
||
.arg(text.isEmpty() ? QCoreApplication::translate("ChatGui", "Not set") : text));
|
||
m_portalStatus->setText(manager->portalStatus());
|
||
m_button->setEnabled(!manager->portalBusy());
|
||
}
|