Add support for view older launcher logs

Signed-off-by: Yihe Li <winmikedows@hotmail.com>
This commit is contained in:
Yihe Li 2025-06-03 06:08:24 +08:00
parent c58cc3396a
commit 289645266a
No known key found for this signature in database
8 changed files with 560 additions and 183 deletions

View file

@ -697,16 +697,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("ConsoleMaxLines", 100000);
m_settings->registerSetting("ConsoleOverflowStop", true);
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk);
if (!conversionOk) {
maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
}
logModel->setMaxLines(maxLines);
logModel->setStopOnOverflow(settings()->get("ConsoleOverflowStop").toBool());
logModel->setOverflowMessage(tr("Cannot display this log since the log length surpassed %1 lines.").arg(maxLines));
logModel->setMaxLines(getConsoleMaxLines());
logModel->setStopOnOverflow(shouldStopOnConsoleOverflow());
logModel->setOverflowMessage(tr("Cannot display this log since the log length surpassed %1 lines.").arg(logModel->getMaxLines()));
// Folders
m_settings->registerSetting("InstanceDir", "instances");
@ -1614,6 +1607,23 @@ void Application::updateIsRunning(bool running)
m_updateRunning = running;
}
int Application::getConsoleMaxLines() const
{
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk);
if (!conversionOk) {
maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
}
return maxLines;
}
bool Application::shouldStopOnConsoleOverflow() const
{
return settings()->get("ConsoleOverflowStop").toBool();
}
void Application::controllerSucceeded()
{
auto controller = qobject_cast<LaunchController*>(QObject::sender());

View file

@ -162,6 +162,9 @@ class Application : public QApplication {
QString getModrinthAPIToken();
QString getUserAgent();
int getConsoleMaxLines() const;
bool shouldStopOnConsoleOverflow() const;
/// this is the root of the 'installation'. Used for automatic updates
const QString& root() { return m_rootPath; }

View file

@ -54,3 +54,18 @@ MessageLevel::Enum MessageLevel::fromLine(QString& line)
}
return MessageLevel::Unknown;
}
MessageLevel::Enum MessageLevel::fromLauncherLine(QString& line)
{
// Level prefix
int startMark = 0;
while (startMark < line.size() && (line[startMark].isDigit() || line[startMark].isSpace() || line[startMark] == '.'))
++startMark;
int endmark = line.indexOf(":");
if (startMark < line.size() && endmark != -1) {
auto level = MessageLevel::getLevel(line.left(endmark).mid(startMark));
line = line.mid(endmark + 2);
return level;
}
return MessageLevel::Unknown;
}

View file

@ -26,4 +26,7 @@ MessageLevel::Enum getLevel(QtMsgType type);
/* Get message level from a line. Line is modified if it was successful. */
MessageLevel::Enum fromLine(QString& line);
/* Get message level from a line from the launcher log. Line is modified if it was successful. */
MessageLevel::Enum fromLauncherLine(QString& line);
} // namespace MessageLevel

View file

