#ifndef ORBITHUB_SESSION_TAB_H #define ORBITHUB_SESSION_TAB_H #include "profile_repository.h" #include "session_backend.h" #include #include #include #include #include #include class QPlainTextEdit; class QThread; class SessionBackend; class TerminalView; class RdpDisplayWidget; class VncDisplayWidget; class QToolButton; class QLineEdit; class QComboBox; class QLabel; class QPushButton; class QScrollArea; class QShowEvent; class KodoTerm; struct SessionUiPreferences { QString terminalThemeName = QStringLiteral("Dark"); bool eventsPanelExpanded = false; int terminalFontPointSize = 0; bool vncScaleToFit = true; }; class SessionTab : public QWidget { Q_OBJECT public: explicit SessionTab(const Profile& profile, const SessionUiPreferences& preferences, QWidget* parent = nullptr); ~SessionTab() override; QString tabTitle() const; void connectSession(); void disconnectSession(); void reconnectSession(); void clearTerminal(); void setTerminalThemeName(const QString& themeName); QString terminalThemeName() const; bool supportsThemeSelection() const; bool supportsClearAction() const; bool supportsZoom() const; void zoomIn(); void zoomOut(); void resetZoom(); void setTerminalFontPointSize(int pointSize); int terminalFontPointSize() const; bool isEventsPanelExpanded() const; void setEventsPanelExpanded(bool expanded); void clearEvents(); void copyEvents() const; void exportEventsToFile(); bool supportsVncScaleToggle() const; void setVncScaleToFit(bool scaleToFit); bool vncScaleToFit() const; bool awaitingUserInput() const; // True if this tab's profile was never saved to the repository (i.e. // it came from Quick Connect, issue #23, rather than a saved profile). // ProfileDialog::profile() always returns id <= 0 for an unsaved // profile, and Quick Connect is the only path that hands such a // profile to SessionWindow::addSessionTab() -- every other path looks // the profile up from the repository first, which returns id > 0. bool isQuickConnect() const; Profile profileForSaving() const; signals: void tabTitleChanged(const QString& title); void tabStateChanged(SessionState state); // Fires whenever the inline username/password prompt bar is shown or // hidden -- independent of tabStateChanged(), since the backend is // still just "Connecting" while it's up. Lets SessionWindow mark a // background tab that needs the user's attention (issue #22). void awaitingUserInputChanged(bool waiting); void terminalThemeChanged(const QString& themeName); void terminalFontSizeChanged(int pointSize); void eventsPanelVisibilityChanged(bool expanded); void vncScaleModeChanged(bool scaleToFit); void requestConnect(const SessionConnectOptions& options); void requestDisconnect(); void requestReconnect(const SessionConnectOptions& options); void requestInput(const QString& input); void requestHostKeyConfirmation(bool trustHost); void requestTerminalSize(int columns, int rows); void requestDisplayScale(qreal devicePixelRatio); void requestKeyEvent(int key, quint32 nativeScanCode, const QString& text, bool pressed, int modifiers); void requestMouseMoveEvent(int x, int y); void requestMouseButtonEvent(int x, int y, int button, bool pressed); void requestMouseWheelEvent(int x, int y, int deltaX, int deltaY); void requestSetClipboardText(const QString& text); void requestProvideUsername(const QString& username); protected: // A tab that wasn't in front while the local clipboard changed missed // that change (only the visible tab syncs), so catch it up on show. void showEvent(QShowEvent* event) override; private slots: void onBackendStateChanged(SessionState state, const QString& message); void onBackendEventLogged(const QString& message); void onBackendConnectionError(const QString& displayMessage, const QString& rawMessage); void onBackendOutputReceived(const QString& text); void onBackendHostKeyConfirmationRequested(const QString& prompt); void onBackendUsernameRequested(const QString& prompt); void onBackendRemoteClipboardTextChanged(const QString& text); void onSystemClipboardChanged(); private: Profile m_profile; QThread* m_backendThread; SessionBackend* m_backend; bool m_useKodoTermForSsh; SessionState m_state; QString m_lastError; SessionConnectOptions m_lastConnectOptions; QString m_terminalThemeName; int m_terminalFontPointSize; // Text we just wrote to the system clipboard from a remote update, so // the dataChanged() it triggers isn't echoed straight back out. Only // honored briefly -- see onSystemClipboardChanged(). QString m_pendingClipboardEcho; qint64 m_pendingClipboardEchoMs = 0; bool m_clipboardSyncSupported; KodoTerm* m_sshTerminal; RdpDisplayWidget* m_rdpDisplay; VncDisplayWidget* m_vncDisplay; QScrollArea* m_vncScrollArea; TerminalView* m_terminalOutput; QPlainTextEdit* m_eventLog; QToolButton* m_toggleEventsButton; QLineEdit* m_eventFilterInput; QComboBox* m_eventSeverityFilterInput; QToolButton* m_clearEventsButton; QToolButton* m_exportEventsButton; QWidget* m_eventsPanel; QWidget* m_passwordPromptBar; QLabel* m_passwordPromptIcon; QLabel* m_passwordPromptLabel; QLineEdit* m_passwordPromptInput; QPushButton* m_passwordPromptConnectButton; QPushButton* m_passwordPromptCancelButton; std::function)> m_passwordPromptCallback; bool m_awaitingUserInput; enum class EventSeverity { Info, Warning, Error, }; struct EventEntry { QString line; EventSeverity severity; }; std::vector m_eventEntries; QString m_eventFilter; EventSeverity m_eventSeverityFilter; bool m_eventsPanelExpanded; void setupUi(); void requestConnectOptions(std::function)> callback); void showPasswordPrompt(const QString& labelText, std::function)> callback, bool maskInput = true); void hidePasswordPrompt(); void pushLocalClipboardToRemote(); bool validateProfileForConnect(); void appendEvent(const QString& message); void setState(SessionState state, const QString& message); QString stateSuffix() const; void refreshActionButtons(); void setPanelExpanded(QToolButton* button, QWidget* panel, const QString& name, bool expanded); bool startSshTerminal(const SessionConnectOptions& options); void applyTerminalTheme(const QString& themeName); void refreshEventLogView(); static EventSeverity classifyEventSeverity(const QString& message); }; #endif