Files
utun2/tools/chatgui/src/mainwindow.h
T

200 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Главное окно desktop-чата: связывает списки, bridge-события, UtunNode, media и звонки.
* Координирует запуск/остановку ядра, tray и отдельные окна; UI-обработчики работают в GUI-потоке. */
#pragma once
#include <QMainWindow>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QQueue>
#include <QSet>
#include <QHash>
#include <QTimer>
#include <QPointer>
#include <QPair>
#include <memory>
class ChannelList;
class MessageList;
class AccountList;
class DbManager;
class QSplitter;
class UtunNode;
class AudioRecorder;
class VideoPlayerWindow;
class StreamPlayerWindow;
class StreamGridWindow;
class StreamPublishEngine;
class StreamPublishWindow;
class ImageViewerWindow;
class CallWindow;
class CallAudioEngine;
class MemberFileBrowser;
class MessageNotification;
class QMessageBox;
struct MessageRow;
struct AutoPlayItem {
QString channelId; // Канал снимка; msgId уникален только внутри него.
qint64 msgId = 0; // Локальный rowid сообщения.
bool downloadStarted = false; // Команда загрузки принята очередью; false допускает первую попытку.
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
// db заимствован до деструктора; nullptr создаёт UI без UtunNode. cfgPath — профиль, dbPath — override каталога.
explicit MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
const QString& dbPath = QString(),
const QString& debugFile = QString(),
const QString& debugLevel = QString(),
const QString& debugCategories = QString());
// Снять callbacks, остановить media, stop/join node и очистить анимации.
~MainWindow() override;
protected:
// Сохранить геометрию и скрыть в tray; для выхода служит requestShutdown.
void closeEvent(QCloseEvent *event) override;
private slots:
void onTrayActivated(QSystemTrayIcon::ActivationReason reason);
void showShareInviteDialog(const QString& channelId);
void showInviteByLinkDialog(const QString& channelId);
void showJoinDialog();
void onCreateGroupRequested();
void showSettings();
void openVideo(const QString& filePath);
void openImage(const QString& filePath);
void showChannelSettings(const QString& channelId);
// Дождаться конца/подготовки player, разрешить путь головы и запустить один voice.
void autoPlayTimerTick();
public:
// ch_id задан с длиной; initialSync обновляет историю без уведомления, звука и auto-play.
void onMessageReceived(const char* ch_id, int ch_id_len, uint64_t author, bool initialSync);
// Передать бинарный CHAT_MEMBER_DISPLAY снимок списку участников и кэшу аватаров.
void onMemberList(const QString& chId, int count, const uint8_t* data, int dataLen);
// Обновить участника по codec chat_member.h; байты заимствованы до возврата.
void onMemberUpdated(const QString& chId, const uint8_t* data);
// Удалить nodeId из модели/кэша выбранного chId.
void onMemberRemoved(const QString& chId, uint64_t nodeId);
// Обновить статус поиска соседей канала.
void onAutoConnectStatus(uint8_t status, uint16_t total, uint16_t totalNodes, uint16_t connected);
// Обновить online-счётчик ch_id указанной длины.
void onChannelPeersOnline(const char* ch_id, int ch_id_len, uint16_t online);
// Перечитать список, сбросить исчезнувший target и применить отложенный выбор.
void reloadChannels();
// Завершение (channel,rowid): разбудить очередь, проверить готовый encrypted voice.
void onAttachmentDownloadedForAutoPlay(const QString& chId, int64_t msgId);
// Сохранить geometry/read/scroll и поставить stop после этих команд; OOM допускает прямой stop с логом.
void requestShutdown();
// Заимствованный QObject-владелец ядра либо nullptr; управление — в GUI.
UtunNode* utunNode() const { return m_node; }
/* звонки */
// Создать исходящее окно после принятия START мостом; chId может быть dm:<conv_id>.
void onCallRequested(const QString& chId, quint64 nodeId, const QString& name);
// Показать входящий callId с именем caller из DbManager; повтор ID не создаёт окно.
void onCallIncoming(const QString& chId, quint64 caller, quint64 callId);
// Обновить состояние существующего исходящего окна и тон.
void onCallRinging(quint64 callId);
// Активировать callId/audio; при отказе capture послать hangup.
void onCallAccepted(quint64 callId);
// Завершить audio/окно, при PHONE_BUSY показать причину.
void onCallEnded(quint64 callId, uint8_t reason);
// Залогировать протокольную ошибку и закрыть окно callId.
void onCallError(quint64 callId, uint8_t err);
// Обновить диагностический BGP-путь существующего окна.
void onCallPath(quint64 callId, const QString& path);
// Снять callId из map до deleteLater; повтор безопасен.
void closeCallWindow(quint64 callId);
// Передать START для CHAT-канала и peer с новым ненулевым ID.
void onRadioCallRequested(const QString& channel, quint64 peer);
// Применить 27-байтный radio_call event, управлять диалогом/рингтоном и READY/ACCEPT.
void onRadioCallEvent(const uint8_t* data, int len);
/* DM (p2p чат) */
// Скопировать общую группу/peer/имя в команду; PM выбирается после DM_CONV_UPDATED.
void onPmRequested(const QString& chId, quint64 nodeId, const QString& name);
// Открыть отправку свежего invite фиксированному peer; source может быть пустым из PM.
void onInviteToPmRequested(const QString& source, quint64 peer, const QString& name);
// Разобрать utun/utun-msg ссылку и перейти к доступной группе/оригиналу.
void onMessageLinkActivated(const QString& link);
// Выбрать только видимое совпавшее поколение истории PM.
void onDmConvUpdated(const QString& convId, quint64 generation);
// Обновить PM/уведомление; выбранной беседе запросить снимок.
void onDmMsgReceived(const QString& convId, quint64 author);
// Подтвердить удаление конкретного generation и заблокировать его composer.
void onDmDeleteRequested(const QString& convId, quint64 generation, const QString& name);
// Применить только зарегистрированный request/conv/generation без очистки новой истории.
void onDmDeleteResult(quint64 request, const QString& convId, quint64 generation, bool success);
private:
void setupUi();
// Поставить owned geometry/maximized/splitter в ui_state.
void saveWindowGeometry();
// Восстановить размеры в пределах доступных экранов.
void restoreWindowGeometry();
void setupTray();
void showIncomingMessageNotification(const QString& target, quint64 author);
// Запустить UtunNode; stopping отсоединяет SQLite до native teardown.
void setupNode();
void setupMessaging();
void setupSoundFromConfig();
// Один MainWindow получает bridge events в GUI; деструктор снимает callbacks.
void setupBridgeCallbacks();
// Связать списки/composer/окна с командами и локальными действиями.
void setupConnects();
void onShutdownCheck();
// Проверить последнюю запись или msgId>0; повторный/свой voice не добавлять.
void autoPlayCheckNewMessage(const QString& channelId, qint64 msgId = 0);
QString autoPlayMediaDir() const;
// Путь обычного voice/проверенного plaintext VOICE; неизвестный encrypted MIME даёт пусто.
QString autoPlayFilePath(const QString& channelId, const MessageRow& message) const;
// Открыть CHAT browser с owner/общим списком; DM не поддерживается.
void showMemberFiles(const QString& channel, quint64 owner = 0);
void showStreamPublisher(quint64 group, StreamGridWindow* returnTo = nullptr); // Управление; закрытие возвращает фокус сетке.
// Qt-дети главного окна: списки, splitter, tray/menu. DbManager — внешний заимствованный QObject.
ChannelList *m_channelList;
MessageList *m_messageList;
AccountList *m_accountList;
QSplitter *m_splitter = nullptr;
QSystemTrayIcon *m_trayIcon;
QMenu *m_trayMenu;
std::unique_ptr<MessageNotification> m_messageNotification; // Единственное transient-уведомление.
DbManager *m_db;
UtunNode *m_node = nullptr; // Собственный QObject: worker join до удаления.
// INI/override каталога и настройки общего logger, передаваемые до старта node.
QString m_cfgPath;
QString m_cfgDbPath;
QString m_cfgDebugFile;
QString m_cfgDebugLevel;
QString m_cfgDebugCategories;
QString m_pendingChannelSelect; // Выбор после появления канала/PM в SQLite.
QString m_currentChannelId; // Decimal CHAT id или dm:<conv_id>, пусто без выбора.
quint64 m_dmDeleteSequence = 0; // Локальная корреляция подтверждённых удалений.
QHash<quint64, QPair<QString, quint64>> m_dmDeleteRequests; /* request_id → conv_id и удаляемое поколение */
AudioRecorder *m_recorder = nullptr; // Qt child, shutdown до остановки ядра.
// Отдельные media windows; raw pointers обнуляются destroyed/closed callbacks, QPointer — автоматически.
VideoPlayerWindow *m_videoPlayer = nullptr;
QPointer<StreamPlayerWindow> m_streamPlayer;
QPointer<StreamGridWindow> m_streamGrid;
StreamPublishEngine* m_streamPublisher = nullptr; // Собственный publisher, удаляется до node.
QPointer<StreamPublishWindow> m_streamPublishWindow;
ImageViewerWindow *m_imageViewer = nullptr;
QPointer<MemberFileBrowser> m_fileBrowser;
QQueue<AutoPlayItem> m_autoPlayQueue; // Последовательный playback чужих voice, ожидание загрузки головы.
QTimer* m_autoPlayTimer = nullptr; // 200ms, активен при непустой очереди.
QTimer* m_cpuLoadTimer = nullptr; // GUI polling 333ms.
bool m_cpuLoadPending = false; // Не более одного ожидающего CPU snapshot.
QTimer* m_shutdownTimer = nullptr; // POSIX: проверка signal flag, 200ms.
QSet<QPair<QString, qint64>> m_autoPlayDownloading; // (channel,rowid) ожидающей загрузки.
QHash<QString, qint64> m_lastAutoPlayId; // Подавляет повторное добавление последнего сообщения канала.
QHash<quint64, CallWindow*> m_activeCalls; /* call_id → окно звонка */
QPointer<QMessageBox> m_radioCallWindow; // Один адресный вызов в рацию; диалог удаляется при закрытии.
quint64 m_radioCallId = 0; // 0 без незавершённого приглашения.
bool m_radioCallAnswered = false; // finished не дублирует уже отправленное решение кнопки.
};