#include #include #include #include #include #include #include #include REGISTER_DOCK_WIDGET(LogsWidget, "Logs", true) LogsWidget::LogsWidget(const AdaptixWidget* w) : DockTab("Logs", w->GetProfile()->GetProject(), ":/icons/logs") { this->createUI(); connect(searchLineEdit, &QLineEdit::returnPressed, this, &LogsWidget::handleSearch); connect(nextButton, &ClickableLabel::clicked, this, &LogsWidget::handleSearch); connect(prevButton, &ClickableLabel::clicked, this, &LogsWidget::handleSearchBackward); connect(hideButton, &ClickableLabel::clicked, this, &LogsWidget::toggleSearchPanel); connect(logsConsoleTextEdit, &TextEditConsole::ctx_find, this, &LogsWidget::toggleSearchPanel); shortcutSearch = new QShortcut(QKeySequence("Ctrl+F"), logsConsoleTextEdit); shortcutSearch->setContext(Qt::WidgetShortcut); connect(shortcutSearch, &QShortcut::activated, this, &LogsWidget::toggleSearchPanel); shortcutSearch = new QShortcut(QKeySequence("Ctrl+L"), logsConsoleTextEdit); shortcutSearch->setContext(Qt::WidgetShortcut); connect(shortcutSearch, &QShortcut::activated, logsConsoleTextEdit, &QPlainTextEdit::clear); shortcutSearch = new QShortcut(QKeySequence("Ctrl+A"), logsConsoleTextEdit); shortcutSearch->setContext(Qt::WidgetShortcut); connect(shortcutSearch, &QShortcut::activated, logsConsoleTextEdit, &QPlainTextEdit::selectAll); connect(&ConsoleThemeManager::instance(), &ConsoleThemeManager::themeChanged, this, &LogsWidget::applyTheme); connect(logsConsoleTextEdit, &TextEditConsole::ctx_bgToggled, this, [this](bool){ applyTheme(); }); applyTheme(); this->dockWidget->setWidget(this); } LogsWidget::~LogsWidget() = default; void LogsWidget::createUI() { searchWidget = new QWidget(this); searchWidget->setVisible(false); prevButton = new ClickableLabel("<"); prevButton->setCursor( Qt::PointingHandCursor ); nextButton = new ClickableLabel(">"); nextButton->setCursor( Qt::PointingHandCursor ); searchLabel = new QLabel("0 of 0"); searchLineEdit = new QLineEdit(); searchLineEdit->setPlaceholderText("Find"); searchLineEdit->setMaximumWidth(300); hideButton = new ClickableLabel("X"); hideButton->setCursor( Qt::PointingHandCursor ); spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); searchLayout = new QHBoxLayout(searchWidget); searchLayout->setContentsMargins(0, 3, 0, 0); searchLayout->setSpacing(4); searchLayout->addWidget(prevButton); searchLayout->addWidget(nextButton); searchLayout->addWidget(searchLabel); searchLayout->addWidget(searchLineEdit); searchLayout->addWidget(hideButton); searchLayout->addSpacerItem(spacer); logsConsoleTextEdit = new TextEditConsole(this); logsConsoleTextEdit->setReadOnly(true); logsConsoleTextEdit->setStyleSheet("background-color: #151515; color: #BEBEBE; border: 1px solid #2A2A2A; border-radius: 4px;"); logsConsoleTextEdit->setAutoScrollEnabled(true); logsGridLayout = new QGridLayout(this); logsGridLayout->setContentsMargins(1, 1, 1, 1); logsGridLayout->setVerticalSpacing(1); logsGridLayout->addWidget( searchWidget, 0, 0, 1, 1); logsGridLayout->addWidget( logsConsoleTextEdit, 1, 0, 1, 1); logsWidget = new QWidget(this); logsWidget->setLayout(logsGridLayout); /// ToDo: todo list + sync chat // todoLabel = new QLabel(this); // todoLabel->setText("ToDo notes"); // todoLabel->setAlignment(Qt::AlignCenter); // // todoGridLayout = new QGridLayout(this); // todoGridLayout->setContentsMargins(1, 1, 1, 1); // todoGridLayout->setVerticalSpacing(1); // todoGridLayout->setHorizontalSpacing(2); // // todoGridLayout->addWidget(todoLabel, 0, 0, 1, 1); // // todoWidget = new QWidget(this); // todoWidget->setLayout(todoGridLayout); mainHSplitter = new QSplitter( Qt::Horizontal, this ); mainHSplitter->setHandleWidth(3); mainHSplitter->addWidget(logsWidget); mainGridLayout = new QGridLayout( this ); mainGridLayout->setContentsMargins(0, 0, 0, 0); mainGridLayout->setVerticalSpacing(0); mainGridLayout->addWidget( mainHSplitter, 0, 0, 1, 1); this->setLayout( mainGridLayout ); } void LogsWidget::SetUpdatesEnabled(const bool enabled) { logsConsoleTextEdit->setUpdatesEnabled(enabled); } void LogsWidget::findAndHighlightAll(const QString &pattern) { allSelections.clear(); QTextCursor cursor(logsConsoleTextEdit->document()); cursor.movePosition(QTextCursor::Start); QTextCharFormat baseFmt; baseFmt.setBackground(Qt::blue); baseFmt.setForeground(Qt::white); while (true) { auto found = logsConsoleTextEdit->document()->find(pattern, cursor); if (found.isNull()) break; QTextEdit::ExtraSelection sel; sel.cursor = found; sel.format = baseFmt; allSelections.append(sel); cursor = found; } logsConsoleTextEdit->setExtraSelections(allSelections); } void LogsWidget::highlightCurrent() const { if (allSelections.isEmpty()) { searchLabel->setText("0 of 0"); return; } auto sels = allSelections; QTextCharFormat activeFmt; activeFmt.setBackground(Qt::white); activeFmt.setForeground(Qt::black); sels[currentIndex].format = activeFmt; logsConsoleTextEdit->setExtraSelections(sels); logsConsoleTextEdit->setTextCursor(sels[currentIndex].cursor); searchLabel->setText(QString("%1 of %2").arg(currentIndex + 1).arg(sels.size())); } void LogsWidget::AddLogs(const int type, const qint64 time, const QString &message) { const auto& theme = ConsoleThemeManager::instance().theme(); QString sTime = UnixTimestampGlobalToStringLocal(time); QString logTime = QString("[%1] -> ").arg(sTime); logsConsoleTextEdit->appendFormatted(logTime, [&](QTextCharFormat& fmt){ fmt = theme.logDebug.toFormat(); }); QString logMsg = message + "\n"; if( type == EVENT_CLIENT_CONNECT ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.operatorConnect.toFormat(); }); else if( type == EVENT_CLIENT_DISCONNECT ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.operatorDisconnect.toFormat(); }); else if( type == EVENT_LISTENER_START ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.listenerStart.toFormat(); }); else if( type == EVENT_LISTENER_STOP ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.listenerStop.toFormat(); }); else if( type == EVENT_AGENT_NEW ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.agentNew.toFormat(); }); else if( type == EVENT_TUNNEL_START ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.tunnel.toFormat(); }); else if( type == EVENT_TUNNEL_STOP ) logsConsoleTextEdit->appendFormatted(logMsg, [&](QTextCharFormat& fmt){ fmt = theme.tunnel.toFormat(); }); else logsConsoleTextEdit->appendPlain(logMsg); } void LogsWidget::applyTheme() { const auto& theme = ConsoleThemeManager::instance().theme(); const auto& bg = theme.background; bool showBg = GlobalClient->settings->data.ConsoleShowBackground; QString imagePath = (showBg && bg.type == ConsoleBackground::Image) ? bg.imagePath : QString(); logsConsoleTextEdit->setConsoleBackground(bg.color, imagePath, bg.dimming); logsConsoleTextEdit->setStyleSheet(QString("QPlainTextEdit { color: %1; border: 1px solid #2A2A2A; border-radius: 4px; }").arg(theme.textColor.name())); } void LogsWidget::Clear() const { logsConsoleTextEdit->clear(); } void LogsWidget::toggleSearchPanel() { if (this->searchWidget->isVisible()) { this->searchWidget->setVisible(false); searchLineEdit->setText(""); handleSearch(); } else { this->searchWidget->setVisible(true); searchLineEdit->setFocus(); searchLineEdit->selectAll(); } } void LogsWidget::handleSearch() { const QString pattern = searchLineEdit->text(); if ( pattern.isEmpty() && allSelections.size() ) { allSelections.clear(); currentIndex = -1; searchLabel->setText("0 of 0"); logsConsoleTextEdit->setExtraSelections({}); return; } if (currentIndex < 0 || allSelections.isEmpty() || allSelections[0].cursor.selectedText() != pattern) { findAndHighlightAll(pattern); currentIndex = 0; } else { currentIndex = (currentIndex + 1) % allSelections.size(); } highlightCurrent(); } void LogsWidget::handleSearchBackward() { const QString pattern = searchLineEdit->text(); if (pattern.isEmpty() && allSelections.size()) { allSelections.clear(); currentIndex = -1; searchLabel->setText("0 of 0"); logsConsoleTextEdit->setExtraSelections({}); return; } if (currentIndex < 0 || allSelections.isEmpty() || allSelections[0].cursor.selectedText() != pattern) { findAndHighlightAll(pattern); currentIndex = allSelections.size() - 1; } else { currentIndex = (currentIndex - 1 + allSelections.size()) % allSelections.size(); } highlightCurrent(); }