Introduce shell-embedded message area, to avoid dialog windows

When code wants to inform the user about an issue, lots of KDevelop
code currently simply pops up a message dialog window, with all the
consequences like stealing the focus.
Most of the time the message though is just assisting information
why something has not worked and does not need to block the user instantly
from the normal interaction, instead could be digested by the user later.

This patch introduces a message area to the shell, between toolbars and
the view area. Which will auto-expand if messages are to be shown.
This is similar to the inline-message area known from KTextEditor documents,
but here applied to the scope of the shell.

Luckily the code from KTextEditor for the message area can also be reused
here (though only as fork for now), so it is well tested code.

Not all places using KMessageBox/QMessageBox for notification-like
messages can be replaced yet, as sometimes they are used when assistant
dialogs are open. That might need a second step to also ensure all such
dialogs have such areas and could then get the messages channeled to them.

This message system would also allow to be extended later to send off
messages to the workspace notification system if the shell window does
not have focus, for messages flagged as being important enough for this.
parent faf02ba7
Pipeline #13276 failed with stage
in 60 minutes
......@@ -32,6 +32,7 @@ namespace Sublime{
class Controller;
class View;
class Area;
class Message;
}
namespace KDevelop {
......@@ -139,6 +140,19 @@ public:
* @p message The message
* @p timeout The timeout in seconds how long to show the message */
virtual void showErrorMessage(const QString& message, int timeout = 1) = 0;
// TODO: convert all these calls into postMessage
/**
* Shows a message in the message area.
*
* If running in NoGui mode, the message will be discarded.
*
* Unlike all other functions in this class, this function is thread-safe.
* You can call it from the background.
*
* @p message the message, ownership is transferred
*/
virtual void postMessage(Sublime::Message* message) = 0;
/** @return area for currently active sublime mainwindow or 0 if
no sublime mainwindow is active.*/
......
......@@ -190,6 +190,7 @@ PUBLIC
KF5::ThreadWeaver
PRIVATE
KDev::Project
KDev::Sublime
KF5::GuiAddons
KF5::TextEditor
KF5::Parts
......
......@@ -26,8 +26,9 @@
#include <language/backgroundparser/backgroundparser.h>
#include <interfaces/icore.h>
#include <interfaces/ilanguagecontroller.h>
#include <KMessageBox>
#include <interfaces/iuicontroller.h>
#include <sublime/message.h>
// KF
#include <KLocalizedString>
using namespace KDevelop;
......@@ -117,7 +118,8 @@ void RenameAction::execute()
DocumentChangeSet::ChangeResult result = changes.applyAllChanges();
if (!result) {
KMessageBox::error(nullptr, i18n("Failed to apply changes: %1", result.m_failureReason));
auto* message = new Sublime::Message(i18n("Failed to apply changes: %1", result.m_failureReason), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
emit executed(this);
......
......@@ -25,10 +25,11 @@
#include <language/codegen/basicrefactoring.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/icore.h>
#include <sublime/message.h>
// KF
#include <KLocalizedString>
#include <KMessageBox>
using namespace KDevelop;
......@@ -84,7 +85,8 @@ void RenameFileAction::execute()
result = changes.applyAllChanges();
}
if (!result) {
KMessageBox::error(nullptr, i18n("Failed to apply changes: %1", result.m_failureReason));
auto* message = new Sublime::Message(i18n("Failed to apply changes: %1", result.m_failureReason), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
emit executed(this);
}
......@@ -21,7 +21,6 @@
// Qt
#include <QAction>
// KF
#include <KMessageBox>
#include <KParts/MainWindow>
#include <KTextEditor/Document>
#include <KTextEditor/View>
......@@ -40,6 +39,7 @@
#include <duchain/classdeclaration.h>
#include <duchain/classfunctiondeclaration.h>
#include <duchain/use.h>
#include <sublime/message.h>
#include "progressdialogs/refactoringdialog.h"
#include <debug.h>
......@@ -231,14 +231,16 @@ void BasicRefactoring::startInteractiveRename(const KDevelop::IndexedDeclaration
Declaration* declaration = decl.data();
if (!declaration) {
KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("No declaration under cursor"));
auto* message = new Sublime::Message(i18n("No declaration under cursor"), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return;
}
QFileInfo info(declaration->topContext()->url().str());
if (!info.isWritable()) {
KMessageBox::error(ICore::self()->uiController()->activeMainWindow(),
i18n("Declaration is located in non-writable file %1.",
declaration->topContext()->url().str()));
const QString messageText = i18n("Declaration is located in non-writable file %1.",
declaration->topContext()->url().str());
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return;
}
......@@ -345,7 +347,8 @@ DocumentChangeSet BasicRefactoring::renameCollectedDeclarations(KDevelop::BasicR
DocumentChangeSet::ChangeResult result = applyChanges(originalName, replacementName, changes,
collected.data(), usedDeclarationIndex);
if (!result) {
KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason));
auto* message = new Sublime::Message(i18n("Failed to apply changes: %1", result.m_failureReason), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return {};
}
}
......@@ -354,7 +357,8 @@ DocumentChangeSet BasicRefactoring::renameCollectedDeclarations(KDevelop::BasicR
DocumentChangeSet::ChangeResult result = applyChangesToDeclarations(originalName, replacementName, changes,
collector->declarations());
if (!result) {
KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason));
auto* message = new Sublime::Message(i18n("Failed to apply changes: %1", result.m_failureReason), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return {};
}
......@@ -367,7 +371,8 @@ DocumentChangeSet BasicRefactoring::renameCollectedDeclarations(KDevelop::BasicR
result = changes.applyAllChanges();
if (!result) {
KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason));
auto* message = new Sublime::Message(i18n("Failed to apply changes: %1", result.m_failureReason), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
return {};
......
......@@ -35,6 +35,7 @@
#include <debug.h>
#include <interfaces/iuicontroller.h>
#include <codegen/coderepresentation.h>
#include <sublime/message.h>
#include <KLocalizedString>
using namespace KDevelop;
......@@ -410,24 +411,22 @@ void UsesCollector::updateReady(const KDevelop::IndexedString& url, KDevelop::Re
///or whether we work on with it.
///@todo We will lose files that were edited right after their update here.
qCWarning(LANGUAGE) << "WARNING: context" << topContext->url().str() << "does not have the required features!!";
ICore::self()->uiController()->showErrorMessage(QLatin1String(
"Updating ") +
ICore::self()->projectController()->prettyFileName(topContext->
url().toUrl(),
KDevelop::
IProjectController
::FormatPlain) +
QLatin1String(" failed!"), 5);
// TODO no i18n?
const QString messageText = QLatin1String("Updating ") +
ICore::self()->projectController()->prettyFileName(topContext->url().toUrl(), KDevelop::IProjectController::FormatPlain) + QLatin1String(" failed!");
auto* message = new Sublime::Message(messageText, Sublime::Message::Warning);
message->setAutoHide(0);
ICore::self()->uiController()->postMessage(message);
return;
}
if (topContext->parsingEnvironmentFile()->needsUpdate()) {
qCWarning(LANGUAGE) << "WARNING: context" << topContext->url().str() << "is not up to date!";
ICore::self()->uiController()->showErrorMessage(i18n("%1 still needs an update!",
ICore::self()->projectController()->prettyFileName(
topContext->url().toUrl(),
KDevelop
::IProjectController::FormatPlain)), 5);
const auto prettyFileName = ICore::self()->projectController()->prettyFileName(topContext->url().toUrl(), KDevelop::IProjectController::FormatPlain);
const auto messageText = i18n("%1 still needs an update!", prettyFileName);
auto* message = new Sublime::Message(messageText, Sublime::Message::Warning);
message->setAutoHide(0);
ICore::self()->uiController()->postMessage(message);
// return;
}
......
......@@ -43,8 +43,8 @@ PUBLIC
KDev::Util # util/path.h
KDev::Vcs
PRIVATE
KDev::Interfaces
KDev::Serialization
KDev::Sublime
KF5::KIOWidgets
Qt5::Concurrent
)
......
......@@ -33,7 +33,6 @@
#include <KIO/MkdirJob>
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageBox>
#include <KTextEditor/Document>
#include <interfaces/iproject.h>
......@@ -42,6 +41,8 @@
#include <vcs/vcsjob.h>
#include <interfaces/icore.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/iuicontroller.h>
#include <sublime/message.h>
using namespace KDevelop;
......@@ -75,9 +76,11 @@ bool KDevelop::removeUrl(const KDevelop::IProject* project, const QUrl& url, con
auto deleteJob = KIO::del(url);
KJobWidgets::setWindow(deleteJob, window);
if (!deleteJob->exec() && url.isLocalFile() && (QFileInfo::exists(url.toLocalFile()))) {
KMessageBox::error( window,
const QString messageText =
isFolder ? i18n( "Cannot remove folder <i>%1</i>.", url.toDisplayString(QUrl::PreferLocalFile) )
: i18n( "Cannot remove file <i>%1</i>.", url.toDisplayString(QUrl::PreferLocalFile) ) );
: i18n( "Cannot remove file <i>%1</i>.", url.toDisplayString(QUrl::PreferLocalFile) );
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
return true;
......@@ -93,8 +96,9 @@ bool KDevelop::createFile(const QUrl& file)
auto statJob = KIO::stat(file, KIO::StatJob::DestinationSide, 0);
KJobWidgets::setWindow(statJob, QApplication::activeWindow());
if (statJob->exec()) {
KMessageBox::error( QApplication::activeWindow(),
i18n( "The file <i>%1</i> already exists.", file.toDisplayString(QUrl::PreferLocalFile) ) );
const QString messageText = i18n("The file <i>%1</i> already exists.", file.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
......@@ -102,8 +106,9 @@ bool KDevelop::createFile(const QUrl& file)
auto uploadJob = KIO::storedPut(QByteArray("\n"), file, -1);
KJobWidgets::setWindow(uploadJob, QApplication::activeWindow());
if (!uploadJob->exec()) {
KMessageBox::error( QApplication::activeWindow(),
i18n( "Cannot create file <i>%1</i>.", file.toDisplayString(QUrl::PreferLocalFile) ) );
const QString messageText = i18n("Cannot create file <i>%1</i>.", file.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
}
......@@ -120,7 +125,9 @@ bool KDevelop::createFolder(const QUrl& folder)
auto mkdirJob = KIO::mkdir(folder);
KJobWidgets::setWindow(mkdirJob, QApplication::activeWindow());
if (!mkdirJob->exec()) {
KMessageBox::error( QApplication::activeWindow(), i18n( "Cannot create folder <i>%1</i>.", folder.toDisplayString(QUrl::PreferLocalFile) ) );
const QString messageText = i18n("Cannot create folder <i>%1</i>.", folder.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
return true;
......
......@@ -46,6 +46,7 @@ Boston, MA 02110-1301, USA.
#include <KTextEditor/AnnotationInterface>
#include <sublime/area.h>
#include <sublime/message.h>
#include <sublime/view.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/iprojectcontroller.h>
......@@ -1256,8 +1257,10 @@ void DocumentController::vcsAnnotateCurrentDocument()
helper->annotation();
}
else {
KMessageBox::error(nullptr, i18n("Could not annotate the document because it is not "
"part of a version-controlled project."));
const QString messageText =
i18n("Could not annotate the document because it is not part of a version-controlled project.");
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
}
......
......@@ -44,8 +44,10 @@
#include <interfaces/iplugin.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/iruncontroller.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/isession.h>
#include <project/projectmodel.h>
#include <sublime/message.h>
#include <util/path.h>
#include <serialization/indexedstring.h>
#include <vcs/interfaces/ibasicversioncontrol.h>
......@@ -233,10 +235,12 @@ public:
KIO::StatJob* statJob = KIO::stat( projectFile.toUrl(), KIO::HideProgressInfo );
if ( !statJob->exec() ) //be sync for right now
{
KMessageBox::sorry( Core::self()->uiControllerInternal()->defaultMainWindow(),
i18n( "Unable to load the project file %1.<br>"
"The project has been removed from the session.",
projectFile.pathOrUrl() ) );
const QString messageText =
i18n("Unable to load the project file %1.<br>"
"The project has been removed from the session.",
projectFile.pathOrUrl());
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
......@@ -257,11 +261,11 @@ public:
KIO::SimpleJob* mkdirJob = KIO::mkdir( dir );
if( !mkdirJob->exec() )
{
KMessageBox::sorry(
Core::self()->uiController()->activeMainWindow(),
const QString messageText =
i18n("Unable to create hidden dir (%1) for developer file",
dir.toDisplayString(QUrl::PreferLocalFile) )
);
dir.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
}
......@@ -274,9 +278,9 @@ public:
{
qCDebug(SHELL) << "Job failed:" << copyJob->errorString();
KMessageBox::sorry( Core::self()->uiController()->activeMainWindow(),
i18n("Unable to get project file: %1",
projectFile.pathOrUrl() ) );
const QString messageText = i18n("Unable to get project file: %1", projectFile.pathOrUrl());
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
......@@ -313,9 +317,10 @@ public:
progress->projectName = name;
if( Core::self()->projectController()->isProjectNameUsed( name ) )
{
KMessageBox::sorry( Core::self()->uiControllerInternal()->defaultMainWindow(),
i18n( "Could not load %1, a project with the same name '%2' is already open.",
projectFile.pathOrUrl(), name ) );
const QString messageText =
i18n("Could not load %1, a project with the same name '%2' is already open.", projectFile.pathOrUrl(), name);
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
qCWarning(SHELL) << "Trying to open a project with a name that is already used by another open project";
return true;
......@@ -343,17 +348,20 @@ public:
iface = manager->extension<IProjectFileManager>();
else
{
KMessageBox::sorry( Core::self()->uiControllerInternal()->defaultMainWindow(),
i18n( "Could not load project management plugin <b>%1</b>.<br> Check that the required programs are installed,"
" or see console output for more information.",
managerSetting ) );
const QString messageText =
i18n("Could not load project management plugin <b>%1</b>.<br>Check that the required programs are installed,"
" or see console output for more information.", managerSetting);
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
manager = nullptr;
return nullptr;
}
if (iface == nullptr)
{
KMessageBox::sorry( Core::self()->uiControllerInternal()->defaultMainWindow(),
i18n( "project importing plugin (%1) does not support the IProjectFileManager interface.", managerSetting ) );
const QString messageText =
i18n("The project importing plugin (%1) does not support the IProjectFileManager interface.", managerSetting);
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
delete manager;
manager = nullptr;
}
......@@ -404,8 +412,8 @@ public:
topItem = fileManager->import( project );
if( !topItem )
{
KMessageBox::sorry( Core::self()->uiControllerInternal()->defaultMainWindow(),
i18n("Could not open project") );
auto* message = new Sublime::Message(i18n("Could not open project."), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return false;
}
......
......@@ -46,12 +46,12 @@ Boston, MA 02110-1301, USA.
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageBox>
#include <KPassivePopup>
#include <KRecentFilesAction>
#include <KSharedConfig>
#include <KStandardAction>
#include <sublime/area.h>
#include <sublime/message.h>
#include <interfaces/iplugin.h>
#include <interfaces/isession.h>
#include <interfaces/context.h>
......@@ -66,6 +66,7 @@ Boston, MA 02110-1301, USA.
#include <projectconfigpage.h>
#include <language/backgroundparser/parseprojectjob.h>
#include <interfaces/iruncontroller.h>
#include <sublime/message.h>
#include <util/scopeddialog.h>
#include <vcs/widgets/vcsdiffpatchsources.h>
#include <vcs/widgets/vcscommitdialog.h>
......@@ -313,18 +314,20 @@ public:
if ( !url.isValid() )
{
KMessageBox::error(Core::self()->uiControllerInternal()->activeMainWindow(),
i18n("Invalid Location: %1", url.toDisplayString(QUrl::PreferLocalFile)));
const QString messageText = i18n("Invalid Location: %1", url.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return;
}
if ( m_currentlyOpening.contains(url))
{
qCDebug(SHELL) << "Already opening " << url << ". Aborting.";
KPassivePopup::message( i18n( "Project already being opened"),
i18n( "Already opening %1, not opening again",
url.toDisplayString(QUrl::PreferLocalFile) ),
m_core->uiController()->activeMainWindow() );
const QString messageText =
i18n("Already opening %1, not opening again", url.toDisplayString(QUrl::PreferLocalFile));
auto* message = new Sublime::Message(messageText, Sublime::Message::Information);
message->setAutoHide(0);
ICore::self()->uiController()->postMessage(message);
return;
}
......@@ -511,8 +514,9 @@ QUrl ProjectDialogProvider::askProjectConfigLocation(bool fetch, const QUrl& sta
delJob->exec();
if (!writeProjectSettingsToConfigFile(projectFileUrl, dlg)) {
KMessageBox::error(d->m_core->uiControllerInternal()->defaultMainWindow(),
i18n("Unable to create configuration file %1", projectFileUrl.url()));
const QString messageText = i18n("Unable to create configuration file %1", projectFileUrl.url());
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return QUrl();
}
}
......@@ -777,10 +781,12 @@ void ProjectController::openProjectForUrlSlot(bool) {
if(!project) {
openProjectForUrl(url);
}else{
KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("Project already open: %1", project->name()));
auto* message = new Sublime::Message(i18n("Project already open: %1", project->name()), Sublime::Message::Error);
Core::self()->uiController()->postMessage(message);
}
}else{
KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("No active document"));
auto* message = new Sublime::Message(i18n("No active document"), Sublime::Message::Error);
Core::self()->uiController()->postMessage(message);
}
}
......@@ -931,8 +937,10 @@ bool ProjectController::fetchProjectFromUrl(const QUrl& repoUrl, FetchFlags fetc
}
if (!vcsOrProviderPlugin) {
if (fetchFlags.testFlag(FetchShowErrorIfNotSupported)) {
KMessageBox::error(Core::self()->uiController()->activeMainWindow(),
i18n("No enabled plugin supports this repository URL: %1", repoUrl.toDisplayString()));
const QString messageText =
i18n("No enabled plugin supports this repository URL: %1", repoUrl.toDisplayString());
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
return false;
}
......
......@@ -27,7 +27,6 @@ Boston, MA 02110-1301, USA.
#include <KActionCollection>
#include <KActionMenu>
#include <KDialogJobUiDelegate>
#include <KMessageBox>
#include <KLocalizedString>
#include <KSelectAction>
......@@ -38,6 +37,7 @@ Boston, MA 02110-1301, USA.
#include <interfaces/launchconfigurationtype.h>
#include <outputview/outputjob.h>
#include <project/projectmodel.h>
#include <sublime/message.h>
#include "core.h"
#include "uicontroller.h"
......@@ -417,10 +417,9 @@ KJob* RunController::execute(const QString& runMode, ILaunchConfiguration* launc
ILauncher* launcher = run->type()->launcherForId( launcherId );
if( !launcher ) {
KMessageBox::error(
qApp->activeWindow(),
i18n("The current launch configuration does not support the '%1' mode.", runMode),
QString());
const QString messageText = i18n("The current launch configuration does not support the '%1' mode.", runMode);
auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
return nullptr;
}
......@@ -733,17 +732,8 @@ void KDevelop::RunController::finished(KJob * job)
default:
{
///WARNING: do *not* use a nested event loop here, it might cause
/// random crashes later on, see e.g.:
/// https://bugs.kde.org/show_bug.cgi?id=309811
auto dialog = new QDialog(qApp->activeWindow());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowTitle(i18n("Process Error"));
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, dialog);
KMessageBox::createKMessageBox(dialog, buttonBox, QMessageBox::Warning,
job->errorString(), QStringList(),
QString(), nullptr, KMessageBox::NoExec);
dialog->show();
auto* message = new Sublime::Message(job->errorString(), Sublime::Message::Error);
Core::self()->uiController()->postMessage(message);
}
}
}
......
......@@ -32,12 +32,12 @@
#include <KIO/StoredTransferJob>
#include <KLocalizedString>
#include <KMessageBox>
#include <interfaces/icore.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/isourceformatter.h>
#include <sublime/message.h>
using namespace KDevelop;
......@@ -145,9 +145,12 @@ void SourceFormatterJob::formatFile(const QUrl& url)
auto putJob = KIO::storedPut(text.toLocal8Bit(), url, -1, KIO::Overwrite);
// see getJob
if (!putJob->exec())
// TODO: integrate with job error reporting, use showErrorMessage?
KMessageBox::error(nullptr, putJob->errorString());
} else
KMessageBox::error(nullptr, getJob->errorString());
if (!putJob->exec()) {
auto* message = new Sublime::Message(putJob->errorString(), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
} else {
auto* message = new Sublime::Message(getJob->errorString(), Sublime::Message::Error);
ICore::self()->uiController()->postMessage(message);
}
}
......@@ -37,6 +37,7 @@
#include <sublime/holdupdates.h>
#include <interfaces/itoolviewactionlistener.h>
#include <sublime/message.h>
#include <util/scopeddialog.h>
#include "core.h"
......@@ -766,6 +767,17 @@ void UiController::showErrorMessage(const QString& message, int timeout)
QMetaObject::invokeMethod(mw, "showErrorMessage", Q_ARG(QString, message), Q_ARG(int, timeout));
}
void UiController::postMessage(Sublime::Message* message)
{
// if Core has flag Core::NoUi there also is no window, so catched as well here
Sublime::MainWindow* window = activeSublimeWindow();
if (!window) {
delete message;
return;
}
QMetaObject::invokeMethod(window, "postMessage", Q_ARG(Sublime::Message*, message));
}
const QHash< IToolViewFactory*, Sublime::ToolDocument* >& UiController::factoryDocuments() const
{
Q_D(const UiController);
......
......@@ -86,6 +86,7 @@ public:
void registerStatus(QObject* status) override;
void showErrorMessage(const QString& message, int timeout) override;
void postMessage(Sublime::Message* message) override;
/// Returns list of available view factories together with their ToolDocuments.
/// @see addToolView(), removeToolView(), findToolView()
......
......@@ -27,6 +27,11 @@ set(sublime_LIB_SRCS
idealtoolbutton.cpp
idealdockwidget.cpp
idealbuttonbarwidget.cpp
message.cpp
messagewidget/messagewidget.cpp
messagewidget/messagewidgetanimation.cpp
messagewidget/messagewidgetfadeeffect.cpp
)
declare_qt_logging_category(sublime_LIB_SRCS
TYPE LIBRARY
......@@ -49,6 +54,7 @@ install(FILES
document.h
mainwindow.h
mainwindowoperator.h
message.h
urldocument.h
sublimedefs.h
tooldocument.h
......
......@@ -378,6 +378,13 @@ bool MainWindow::queryClose()
return KParts::MainWindow::queryClose();
}
void MainWindow::postMessage(Message* message)
{
Q_D(MainWindow);
d->postMessage(message);
}
QString MainWindow::screenKey() const
{
const int scnum = QApplication::desktop()->screenNumber(parentWidget());
......
......@@ -34,6 +34,7 @@ class View;
class Controller;
class MainWindowOperator;
class ViewBarContainer;
class Message;
class MainWindowPrivate;
/**
......@@ -112,6 +113,8 @@ public:
public Q_SLOTS:
/**Shows the @p view and makes it active, focusing it by default).*/
void activateView(Sublime::View *view, bool focus = true);
/** Shows the @p message in the message area */
void postMessage(Sublime::Message* message);
/**Loads size/toolbar/menu/statusbar settings to the global configuration file.
Reimplement in subclasses to load more and don't forget to call inherited method.*/
virtual void loadSettings();
......
/***************************************************************************
* Copyright 2006-2009 Alexander Dymo <adymo@kdevelop.org> *
* Copyright 2012 Dominik Haumann <dhaumann@kde.org> *
* Copyright 2020 Friedrich W. H. Kossebau <kossebau@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
......@@ -40,6 +42,8 @@
#include "idealcontroller.h"
#include "holdupdates.h"
#include "idealbuttonbarwidget.h"
#include "message.h"
#include "messagewidget/messagewidget.h"
#include <debug.h>
class IdealToolBar : public QToolBar
......@@ -156,6 +160,9 @@ MainWindowPrivate::MainWindowPrivate(MainWindow *w, Controller* controller)
layout->setMargin(0);
centralWidget->setLayout(layout);
messageWidget = new MessageWidget(nullptr);
layout->addWidget(messageWidget);