diff --git a/tools/chatgui/src/accountdelegate.cpp b/tools/chatgui/src/accountdelegate.cpp index 5bab2e16..c7da53ef 100644 --- a/tools/chatgui/src/accountdelegate.cpp +++ b/tools/chatgui/src/accountdelegate.cpp @@ -148,7 +148,9 @@ void AccountDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio const QRect radio = actionRect(option.rect, Action::RadioCall); const QColor radioColor = flags & CHAT_MEMBER_FLAG_RADIO ? QColor("#4CAF50") : color; FlagPainter::drawRadio(painter, radio.center().x() - 12, radio.center().y() - 12, 24, radioColor); - drawCallButton(painter, actionRect(option.rect, Action::Call), color); + const bool known = index.data(MemberListModel::OnlineRole).toBool() && (flags & CHAT_MEMBER_FLAG_PHONE_KNOWN); + const QColor phoneColor = known ? QColor(flags & CHAT_MEMBER_FLAG_PHONE_BUSY ? "#E53935" : "#4CAF50") : color; + drawCallButton(painter, actionRect(option.rect, Action::Call), phoneColor); } painter->restore(); } @@ -200,6 +202,11 @@ bool AccountDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, cons QString text = QCoreApplication::translate("ChatGui", action == Action::RadioCall ? "Call by radio" : "Call peer"); if (action == Action::RadioCall && (index.data(Qt::UserRole + 3).toInt() & CHAT_MEMBER_FLAG_RADIO)) text += QCoreApplication::translate("ChatGui", " · Radio enabled"); + if (action == Action::Call && index.data(MemberListModel::OnlineRole).toBool()) { + const int flags = index.data(Qt::UserRole + 3).toInt(); + text += " · " + QCoreApplication::translate("ChatGui", !(flags & CHAT_MEMBER_FLAG_PHONE_KNOWN) ? "Phone status unknown" : + (flags & CHAT_MEMBER_FLAG_PHONE_BUSY) ? "Busy on a call" : "Phone is free"); + } if (!index.data(MemberListModel::OnlineRole).toBool()) text += " · " + QCoreApplication::translate("ChatGui", "offline"); QToolTip::showText(event->globalPos(), text, view); return true; diff --git a/tools/chatgui/src/callwindow.cpp b/tools/chatgui/src/callwindow.cpp index e5f0e04c..d29b2b1f 100644 --- a/tools/chatgui/src/callwindow.cpp +++ b/tools/chatgui/src/callwindow.cpp @@ -257,6 +257,15 @@ CallWindow::CallWindow(QWidget* parent) lay->addWidget(m_avatarLabel, 0, Qt::AlignHCenter); lay->addWidget(m_peerLabel); lay->addWidget(m_statusLabel); + m_continueBtn = new QPushButton(callText("Call anyway"), this); + lay->addWidget(m_continueBtn); + connect(m_continueBtn, &QPushButton::clicked, this, [this] { + if (m_state != WaitDecision) return; + DEBUG_INFO(DEBUG_CATEGORY_CALL, "desktop caller overrides phone busy id=%016llx", (unsigned long long)m_callId); + postCallFn(call_continue_trampoline, m_callId); + setState(Outgoing); + SoundManager::instance()->playLoop("call_connecting"); + }); lay->addWidget(m_durationLabel); lay->addWidget(m_connectionLabel); lay->addWidget(m_bgpPathLabel); @@ -389,6 +398,10 @@ void CallWindow::setConnectionType(uint8_t type, quint64 nextHop) { void CallWindow::setState(State s) { m_state = s; switch (s) { + case WaitDecision: + m_statusLabel->setText(callText("Subscriber is on a phone call")); + m_durationLabel->hide(); + break; case Outgoing: m_statusLabel->setText(QCoreApplication::translate("ChatGui", "Connecting…")); m_durationLabel->hide(); @@ -439,9 +452,10 @@ void CallWindow::populateDeviceCombo() { } void CallWindow::updateButtons() { + m_continueBtn->setVisible(m_state == WaitDecision); m_acceptCell->setVisible(m_state == Incoming); m_declineCell->setVisible(m_state == Incoming); - m_hangupCell->setVisible(m_state == Outgoing || m_state == Ringing || m_state == Active); + m_hangupCell->setVisible(m_state == Outgoing || m_state == Ringing || m_state == Active || m_state == WaitDecision); m_muteCell->setVisible(m_state == Active); updateVideoPresentation(); } @@ -509,7 +523,7 @@ void CallWindow::onDeviceChanged(int index) { void CallWindow::closeEvent(QCloseEvent* event) { m_video->stop(); m_videoCall = 0; - if (m_state == Outgoing || m_state == Ringing || m_state == Active) + if (m_state == Outgoing || m_state == Ringing || m_state == Active || m_state == WaitDecision) postCallFn(call_hangup_trampoline, m_callId); emit closed(m_callId); event->accept(); diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index defd87bd..7b92fffc 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -571,6 +571,14 @@ void MainWindow::setupBridgeCallbacks() { gui_bridge_set_call_ringing_cb([](uint64_t call_id) { if (s_mainWindow) s_mainWindow->onCallRinging(call_id); }); + gui_bridge_set_call_phone_busy_cb([](uint64_t call_id) { + if (!s_mainWindow) return; + auto* window = s_mainWindow->m_activeCalls.value(call_id, nullptr); + if (!window || window->state() != CallWindow::Outgoing) return; + SoundManager::instance()->stopLoop(); + window->setState(CallWindow::WaitDecision); + window->show(); window->raise(); + }); gui_bridge_set_call_accepted_cb([](uint64_t call_id) { if (s_mainWindow) s_mainWindow->onCallAccepted(call_id); }); @@ -767,7 +775,7 @@ void MainWindow::onRadioCallRequested(const QString& channel, quint64 peer) { /* Один диалог на незавершённое приглашение; принятая рация живёт своим обычным циклом. */ void MainWindow::onRadioCallEvent(const uint8_t* data, int len) { - if (!data || len != 27 || data[24] < RADIO_CALL_OUTGOING || data[24] > RADIO_CALL_FINISHED || + if (!data || len != 27 || data[24] < RADIO_CALL_OUTGOING || data[24] > RADIO_CALL_WAIT_DECISION || data[25] > RADIO_CALL_AUDIO_ERROR || data[26] > 1) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "desktop invalid radio call event bytes=%d", len); return; } @@ -807,6 +815,13 @@ void MainWindow::onRadioCallEvent(const uint8_t* data, int len) { } return; } + if (state == RADIO_CALL_WAIT_DECISION && incoming) return; + if (state == RADIO_CALL_WAIT_DECISION && m_radioCallId == id) { + m_radioCallId = 0; + SoundManager::instance()->stopLoop(); + if (m_radioCallWindow) m_radioCallWindow->close(); + m_radioCallWindow = nullptr; + } if (m_radioCallId == id) return; if (m_radioCallId || !m_activeCalls.isEmpty()) { postRadioCall(id, RADIO_CALL_REJECT_BUSY); return; } QString name = m_db->getDisplayName(peer); @@ -830,6 +845,17 @@ void MainWindow::onRadioCallEvent(const uint8_t* data, int len) { bool ready = m_currentChannelId == channel && m_messageList->enableRadio(); postRadioCall(id, ready ? RADIO_CALL_ACCEPT : RADIO_CALL_FAIL); }); + } else if (state == RADIO_CALL_WAIT_DECISION) { + box->setInformativeText(QCoreApplication::translate("ChatGui", "Subscriber is on a phone call")); + auto* proceed = box->addButton(QCoreApplication::translate("ChatGui", "Call anyway"), QMessageBox::AcceptRole); + box->addButton(QCoreApplication::translate("ChatGui", "End call"), QMessageBox::RejectRole); + connect(box, &QMessageBox::buttonClicked, this, [this, id, proceed](QAbstractButton* button) { + if (m_radioCallId != id || button != proceed) return; + m_radioCallAnswered = true; + m_radioCallId = 0; + DEBUG_INFO(DEBUG_CATEGORY_RADIO, "desktop caller overrides phone busy id=%016llx", (unsigned long long)id); + postRadioCall(id, RADIO_CALL_CONTINUE); + }); } else { box->addButton(QMessageBox::Cancel); box->setInformativeText(QCoreApplication::translate("ChatGui", "Checking subscriber availability…")); @@ -842,7 +868,7 @@ void MainWindow::onRadioCallEvent(const uint8_t* data, int len) { } }); box->show(); box->raise(); box->activateWindow(); - SoundManager::instance()->playLoop(incoming ? "radio_incoming" : "call_connecting"); + if (state != RADIO_CALL_WAIT_DECISION) SoundManager::instance()->playLoop(incoming ? "radio_incoming" : "call_connecting"); if (incoming) { if (m_trayIcon && (!isVisible() || isMinimized())) m_trayIcon->showMessage(QCoreApplication::translate("ChatGui", "Radio call"), box->text());