Internal
Public Access
File > Quick Connect... and a matching Profiles-tab button open ProfileDialog in a new quick-connect mode (Name/Tags/Folder hidden, Name validation skipped) and hand the resulting in-memory profile straight to a session tab -- nothing is written to the profile database. The tab title is derived from host/username/protocol for display only. A Quick Connect tab's context menu gets a "Save as Profile..." action, shown only on tabs that were never saved, that opens the normal profile dialog pre-filled to persist it afterward. Bump version to v2026.9.28. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
197 lines
7.0 KiB
C++
197 lines
7.0 KiB
C++
#ifndef ORBITHUB_SESSION_TAB_H
|
|
#define ORBITHUB_SESSION_TAB_H
|
|
|
|
#include "profile_repository.h"
|
|
#include "session_backend.h"
|
|
|
|
#include <QWidget>
|
|
#include <QStringList>
|
|
#include <QtGlobal>
|
|
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
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<void(std::optional<QString>)> m_passwordPromptCallback;
|
|
bool m_awaitingUserInput;
|
|
enum class EventSeverity {
|
|
Info,
|
|
Warning,
|
|
Error,
|
|
};
|
|
struct EventEntry {
|
|
QString line;
|
|
EventSeverity severity;
|
|
};
|
|
std::vector<EventEntry> m_eventEntries;
|
|
QString m_eventFilter;
|
|
EventSeverity m_eventSeverityFilter;
|
|
bool m_eventsPanelExpanded;
|
|
|
|
void setupUi();
|
|
void requestConnectOptions(std::function<void(std::optional<SessionConnectOptions>)> callback);
|
|
void showPasswordPrompt(const QString& labelText,
|
|
std::function<void(std::optional<QString>)> 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
|