@ -2,7 +2,6 @@
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* Copyright (c) 2025 Yihe Li <winmikedows@hotmail.com>
*
@ -39,19 +38,18 @@
#include "LauncherLogPage.h"
#include "ui_LauncherLogPage.h"
#include "Application.h"
#include <QIdentityProxyModel>
#include <QScrollBar>
#include <QShortcut>
#include "launch/LaunchTask.h"
#include "settings/Setting.h"
#include <QMessageBox>
#include "ui/GuiUtil.h"
#include "ui/themes/ThemeManager.h"
#include <BuildConfig.h>
#include <FileSystem.h>
#include <GZip.h>
#include <QDir>
#include <QDirIterator>
#include <QFileSystemWatcher>
#include <QShortcut>
#include <QUrl>
QVariant LogFormatProxyModel::data(const QModelIndex& index, int role) const
{
@ -129,7 +127,12 @@ QModelIndex LogFormatProxyModel::find(const QModelIndex& start, const QString& v
return QModelIndex();
}
LauncherLogPage::LauncherLogPage(QWidget* parent) : QWidget(parent), ui(new Ui::LauncherLogPage)
LauncherLogPage::LauncherLogPage(QWidget* parent)
: QWidget(parent)
, ui(new Ui::LauncherLogPage)
, m_model(APPLICATION->logModel)
, m_basePath(APPLICATION->dataRoot())
, m_logSearchPaths({ "logs" })
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
@ -148,16 +151,21 @@ LauncherLogPage::LauncherLogPage(QWidget* parent) : QWidget(parent), ui(new Ui::
}
ui->text->setModel(m_proxy);
m_proxy->setSourceModel(APPLICATION->logModel.get());
m_proxy->setSourceModel(m_model.get());
modelStateToUI();
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &LauncherLogPage::populateSelectLogBox);
auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this);
connect(findShortcut, SIGNAL(activated()), SLOT(findActivated()));
connect(findShortcut, &QShortcut::activated, this, &LauncherLogPage::findActivated);
auto findNextShortcut = new QShortcut(QKeySequence(QKeySequence::FindNext), this);
connect(findNextShortcut, SIGNAL(activated()), SLOT(findNextActivated()));
connect(ui->searchBar, SIGNAL(returnPressed()), SLOT(on_findButton_clicked()));
connect(findNextShortcut, &QShortcut::activated, this, &LauncherLogPage::findNextActivated);
auto findPreviousShortcut = new QShortcut(QKeySequence(QKeySequence::FindPrevious), this);
connect(findPreviousShortcut, SIGNAL(activated()), SLOT(findPreviousActivated()));
connect(findPreviousShortcut, &QShortcut::activated, this, &LauncherLogPage::findPreviousActivated);
connect(ui->searchBar, &QLineEdit::returnPressed, this, &LauncherLogPage::on_findButton_clicked);
}
LauncherLogPage::~LauncherLogPage()
@ -167,21 +175,21 @@ LauncherLogPage::~LauncherLogPage()
void LauncherLogPage::modelStateToUI()
{
if (APPLICATION->logModel->wrapLines()) {
if (m_model->wrapLines()) {
ui->text->setWordWrap(true);
ui->wrapCheckbox->setCheckState(Qt::Checked);
} else {
ui->text->setWordWrap(false);
ui->wrapCheckbox->setCheckState(Qt::Unchecked);
}
if (APPLICATION->logModel->colorLines()) {
if (m_model->colorLines()) {
ui->text->setColorLines(true);
ui->colorCheckbox->setCheckState(Qt::Checked);
} else {
ui->text->setColorLines(false);
ui->colorCheckbox->setCheckState(Qt::Unchecked);
}
if (APPLICATION->logModel->suspended()) {
if (m_model->suspended()) {
ui->trackLogCheckbox->setCheckState(Qt::Unchecked);
} else {
ui->trackLogCheckbox->setCheckState(Qt::Checked);
@ -190,47 +198,205 @@ void LauncherLogPage::modelStateToUI()
void LauncherLogPage::UIToModelState()
{
if (!APPLICATION->logModel) {
if (!m_model) {
return;
}
APPLICATION->logModel->setLineWrap(ui->wrapCheckbox->checkState() == Qt::Checked);
APPLICATION->logModel->setColorLines(ui->colorCheckbox->checkState() == Qt::Checked);
APPLICATION->logModel->suspend(ui->trackLogCheckbox->checkState() != Qt::Checked);
m_model->setLineWrap(ui->wrapCheckbox->checkState() == Qt::Checked);
m_model->setColorLines(ui->colorCheckbox->checkState() == Qt::Checked);
m_model->suspend(ui->trackLogCheckbox->checkState() != Qt::Checked);
}
void LauncherLogPage::retranslate()
{
ui->retranslateUi(this);
}
void LauncherLogPage::openedImpl()
{
const QStringList failedPaths = m_watcher.addPaths(m_logSearchPaths);
for (const QString& path : m_logSearchPaths) {
if (failedPaths.contains(path))
qDebug() << "Failed to start watching" << path;
else
qDebug() << "Started watching" << path;
}
populateSelectLogBox();
}
void LauncherLogPage::closedImpl()
{
const QStringList failedPaths = m_watcher.removePaths(m_logSearchPaths);
for (const QString& path : m_logSearchPaths) {
if (failedPaths.contains(path))
qDebug() << "Failed to stop watching" << path;
else
qDebug() << "Stopped watching" << path;
}
}
void LauncherLogPage::populateSelectLogBox()
{
const QString prevCurrentFile = m_currentFile;
ui->selectLogBox->blockSignals(true);
ui->selectLogBox->clear();
ui->selectLogBox->addItem("Current logs");
ui->selectLogBox->addItems(getPaths());
ui->selectLogBox->blockSignals(false);
if (!prevCurrentFile.isEmpty()) {
const int index = ui->selectLogBox->findText(prevCurrentFile);
if (index != -1) {
ui->selectLogBox->blockSignals(true);
ui->selectLogBox->setCurrentIndex(index);
ui->selectLogBox->blockSignals(false);
setControlsEnabled(true);
// don't refresh file
return;
} else {
setControlsEnabled(false);
}
} else {
ui->selectLogBox->setCurrentIndex(0);
setControlsEnabled(true);
}
on_selectLogBox_currentIndexChanged(ui->selectLogBox->currentIndex());
}
void LauncherLogPage::on_selectLogBox_currentIndexChanged(const int index)
{
QString file;
if (index > 0) {
file = ui->selectLogBox->itemText(index);
}
if (index != 0 && (file.isEmpty() || !QFile::exists(FS::PathCombine(m_basePath, file)))) {
m_currentFile = QString();
ui->text->clear();
setControlsEnabled(false);
} else {
m_currentFile = file;
reload();
setControlsEnabled(true);
}
}
void LauncherLogPage::on_btnReload_clicked()
{
if (m_currentFile.isEmpty()) {
if (!m_model)
return;
m_model->clear();
m_container->refreshContainer();
} else {
reload();
}
}
void LauncherLogPage::reload()
{
if (m_currentFile.isEmpty()) {
m_model = APPLICATION->logModel;
m_proxy->setSourceModel(m_model.get());
ui->text->setModel(m_proxy);
ui->text->scrollToBottom();
UIToModelState();
setControlsEnabled(true);
return;
}
QFile file(FS::PathCombine(m_basePath, m_currentFile));
if (!file.open(QFile::ReadOnly)) {
setControlsEnabled(false);
ui->btnReload->setEnabled(true); // allow reload
m_currentFile = QString();
QMessageBox::critical(this, tr("Error"), tr("Unable to open %1 for reading: %2").arg(m_currentFile, file.errorString()));
} else {
auto setPlainText = [this](const QString& text) {
QTextDocument* doc = ui->text->document();
doc->setDefaultFont(m_proxy->getFont());
ui->text->setPlainText(text);
};
auto showTooBig = [setPlainText, &file]() {
setPlainText(tr("The file (%1) is too big. You may want to open it in a viewer optimized "
"for large files.")
.arg(file.fileName()));
};
if (file.size() > (1024ll * 1024ll * 12ll)) {
showTooBig();
return;
}
MessageLevel::Enum last = MessageLevel::Unknown;
auto handleLine = [this, &last](QString line) {
if (line.isEmpty())
return false;
if (line.back() == '\n')
line = line.remove(line.size() - 1, 1);
QString lineTemp = line; // don't edit out the time and level for clarity
MessageLevel::Enum level = MessageLevel::fromLauncherLine(lineTemp);
last = level;
m_model->append(level, line);
return m_model->isOverFlow();
};
// Try to determine a level for each line
ui->text->clear();
ui->text->setModel(nullptr);
m_model.reset(new LogModel(this));
m_model->setMaxLines(APPLICATION->getConsoleMaxLines());
m_model->setStopOnOverflow(APPLICATION->shouldStopOnConsoleOverflow());
m_model->setOverflowMessage(tr("Cannot display this log since the log length surpassed %1 lines.").arg(m_model->getMaxLines()));
m_model->clear();
if (file.fileName().endsWith(".gz")) {
QString line;
auto error = GZip::readGzFileByBlocks(&file, [&line, handleLine](const QByteArray& d) {
auto block = d;
int newlineIndex = block.indexOf('\n');
while (newlineIndex != -1) {
line += QString::fromUtf8(block).left(newlineIndex);
block.remove(0, newlineIndex + 1);
if (handleLine(line)) {
line.clear();
return false;
}
line.clear();
newlineIndex = block.indexOf('\n');
}
line += QString::fromUtf8(block);
return true;
});
if (!error.isEmpty()) {
setPlainText(tr("The file (%1) encountered an error when reading: %2.").arg(file.fileName(), error));
return;
} else if (!line.isEmpty()) {
handleLine(line);
}
} else {
while (!file.atEnd() && !handleLine(QString::fromUtf8(file.readLine()))) {
}
}
m_proxy->setSourceModel(m_model.get());
ui->text->setModel(m_proxy);
ui->text->scrollToBottom();
UIToModelState();
setControlsEnabled(true);
}
}
void LauncherLogPage::on_btnPaste_clicked()
{
if (!APPLICATION->logModel)
return;
// FIXME: turn this into a proper task and move the upload logic out of GuiUtil!
APPLICATION->logModel->append(MessageLevel::Launcher,
QString("Log upload triggered at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)));
auto url = GuiUtil::uploadPaste(tr("Launcher Log"), APPLICATION->logModel->toPlainText(), this);
if (!url.has_value()) {
APPLICATION->logModel->append(MessageLevel::Error, QString("Log upload canceled"));
} else if (url->isNull()) {
APPLICATION->logModel->append(MessageLevel::Error, QString("Log upload failed!"));
} else {
APPLICATION->logModel->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url.value()));
}
GuiUtil::uploadPaste(m_currentFile, ui->text->toPlainText(), this);
}
void LauncherLogPage::on_btnCopy_clicked()
{
if (!APPLICATION->logModel)
return;
APPLICATION->logModel->append(MessageLevel::Launcher,
QString("Clipboard copy at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)));
GuiUtil::setClipboardText(APPLICATION->logModel->toPlainText());
}
void LauncherLogPage::on_btnClear_clicked()
{
if (!APPLICATION->logModel)
return;
APPLICATION->logModel->clear();
m_container->refreshContainer();
GuiUtil::setClipboardText(ui->text->toPlainText());
}
void LauncherLogPage::on_btnBottom_clicked()
@ -240,25 +406,149 @@ void LauncherLogPage::on_btnBottom_clicked()
void LauncherLogPage::on_trackLogCheckbox_clicked(bool checked)
{
if (!APPLICATION->logModel)
if (!m_model)
return;
APPLICATION->logModel->suspend(!checked);
m_model->suspend(!checked);
}
void LauncherLogPage::on_btnDelete_clicked()
{
if (m_currentFile.isEmpty()) {
setControlsEnabled(false);
return;
}
if (QMessageBox::question(this, tr("Confirm Deletion"),
tr("You are about to delete \"%1\".\n"
"This may be permanent and it will be gone from the logs folder.\n\n"
"Are you sure?")
.arg(m_currentFile),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
return;
}
QFile file(FS::PathCombine(m_basePath, m_currentFile));
if (FS::trash(file.fileName())) {
return;
}
if (!file.remove()) {
QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2").arg(m_currentFile, file.errorString()));
}
}
void LauncherLogPage::on_btnClean_clicked()
{
auto toDelete = getPaths();
if (toDelete.isEmpty()) {
return;
}
QMessageBox* messageBox = new QMessageBox(this);
messageBox->setWindowTitle(tr("Confirm Cleanup"));
if (toDelete.size() > 5) {
messageBox->setText(tr("Are you sure you want to delete all log files?"));
messageBox->setDetailedText(toDelete.join('\n'));
} else {
messageBox->setText(tr("Are you sure you want to delete all these files?\n%1").arg(toDelete.join('\n')));
}
messageBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
messageBox->setDefaultButton(QMessageBox::Ok);
messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse);
messageBox->setIcon(QMessageBox::Question);
messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
if (messageBox->exec() != QMessageBox::Ok) {
return;
}
QStringList failed;
for (auto item : toDelete) {
QString absolutePath = FS::PathCombine(m_basePath, item);
QFile file(absolutePath);
qDebug() << "Deleting log" << absolutePath;
if (FS::trash(file.fileName())) {
continue;
}
if (!file.remove()) {
failed.push_back(item);
}
}
if (!failed.empty()) {
QMessageBox* messageBoxFailure = new QMessageBox(this);
messageBoxFailure->setWindowTitle(tr("Error"));
if (failed.size() > 5) {
messageBoxFailure->setText(tr("Couldn't delete some files!"));
messageBoxFailure->setDetailedText(failed.join('\n'));
} else {
messageBoxFailure->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n')));
}
messageBoxFailure->setStandardButtons(QMessageBox::Ok);
messageBoxFailure->setDefaultButton(QMessageBox::Ok);
messageBoxFailure->setTextInteractionFlags(Qt::TextSelectableByMouse);
messageBoxFailure->setIcon(QMessageBox::Critical);
messageBoxFailure->setTextInteractionFlags(Qt::TextBrowserInteraction);
messageBoxFailure->exec();
}
}
void LauncherLogPage::on_wrapCheckbox_clicked(bool checked)
{
ui->text->setWordWrap(checked);
if (!APPLICATION->logModel)
if (!m_model)
return;
APPLICATION->logModel->setLineWrap(checked);
m_model->setLineWrap(checked);
ui->text->scrollToBottom();
}
void LauncherLogPage::on_colorCheckbox_clicked(bool checked)
{
ui->text->setColorLines(checked);
if (!APPLICATION->logModel)
if (!m_model)
return;
APPLICATION->logModel->setColorLines(checked);
m_model->setColorLines(checked);
ui->text->scrollToBottom();
}
void LauncherLogPage::setControlsEnabled(const bool enabled)
{
if (!m_currentFile.isEmpty()) {
ui->btnReload->setText("&Reload");
ui->btnReload->setToolTip("Reload the contents of the log from the disk");
ui->btnDelete->setEnabled(enabled);
ui->btnClean->setEnabled(enabled);
ui->trackLogCheckbox->setEnabled(false);
} else {
ui->btnReload->setText("Clear");
ui->btnReload->setToolTip("Clear the log");
ui->btnDelete->setEnabled(false);
ui->btnClean->setEnabled(false);
ui->trackLogCheckbox->setEnabled(enabled);
}
ui->btnReload->setEnabled(enabled);
ui->btnCopy->setEnabled(enabled);
ui->btnPaste->setEnabled(enabled);
ui->text->setEnabled(enabled);
}
QStringList LauncherLogPage::getPaths()
{
QDir baseDir(m_basePath);
QStringList result;
for (QString searchPath : m_logSearchPaths) {
QDir searchDir(searchPath);
QStringList filters{ "*.log", "*.log.gz" };
if (searchPath != m_basePath)
filters.append("*.txt");
QStringList entries = searchDir.entryList(filters, QDir::Files | QDir::Readable, QDir::SortFlag::Time);
for (const QString& name : entries)
result.append(baseDir.relativeFilePath(searchDir.filePath(name)));
}
return result;
}
void LauncherLogPage::on_findButton_clicked()
@ -286,8 +576,3 @@ void LauncherLogPage::findActivated()
ui->searchBar->selectAll();
}
}
void LauncherLogPage::retranslate()
{
ui->retranslateUi(this);
}

View file

@ -36,6 +36,7 @@
#pragma once
#include <QFileSystemWatcher>
#include <QIdentityProxyModel>
#include <QWidget>
@ -48,6 +49,7 @@ namespace Ui {
class LauncherLogPage;
}
class QTextCharFormat;
class RecursiveFileSystemWatcher;
class LogFormatProxyModel : public QIdentityProxyModel {
public:
@ -74,10 +76,17 @@ class LauncherLogPage : public QWidget, public BasePage {
QString helpPage() const override { return "Launcher-Logs"; }
void retranslate() override;
void openedImpl() override;
void closedImpl() override;
private slots:
void populateSelectLogBox();
void on_selectLogBox_currentIndexChanged(int index);
void on_btnReload_clicked();
void on_btnPaste_clicked();
void on_btnCopy_clicked();
void on_btnClear_clicked();
void on_btnDelete_clicked();
void on_btnClean_clicked();
void on_btnBottom_clicked();
void on_trackLogCheckbox_clicked(bool checked);
@ -90,10 +99,21 @@ class LauncherLogPage : public QWidget, public BasePage {
void findPreviousActivated();
private:
void reload();
void modelStateToUI();
void UIToModelState();
void setControlsEnabled(bool enabled);
QStringList getPaths();
private:
Ui::LauncherLogPage* ui;
LogFormatProxyModel* m_proxy;
shared_qobject_ptr<LogModel> m_model;
/** Path to display log paths relative to. */
QString m_basePath;
QStringList m_logSearchPaths;
QString m_currentFile;
QFileSystemWatcher m_watcher;
};

View file

@ -33,6 +33,40 @@
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Search:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="searchBar"/>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="findButton">
<property name="text">
<string>&amp;Find</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="btnBottom">
<property name="toolTip">
<string>Scroll all the way to bottom</string>
</property>
<property name="text">
<string>&amp;Bottom</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="LogView" name="text">
<property name="undoRedoEnabled">
@ -52,8 +86,44 @@
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="selectLogBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDelete">
<property name="toolTip">
<string>Delete the selected log</string>
</property>
<property name="text">
<string>&amp;Delete Selected</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClean">
<property name="toolTip">
<string>Delete all the logs</string>
</property>
<property name="text">
<string>Delete &amp;All</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout1">
<item>
<widget class="QCheckBox" name="trackLogCheckbox">
<property name="text">
@ -113,55 +183,23 @@
<string>Upload the log to the paste service configured in preferences</string>
</property>
<property name="text">
<string>Upload</string>
<string>&amp;Upload</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClear">
<widget class="QPushButton" name="btnReload">
<property name="toolTip">
<string>Clear the log</string>
<string>Reload the contents of the log from the disk</string>
</property>
<property name="text">
<string>Clear</string>
<string>&amp;Reload</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Search:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="findButton">
<property name="text">
<string>Find</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="searchBar"/>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="btnBottom">
<property name="toolTip">
<string>Scroll all the way to bottom</string>
</property>
<property name="text">
<string>Bottom</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</layout>
</item>
</layout>
</widget>
@ -178,12 +216,14 @@
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>trackLogCheckbox</tabstop>
<tabstop>wrapCheckbox</tabstop>
<tabstop>colorCheckbox</tabstop>
<tabstop>selectLogBox</tabstop>
<tabstop>btnReload</tabstop>
<tabstop>btnCopy</tabstop>
<tabstop>btnPaste</tabstop>
<tabstop>btnClear</tabstop>
<tabstop>btnDelete</tabstop>
<tabstop>btnClean</tabstop>
<tabstop>wrapCheckbox</tabstop>
<tabstop>colorCheckbox</tabstop>
<tabstop>text</tabstop>
<tabstop>searchBar</tabstop>
<tabstop>findButton</tabstop>

View file

@ -211,7 +211,8 @@ void OtherLogsPage::on_btnReload_clicked()
MessageLevel::Enum level = MessageLevel::Unknown;
// if the launcher part set a log level, use it
auto innerLevel = MessageLevel::fromLine(line);
QString lineTemp = line; // don't edit out the time and level for clarity
auto innerLevel = MessageLevel::fromLine(lineTemp);
if (innerLevel != MessageLevel::Unknown) {
level = innerLevel;
}