Index: addons/katebuild-plugin/CMakeLists.txt =================================================================== --- addons/katebuild-plugin/CMakeLists.txt +++ addons/katebuild-plugin/CMakeLists.txt @@ -9,7 +9,7 @@ SelectTargetView.cpp ) -ki18n_wrap_ui(katebuild_SRCS build.ui SelectTargetUi.ui) +ki18n_wrap_ui(katebuild_SRCS build.ui SelectTargetUi.ui katebuild_config_page.ui) # resource for ui file and stuff qt5_add_resources(katebuild_SRCS plugin.qrc) Index: addons/katebuild-plugin/katebuild_config_page.ui =================================================================== --- /dev/null +++ addons/katebuild-plugin/katebuild_config_page.ui @@ -0,0 +1,70 @@ + + + ConfigPage + + + + 0 + 0 + 688 + 428 + + + + Form + + + + + + Behavior + + + + + + <html><head/><body><p>Where possible will tool tips replaced by &quot;What's This?&quot; help, also on this page but not immediately. You know? To activate this help mode try Shift-F1</p><p>The main intention was to have a way to get rid of the tool tips on the error list.</p></body></html> + + + Avoid tool tips + + + + + + + <html><head/><body><p>Will hide the view when the current build was successful but the previous build was not</p></body></html> + + + Obliging hide on successful build + + + + + + + Don't popup on build start + + + + + + + + + + Qt::Vertical + + + + 20 + 251 + + + + + + + + + Index: addons/katebuild-plugin/plugin_katebuild.h =================================================================== --- addons/katebuild-plugin/plugin_katebuild.h +++ addons/katebuild-plugin/plugin_katebuild.h @@ -29,19 +29,22 @@ #include #include #include +#include #include #include #include #include #include #include #include +#include #include #include #include "ui_build.h" +#include "ui_katebuild_config_page.h" #include "targets.h" /******************************************************************/ @@ -76,6 +79,8 @@ // reimplemented: read and write session config void readSessionConfig(const KConfigGroup& config) override; void writeSessionConfig(KConfigGroup& config) override; + // The settings from the config page + void readConfig(); bool buildCurrentTarget(); @@ -130,7 +135,20 @@ bool checkLocal(const QUrl &dir); void clearBuildResults(); - void displayBuildResult(const QString &message, KTextEditor::Message::MessageType level); + /** + * Display a user information on the current active view. + * @parm message the text to show + * @parm type the message type + * @parm autoHideDelay the time the message will be shown. When 0 is the + * delay set to a sane default, when -1 is auto hide disabled + * @parm updateOnly when true will an already shown message text updated + * to @p message and the parameter @p type and @p autoHideDelay + * will be ignored. Should there no old message exist will a new + * message created with respect of all arguments + * + * @see KTextEditor::message + */ + void postMessage(const QString &message, KTextEditor::Message::MessageType type, int autoHideDelay = 0, bool updateOnly = false); KTextEditor::MainWindow *m_win; QWidget *m_toolView; @@ -142,6 +160,8 @@ QString m_stdOut; QString m_stdErr; QString m_currentlyBuildingTarget; + int m_hideViewOnSuccess = 0; //!< Hide view when > 0 + bool m_troubleOnLastRun = false; //!< Hide view when true bool m_buildCancelled; int m_displayModeBeforeBuild; QString m_make_dir; @@ -155,12 +175,13 @@ QString m_prevItemContent; QModelIndex m_previousIndex; QPointer m_infoMessage; - - - /** - * current project plugin view, if any - */ + QTimer m_progressLimiter; //!< Single shot, as long running, don't update progess info QObject *m_projectPluginView = nullptr; + + // Config Page Settings + bool m_isSetAutoHide = false; + bool m_isSetNoToolTip = false; + bool m_isSetNoPopUp = false; }; @@ -176,7 +197,43 @@ ~KateBuildPlugin() override {} QObject *createView(KTextEditor::MainWindow *mainWindow) override; + int configPages() const override; + KTextEditor::ConfigPage *configPage (int number = 0, QWidget *parent = nullptr) override; + void readConfig(); + +private: + QVector m_views; // FIXME Only to track config page changes, do all this smarter! }; +//BEGIN ConfigPage +class KateBuildPluginConfigPage : public KTextEditor::ConfigPage + , private Ui::ConfigPage +{ + Q_OBJECT + +public: + explicit KateBuildPluginConfigPage(QWidget *parent, KateBuildPlugin *plugin); + ~KateBuildPluginConfigPage() override; + + QString name() const override; + QString fullName() const override; + QIcon icon() const override; + + void apply() override; + void reset() override { loadSettings(); } + void defaults() override { ; } + + void loadSettings(); + +private Q_SLOTS: + void modified(); + +private: + bool m_modified = false; + KateBuildPlugin *m_plugin; + +}; +//END ConfigPage + #endif Index: addons/katebuild-plugin/plugin_katebuild.cpp =================================================================== --- addons/katebuild-plugin/plugin_katebuild.cpp +++ addons/katebuild-plugin/plugin_katebuild.cpp @@ -44,39 +44,70 @@ #include #include #include +#include +#include #include #include #include #include #include - #include "SelectTargetView.h" + K_PLUGIN_FACTORY_WITH_JSON (KateBuildPluginFactory, "katebuildplugin.json", registerPlugin();) static const QString DefConfigCmd = QStringLiteral("cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr/local ../"); static const QString DefConfClean; static const QString DefTargetName = QStringLiteral("all"); static const QString DefBuildCmd = QStringLiteral("make"); static const QString DefCleanCmd = QStringLiteral("make clean"); +static const bool OnlyUpdateMessageText = true; +static const int NeverHide = 60 * 60 * 1000; // 1hour delay, quirk to avoid forced "Close" button -/******************************************************************/ +//BEGIN KateBuildPlugin KateBuildPlugin::KateBuildPlugin(QObject *parent, const VariantList&): KTextEditor::Plugin(parent) { // KF5 FIXME KGlobal::locale()->insertCatalog("katebuild-plugin"); } -/******************************************************************/ QObject *KateBuildPlugin::createView (KTextEditor::MainWindow *mainWindow) { - return new KateBuildView(this, mainWindow); + auto view = new KateBuildView(this, mainWindow); + + m_views.append(view); + + return view; +} + +int KateBuildPlugin::configPages() const +{ + return 1; +} + +KTextEditor::ConfigPage *KateBuildPlugin::configPage(int number, QWidget *parent) +{ + if (number != 0) { + return nullptr; + } + + return new KateBuildPluginConfigPage(parent, this); +} + +void KateBuildPlugin::readConfig() +{ + // Be aware that we can have more than one view! + for (int i = 0; i < m_views.size(); ++i) { + m_views.at(i)->readConfig(); + } } +//END + /******************************************************************/ KateBuildView::KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mw) : QObject (mw) @@ -101,7 +132,7 @@ m_toolView = mw->createToolView(plugin, QStringLiteral("kate_plugin_katebuildplugin"), KTextEditor::MainWindow::Bottom, - QIcon::fromTheme(QStringLiteral("application-x-ms-dos-executable")), + QIcon::fromTheme(QStringLiteral("run-build")), i18n("Build Output")); QAction *a = actionCollection()->addAction(QStringLiteral("select_target")); @@ -191,6 +222,10 @@ // Connect signals from project plugin to our slots m_projectPluginView = m_win->pluginView(QStringLiteral("kateprojectplugin")); slotPluginViewCreated(QStringLiteral("kateprojectplugin"), m_projectPluginView); + + m_progressLimiter.setSingleShot(true); + m_progressLimiter.setInterval(100); + readConfig(); } @@ -309,6 +344,13 @@ slotAddProjectTarget(); } +void KateBuildView::readConfig() +{ + KConfigGroup config(KSharedConfig::openConfig(), "Build"); + m_isSetAutoHide = config.readEntry("AutoHide", false); + m_isSetNoToolTip = config.readEntry("NoToolTips", false); + m_isSetNoPopUp = config.readEntry("NoPopUp", false); +} /******************************************************************/ void KateBuildView::slotNext() @@ -446,9 +488,10 @@ // add tooltips in all columns // The enclosing ... enables word-wrap for long error messages - item->setData(0, Qt::ToolTipRole, filename); - item->setData(1, Qt::ToolTipRole, QStringLiteral("%1").arg(message)); - item->setData(2, Qt::ToolTipRole, QStringLiteral("%1").arg(message)); + const int role = m_isSetNoToolTip ? Qt::WhatsThisRole : Qt::ToolTipRole; + item->setData(0, role, filename); + item->setData(1, role, QStringLiteral("%1").arg(message)); + item->setData(2, role, QStringLiteral("%1").arg(message)); } /******************************************************************/ @@ -507,7 +550,10 @@ m_buildUi.u_tabWidget->setCurrentIndex(1); m_displayModeBeforeBuild = m_buildUi.displayModeSlider->value(); m_buildUi.displayModeSlider->setValue(0); - m_win->showToolView(m_toolView); + + if (!m_isSetNoPopUp) { + m_win->showToolView(m_toolView); + } // set working directory m_make_dir = dir; @@ -541,9 +587,6 @@ { if (m_proc.state() != QProcess::NotRunning) { m_buildCancelled = true; - QString msg = i18n("Building %1 cancelled", m_currentlyBuildingTarget); - m_buildUi.buildStatusLabel->setText(msg); - m_buildUi.buildStatusLabel2->setText(msg); m_proc.terminate(); return true; } @@ -576,6 +619,7 @@ void KateBuildView::slotBuildDefaultTarget() { QModelIndex defaultTarget = m_targetsUi->targetsModel.defaultTarget(m_targetsUi->targetsView->currentIndex()); m_targetsUi->targetsView->setCurrentIndex(defaultTarget); + m_hideViewOnSuccess = 2; buildCurrentTarget(); } @@ -599,17 +643,18 @@ /******************************************************************/ bool KateBuildView::buildCurrentTarget() { - if (m_proc.state() != QProcess::NotRunning) { - displayBuildResult(i18n("Already building..."), KTextEditor::Message::Warning); - return false; + if (m_proc.state() == QProcess::Running) { + m_win->showToolView(m_toolView); + m_hideViewOnSuccess = 0; + return true; } QFileInfo docFInfo = docUrl().toLocalFile(); // docUrl() saves the current document QModelIndex ind = m_targetsUi->targetsView->currentIndex(); m_previousIndex = ind; if (!ind.isValid()) { - KMessageBox::sorry(nullptr, i18n("No target available for building.")); + postMessage(i18n("No target available for building."), KTextEditor::Message::Error); return false; } @@ -622,7 +667,7 @@ if (workDir.isEmpty()) { dir = docFInfo.absolutePath(); if (dir.isEmpty()) { - KMessageBox::sorry(nullptr, i18n("There is no local file or directory specified for building.")); + postMessage(i18n("There is no local file or directory specified for building."), KTextEditor::Message::Error); return false; } } @@ -641,29 +686,58 @@ buildCmd.replace(QStringLiteral("%f"), docFInfo.absoluteFilePath()); buildCmd.replace(QStringLiteral("%d"), docFInfo.absolutePath()); } + + --m_hideViewOnSuccess; m_filenameDetectorGccWorked = false; m_currentlyBuildingTarget = QStringLiteral("%1: %2").arg(targetSet, cmdName); m_buildCancelled = false; QString msg = i18n("Building target %1 ...", m_currentlyBuildingTarget); m_buildUi.buildStatusLabel->setText(msg); m_buildUi.buildStatusLabel2->setText(msg); + postMessage(i18n("Building target..."), KTextEditor::Message::Information, NeverHide); + return startProcess(dir, buildCmd); } /******************************************************************/ -void KateBuildView::displayBuildResult(const QString &msg, KTextEditor::Message::MessageType level) +void KateBuildView::postMessage(const QString &msg, KTextEditor::Message::MessageType type, int autoHideDelay/* = 0*/, bool updateOnly/* = false*/) { KTextEditor::View *kv = m_win->activeView(); if (!kv) return; - delete m_infoMessage; - m_infoMessage = new KTextEditor::Message(xi18nc("@info", "Make Results:%1", msg), level); - m_infoMessage->setWordWrap(true); - m_infoMessage->setPosition(KTextEditor::Message::BottomInView); - m_infoMessage->setAutoHide(5000); - m_infoMessage->setAutoHideMode(KTextEditor::Message::Immediate); - m_infoMessage->setView(kv); - kv->document()->postMessage(m_infoMessage); + QString msgType; + switch (type) { + // TODO KF6 How about a function in KTextEditor::Message to get these translated message type names? + case KTextEditor::Message::Positive: + case KTextEditor::Message::Information: + msgType = i18n("Info"); + break; + case KTextEditor::Message::Warning: + msgType = i18n("Warning"); + break; + case KTextEditor::Message::Error: + msgType = i18n("Error"); + break; + } + + if (updateOnly && m_infoMessage) { + m_infoMessage->setText(xi18nc("@info", "Make %1%2", msgType, msg)); + + } else { + delete m_infoMessage; + // FIXME Add a minimum width to the message box, looks sometimes too titchy + // m_infoMessage->setMinimumWidth(123) will not work, it's only an QOject + // and attempts to add spaces failed. Update: Since we have the hourglass it looks much better + // FIXME Sometimes on the change "progress info"->"we are done" the "progress info" + // becomes the new size of "we are done" which cause an ungly line break. Ideas? + m_infoMessage = new KTextEditor::Message(xi18nc("@info", "Make %1%2", msgType, msg), type); + m_infoMessage->setWordWrap(true); + m_infoMessage->setPosition(KTextEditor::Message::BottomInView); + m_infoMessage->setAutoHide(autoHideDelay); + m_infoMessage->setAutoHideMode(KTextEditor::Message::Immediate); + m_infoMessage->setView(kv); + kv->document()->postMessage(m_infoMessage); + } } /******************************************************************/ @@ -691,7 +765,12 @@ m_win->showToolView(m_toolView); } - if (m_numErrors || m_numWarnings) { + if (m_buildCancelled) { + buildStatus = i18n("Building %1 cancelled", m_currentlyBuildingTarget); + postMessage(i18n("Build cancelled"), KTextEditor::Message::Information); + + } else if (m_numErrors || m_numWarnings) { + m_troubleOnLastRun = true; QStringList msgs; if (m_numErrors) { msgs << i18np("Found one error.", "Found %1 errors.", m_numErrors); @@ -701,21 +780,22 @@ msgs << i18np("Found one warning.", "Found %1 warnings.", m_numWarnings); buildStatus = i18n("Building %1 had warnings.", m_currentlyBuildingTarget); } - displayBuildResult(msgs.join(QLatin1Char('\n')), m_numErrors ? KTextEditor::Message::Error : KTextEditor::Message::Warning); - } - else if (exitCode != 0) { - displayBuildResult(i18n("Build failed."), KTextEditor::Message::Warning); - } - else { - displayBuildResult(i18n("Build completed without problems."), KTextEditor::Message::Positive); - } + postMessage(msgs.join(QLatin1Char('\n')), m_numErrors ? KTextEditor::Message::Error : KTextEditor::Message::Warning); - if (!m_buildCancelled) { - m_buildUi.buildStatusLabel->setText(buildStatus); - m_buildUi.buildStatusLabel2->setText(buildStatus); - m_buildCancelled = false; + } else if (exitCode != 0) { + postMessage(i18n("Build failed."), KTextEditor::Message::Error); + + } else { + postMessage(i18n("Build completed without problems."), KTextEditor::Message::Positive); + if (m_isSetAutoHide && (m_hideViewOnSuccess > 0) && m_troubleOnLastRun) { + m_win->hideToolView(m_toolView); + } + m_troubleOnLastRun = false; } + m_buildUi.buildStatusLabel->setText(buildStatus); + m_buildUi.buildStatusLabel2->setText(buildStatus); + m_hideViewOnSuccess = 0; } @@ -729,6 +809,10 @@ l.remove(QLatin1Char('\r')); m_stdOut += l; + QRegularExpression regExp = QRegularExpression(QStringLiteral("^\\[\\s*([\\d\\%]+)\\]")); + QString hourglass(QStringLiteral("--------------")); + static int hourglassIdx = -1; + // handle one line at a time do { const int end = m_stdOut.indexOf(QLatin1Char('\n')); @@ -738,6 +822,15 @@ m_buildUi.plainTextEdit->appendPlainText(line); //qDebug() << line; + QRegularExpressionMatch match = regExp.match(line); + if (match.hasMatch() && !m_progressLimiter.isActive()) { + m_progressLimiter.start(); + hourglassIdx = (++hourglassIdx > hourglass.size() - 1) ? 0 : hourglassIdx; + hourglass.replace(hourglassIdx, 1, QLatin1Char('>')); + postMessage(i18n("Building target\n%1 %2 %1", hourglass, QStringLiteral("%1").arg(match.captured(1), 4, QLatin1Char(' '))) + , KTextEditor::Message::Information, NeverHide, OnlyUpdateMessageText); + } + if (m_newDirDetector.match(line).hasMatch()) { //qDebug() << "Enter/Exit dir found"; int open = line.indexOf(QLatin1Char('`')); @@ -1059,6 +1152,80 @@ } } +//BEGIN ConfigPage +KateBuildPluginConfigPage::KateBuildPluginConfigPage(QWidget *parent, KateBuildPlugin *plugin) + : KTextEditor::ConfigPage(parent) + , m_plugin(plugin) +{ + setupUi(this); + loadSettings(); + + // FIXME Add observeChanges(..) to master class like in KateConfigPage, best may in KTextEditor::ConfigPage + connect(autoHide, &QAbstractButton::toggled, this, &KateBuildPluginConfigPage::modified); + connect(avoidPopUp, &QAbstractButton::toggled, this, &KateBuildPluginConfigPage::modified); + connect(avoidToolTips, &QAbstractButton::toggled, this, &KateBuildPluginConfigPage::modified); +} + +KateBuildPluginConfigPage::~KateBuildPluginConfigPage() +{} + +// FIXME Should be part of master class +void KateBuildPluginConfigPage::modified() +{ + m_modified = true; + emit changed(); +} +QString KateBuildPluginConfigPage::name() const +{ + return i18n("Build"); +} + +QString KateBuildPluginConfigPage::fullName() const +{ + return i18n("%1 Settings", name()); +} + +QIcon KateBuildPluginConfigPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("project_rebuild")); +} + +void KateBuildPluginConfigPage::apply() +{ + if (!m_modified) { + return; + } + + m_modified = false; + + KConfigGroup config(KSharedConfig::openConfig(), "Build"); + + config.writeEntry("AutoHide", autoHide->isChecked()); + config.writeEntry("NoPopUp", avoidPopUp->isChecked()); + config.writeEntry("NoToolTips", avoidToolTips->isChecked()); + + config.sync(); + + m_plugin->readConfig(); +} + +void KateBuildPluginConfigPage::loadSettings() +{ + KConfigGroup config(KSharedConfig::openConfig(), "Build"); + + autoHide->setChecked(config.readEntry("AutoHide", false)); + avoidPopUp->setChecked(config.readEntry("NoPopUp", false)); + avoidToolTips->setChecked(config.readEntry("NoToolTips", false)); + + // In oposite to the statet WhatsThis help, we don't remove what's not there, but add :-) + if (!avoidToolTips->isChecked()) { + autoHide->setToolTip(autoHide->whatsThis()); + avoidPopUp->setToolTip(avoidPopUp->whatsThis()); + avoidToolTips->setToolTip(avoidToolTips->whatsThis()); + } +} + +//END ConfigPage #include "plugin_katebuild.moc"