diff --git a/addons/backtracebrowser/btfileindexer.cpp b/addons/backtracebrowser/btfileindexer.cpp index 7a544b7ce..c3bdad4cd 100644 --- a/addons/backtracebrowser/btfileindexer.cpp +++ b/addons/backtracebrowser/btfileindexer.cpp @@ -1,93 +1,92 @@ /* This file is part of the KDE project Copyright 2008-2014 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "btfileindexer.h" #include "btdatabase.h" #include #include BtFileIndexer::BtFileIndexer(KateBtDatabase *database) - : QThread() - , cancelAsap(false) + : cancelAsap(false) , db(database) { } BtFileIndexer::~BtFileIndexer() { } void BtFileIndexer::setSearchPaths(const QStringList &urls) { searchPaths.clear(); for (const QString &url : urls) { if (!searchPaths.contains(url)) { searchPaths += url; } } } void BtFileIndexer::setFilter(const QStringList &fileFilter) { filter = fileFilter; qDebug() << filter; } void BtFileIndexer::run() { if (filter.empty()) { qDebug() << "Filter is empty. Aborting."; return; } cancelAsap = false; for (int i = 0; i < searchPaths.size(); ++i) { if (cancelAsap) { break; } indexFiles(searchPaths[i]); } qDebug() << QStringLiteral("Backtrace file database contains %1 files").arg(db->size()); } void BtFileIndexer::cancel() { cancelAsap = true; } void BtFileIndexer::indexFiles(const QString &url) { QDir dir(url); if (!dir.exists()) { return; } QStringList files = dir.entryList(filter, QDir::Files | QDir::NoSymLinks | QDir::Readable | QDir::NoDotAndDotDot | QDir::CaseSensitive); db->add(url, files); QStringList subdirs = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::Readable | QDir::NoDotAndDotDot | QDir::CaseSensitive); for (int i = 0; i < subdirs.size(); ++i) { if (cancelAsap) { break; } indexFiles(url + QLatin1Char('/') + subdirs[i]); } } // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/close-except-like/close_except_plugin.cpp b/addons/close-except-like/close_except_plugin.cpp index 3554a8600..d74be0c54 100644 --- a/addons/close-except-like/close_except_plugin.cpp +++ b/addons/close-except-like/close_except_plugin.cpp @@ -1,276 +1,275 @@ /** * \file * * \brief Kate Close Except/Like plugin implementation * * Copyright (C) 2012 Alex Turbov * * \date Thu Mar 8 08:13:43 MSK 2012 -- Initial design */ /* * KateCloseExceptPlugin is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * KateCloseExceptPlugin is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ // Project specific includes #include "close_except_plugin.h" #include "close_confirm_dialog.h" // Standard includes #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(CloseExceptPluginFactory, "katecloseexceptplugin.json", registerPlugin();) namespace kate { // BEGIN CloseExceptPlugin CloseExceptPlugin::CloseExceptPlugin(QObject *application, const QList &) : KTextEditor::Plugin(application) { } QObject *CloseExceptPlugin::createView(KTextEditor::MainWindow *parent) { return new CloseExceptPluginView(parent, this); } void CloseExceptPlugin::readSessionConfig(const KConfigGroup &config) { const KConfigGroup scg(&config, QStringLiteral("menu")); m_show_confirmation_needed = scg.readEntry(QStringLiteral("ShowConfirmation"), true); } void CloseExceptPlugin::writeSessionConfig(KConfigGroup &config) { KConfigGroup scg(&config, QStringLiteral("menu")); scg.writeEntry(QStringLiteral("ShowConfirmation"), m_show_confirmation_needed); scg.sync(); } // END CloseExceptPlugin // BEGIN CloseExceptPluginView CloseExceptPluginView::CloseExceptPluginView(KTextEditor::MainWindow *mw, CloseExceptPlugin *plugin) : QObject(mw) - , KXMLGUIClient() , m_plugin(plugin) , m_show_confirmation_action(new KToggleAction(i18nc("@action:inmenu", "Show Confirmation"), this)) , m_except_menu(new KActionMenu(i18nc("@action:inmenu close docs except the following...", "Close Except"), this)) , m_like_menu(new KActionMenu(i18nc("@action:inmenu close docs like the following...", "Close Like"), this)) , m_mainWindow(mw) { KXMLGUIClient::setComponentName(QStringLiteral("katecloseexceptplugin"), i18n("Close Except/Like Plugin")); setXMLFile(QStringLiteral("ui.rc")); actionCollection()->addAction(QStringLiteral("file_close_except"), m_except_menu); actionCollection()->addAction(QStringLiteral("file_close_like"), m_like_menu); connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::documentCreated, this, &CloseExceptPluginView::documentCreated); // Configure toggle action and connect it to update state m_show_confirmation_action->setChecked(m_plugin->showConfirmationNeeded()); connect(m_show_confirmation_action.data(), &KToggleAction::toggled, m_plugin, &CloseExceptPlugin::toggleShowConfirmation); // connect(m_mainWindow, &KTextEditor::MainWindow::viewCreated, this, &CloseExceptPluginView::viewCreated); // Fill menu w/ currently opened document masks/groups updateMenu(); m_mainWindow->guiFactory()->addClient(this); } CloseExceptPluginView::~CloseExceptPluginView() { m_mainWindow->guiFactory()->removeClient(this); } void CloseExceptPluginView::viewCreated(KTextEditor::View *view) { connectToDocument(view->document()); updateMenu(); } void CloseExceptPluginView::documentCreated(KTextEditor::Editor *, KTextEditor::Document *document) { connectToDocument(document); updateMenu(); } void CloseExceptPluginView::connectToDocument(KTextEditor::Document *document) { // Subscribe self to document close and name changes connect(document, &KTextEditor::Document::aboutToClose, this, &CloseExceptPluginView::updateMenuSlotStub); connect(document, &KTextEditor::Document::documentNameChanged, this, &CloseExceptPluginView::updateMenuSlotStub); connect(document, &KTextEditor::Document::documentUrlChanged, this, &CloseExceptPluginView::updateMenuSlotStub); } void CloseExceptPluginView::updateMenuSlotStub(KTextEditor::Document *) { updateMenu(); } void CloseExceptPluginView::appendActionsFrom(const std::set &paths, actions_map_type &actions, KActionMenu *menu, CloseFunction closeFunction) { for (const QUrl &path : paths) { QString action = path.path() + QLatin1Char('*'); actions[action] = QPointer(new QAction(action, menu)); menu->addAction(actions[action]); connect(actions[action].data(), &QAction::triggered, this, [this, closeFunction, action]() { (this->*closeFunction)(action); }); } } void CloseExceptPluginView::appendActionsFrom(const std::set &masks, actions_map_type &actions, KActionMenu *menu, CloseFunction closeFunction) { for (const QString &mask : masks) { QString action = mask.startsWith(QLatin1Char('*')) ? mask : mask + QLatin1Char('*'); actions[action] = QPointer(new QAction(action, menu)); menu->addAction(actions[action]); connect(actions[action].data(), &QAction::triggered, this, [this, closeFunction, action]() { (this->*closeFunction)(action); }); } } void CloseExceptPluginView::updateMenu(const std::set &paths, const std::set &masks, actions_map_type &actions, KActionMenu *menu, CloseFunction closeFunction) { // turn menu ON or OFF depending on collected results menu->setEnabled(!paths.empty()); // Clear previous menus for (actions_map_type::iterator it = actions.begin(), last = actions.end(); it != last;) { menu->removeAction(*it); actions.erase(it++); } // Form a new one appendActionsFrom(paths, actions, menu, closeFunction); if (!masks.empty()) { if (!paths.empty()) menu->addSeparator(); // Add separator between paths and file's ext filters appendActionsFrom(masks, actions, menu, closeFunction); } // Append 'Show Confirmation' toggle menu item menu->addSeparator(); // Add separator between paths and show confirmation menu->addAction(m_show_confirmation_action); } void CloseExceptPluginView::updateMenu() { const QList &docs = KTextEditor::Editor::instance()->application()->documents(); if (docs.size() < 2) { // qDebug() << "No docs r (or the only) opened right now --> disable menu"; m_except_menu->setEnabled(false); m_except_menu->addSeparator(); m_like_menu->setEnabled(false); m_like_menu->addSeparator(); /// \note It seems there is always a document present... it named \em 'Untitled' } else { // Iterate over documents and form a set of candidates typedef std::set paths_set_type; typedef std::set paths_set_type_masks; paths_set_type doc_paths; paths_set_type_masks masks; for (KTextEditor::Document *document : docs) { const QString &ext = QFileInfo(document->url().path()).completeSuffix(); if (!ext.isEmpty()) masks.insert(QStringLiteral("*.") + ext); doc_paths.insert(KIO::upUrl(document->url())); } paths_set_type paths = doc_paths; // qDebug() << "stage #1: Collected" << paths.size() << "paths and" << masks.size() << "masks"; // Add common paths to the collection for (paths_set_type::iterator it = doc_paths.begin(), last = doc_paths.end(); it != last; ++it) { for (QUrl url = *it; (!url.path().isEmpty()) && url.path() != QLatin1String("/"); url = KIO::upUrl(url)) { paths_set_type::iterator not_it = it; for (++not_it; not_it != last; ++not_it) if (!not_it->path().startsWith(url.path())) break; if (not_it == last) { paths.insert(url); break; } } } // qDebug() << "stage #2: Collected" << paths.size() << "paths and" << masks.size() << "masks"; // updateMenu(paths, masks, m_except_actions, m_except_menu, &CloseExceptPluginView::closeExcept); updateMenu(paths, masks, m_like_actions, m_like_menu, &CloseExceptPluginView::closeLike); } } void CloseExceptPluginView::close(const QString &item, const bool close_if_match) { QChar asterisk = QLatin1Char('*'); assert("Parameter seems invalid! Is smth has changed in the code?" && !item.isEmpty() && (item[0] == asterisk || item[item.size() - 1] == asterisk)); const bool is_path = item[0] != asterisk; const QString mask = is_path ? item.left(item.size() - 1) : item; // qDebug() << "Going to close items [" << close_if_match << "/" << is_path << "]: " << mask; QList docs2close; const QList &docs = KTextEditor::Editor::instance()->application()->documents(); for (KTextEditor::Document *document : docs) { const QString &path = KIO::upUrl(document->url()).path(); /// \note Take a dot in account, so \c *.c would not match for \c blah.kcfgc const QString &ext = QLatin1Char('.') + QFileInfo(document->url().fileName()).completeSuffix(); const bool match = (!is_path && mask.endsWith(ext)) || (is_path && path.startsWith(mask)); if (match == close_if_match) { // qDebug() << "*** Will close: " << document->url(); docs2close.push_back(document); } } if (docs2close.isEmpty()) { displayMessage(i18nc("@title:window", "Error"), i18nc("@info:tooltip", "No files to close ..."), KTextEditor::Message::Error); return; } // Show confirmation dialog if needed const bool removeNeeded = !m_plugin->showConfirmationNeeded() || CloseConfirmDialog(docs2close, m_show_confirmation_action, qobject_cast(this)).exec(); if (removeNeeded) { if (docs2close.isEmpty()) { displayMessage(i18nc("@title:window", "Error"), i18nc("@info:tooltip", "No files to close ..."), KTextEditor::Message::Error); } else { // Close 'em all! KTextEditor::Editor::instance()->application()->closeDocuments(docs2close); updateMenu(); displayMessage(i18nc("@title:window", "Done"), i18np("%1 file closed", "%1 files closed", docs2close.size()), KTextEditor::Message::Positive); } } } void CloseExceptPluginView::displayMessage(const QString &title, const QString &msg, KTextEditor::Message::MessageType level) { KTextEditor::View *kv = m_mainWindow->activeView(); if (!kv) return; delete m_infoMessage; m_infoMessage = new KTextEditor::Message(xi18nc("@info", "%1%2", title, msg), level); m_infoMessage->setWordWrap(true); m_infoMessage->setPosition(KTextEditor::Message::TopInView); m_infoMessage->setAutoHide(5000); m_infoMessage->setAutoHideMode(KTextEditor::Message::Immediate); m_infoMessage->setView(kv); kv->document()->postMessage(m_infoMessage); } // END CloseExceptPluginView } // namespace kate #include "close_except_plugin.moc" // kate: hl C++11/Qt4; diff --git a/addons/close-except-like/close_except_plugin.h b/addons/close-except-like/close_except_plugin.h index 63f0d1f7a..f9c415852 100644 --- a/addons/close-except-like/close_except_plugin.h +++ b/addons/close-except-like/close_except_plugin.h @@ -1,132 +1,132 @@ /** * \file * * \brief Declare Kate's Close Except/Like plugin classes * * Copyright (C) 2012 Alex Turbov * * \date Thu Mar 8 08:13:43 MSK 2012 -- Initial design */ /* * KateCloseExceptPlugin is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * KateCloseExceptPlugin is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef __SRC__CLOSE_EXCEPT_PLUGIN_H__ #define __SRC__CLOSE_EXCEPT_PLUGIN_H__ // Project specific includes // Standard includes #include #include #include #include #include #include #include #include #include #include #include #include namespace kate { class CloseExceptPlugin; // forward declaration /** * \brief Plugin to close docs grouped by extension or location */ class CloseExceptPluginView : public QObject, public KXMLGUIClient { Q_OBJECT typedef QMap> actions_map_type; public: /// Default constructor CloseExceptPluginView(KTextEditor::MainWindow *, CloseExceptPlugin *); /// Destructor ~CloseExceptPluginView() override; private Q_SLOTS: void viewCreated(KTextEditor::View *); void documentCreated(KTextEditor::Editor *, KTextEditor::Document *); void updateMenuSlotStub(KTextEditor::Document *); void close(const QString &, const bool); void closeExcept(const QString &item) { close(item, false); } void closeLike(const QString &item) { close(item, true); } private: void displayMessage(const QString &, const QString &, KTextEditor::Message::MessageType); void connectToDocument(KTextEditor::Document *); void updateMenu(); using CloseFunction = void (CloseExceptPluginView::*)(const QString &); void updateMenu(const std::set &, const std::set &, actions_map_type &, KActionMenu *, CloseFunction); void appendActionsFrom(const std::set &, actions_map_type &, KActionMenu *, CloseFunction); void appendActionsFrom(const std::set &masks, actions_map_type &actions, KActionMenu *menu, CloseFunction); CloseExceptPlugin *m_plugin; QPointer m_show_confirmation_action; QPointer m_except_menu; QPointer m_like_menu; actions_map_type m_except_actions; actions_map_type m_like_actions; KTextEditor::MainWindow *m_mainWindow; QPointer m_infoMessage; }; /** * \brief Plugin view class */ class CloseExceptPlugin : public KTextEditor::Plugin, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) public: /// Default constructor CloseExceptPlugin(QObject * = nullptr, const QList & = QList()); /// Destructor ~CloseExceptPlugin() override { } /// Create a new view of this plugin for the given main window QObject *createView(KTextEditor::MainWindow *) override; /// \name Plugin interface implementation //@{ void readSessionConfig(const KConfigGroup &) override; void writeSessionConfig(KConfigGroup &) override; //@} bool showConfirmationNeeded() const { return m_show_confirmation_needed; } public Q_SLOTS: void toggleShowConfirmation(bool flag) { m_show_confirmation_needed = flag; } private: - bool m_show_confirmation_needed; + bool m_show_confirmation_needed = false; }; } // namespace kate #endif // __SRC__CLOSE_EXCEPT_PLUGIN_H__ diff --git a/addons/filebrowser/katebookmarkhandler.cpp b/addons/filebrowser/katebookmarkhandler.cpp index 9b1a401ea..8a5b40727 100644 --- a/addons/filebrowser/katebookmarkhandler.cpp +++ b/addons/filebrowser/katebookmarkhandler.cpp @@ -1,70 +1,69 @@ /* This file is part of the KDE project Copyright (C) xxxx KFile Authors Copyright (C) 2002 Anders Lund Copyright (C) 2009 Dominik Haumann Copyright (C) 2007 Mirko Stocker This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katebookmarkhandler.h" #include "katefilebrowser.h" #include #include #include KateBookmarkHandler::KateBookmarkHandler(KateFileBrowser *parent, QMenu *kpopupmenu) : QObject(parent) - , KBookmarkOwner() , mParent(parent) , m_menu(kpopupmenu) { setObjectName(QStringLiteral("KateBookmarkHandler")); if (!m_menu) m_menu = new QMenu(parent); QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kate/fsbookmarks.xml")); if (file.isEmpty()) file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kate/fsbookmarks.xml"); KBookmarkManager *manager = KBookmarkManager::managerForFile(file, QStringLiteral("kate")); manager->setUpdate(true); m_bookmarkMenu = new KBookmarkMenu(manager, this, m_menu, parent->actionCollection()); } KateBookmarkHandler::~KateBookmarkHandler() { delete m_bookmarkMenu; } QUrl KateBookmarkHandler::currentUrl() const { return mParent->dirOperator()->url(); } QString KateBookmarkHandler::currentTitle() const { return currentUrl().url(); } void KateBookmarkHandler::openBookmark(const KBookmark &bm, Qt::MouseButtons, Qt::KeyboardModifiers) { emit openUrl(bm.url().url()); } // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/filebrowser/katefilebrowser.h b/addons/filebrowser/katefilebrowser.h index 41c6e585c..7e1d28eaa 100644 --- a/addons/filebrowser/katefilebrowser.h +++ b/addons/filebrowser/katefilebrowser.h @@ -1,117 +1,117 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund Copyright (C) 2007 Mirko Stocker Copyright (C) 2009 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KATE_FILEBROWSER_H #define KATE_FILEBROWSER_H #include #include #include #include class KateBookmarkHandler; class KActionCollection; class KDirOperator; class KFileItem; class KHistoryComboBox; class KToolBar; class KConfigGroup; class KUrlNavigator; class QAbstractItemView; class QAction; /* The kate file selector presents a directory view, in which the default action is to open the activated file. Additionally, a toolbar for managing the kdiroperator widget + sync that to the directory of the current file is available, as well as a filter widget allowing to filter the displayed files using a name filter. */ class KateFileBrowser : public QWidget { Q_OBJECT public: explicit KateFileBrowser(KTextEditor::MainWindow *mainWindow = nullptr, QWidget *parent = nullptr); ~KateFileBrowser() override; void readSessionConfig(const KConfigGroup &config); void writeSessionConfig(KConfigGroup &config); void setupToolbar(); void setView(KFile::FileView); KDirOperator *dirOperator() { return m_dirOperator; } KActionCollection *actionCollection() { return m_actionCollection; } public Q_SLOTS: void slotFilterChange(const QString &); void setDir(const QUrl &); void setDir(const QString &url) { setDir(QUrl(url)); } void selectorViewChanged(QAbstractItemView *); private Q_SLOTS: void fileSelected(const KFileItem & /*file*/); void updateDirOperator(const QUrl &u); void updateUrlNavigator(const QUrl &u); void setActiveDocumentDir(); void autoSyncFolder(); protected: QUrl activeDocumentUrl(); void openSelectedFiles(); void setupActions(); public: KTextEditor::MainWindow *mainWindow() { return m_mainWindow; } private: KToolBar *m_toolbar; KActionCollection *m_actionCollection; - KateBookmarkHandler *m_bookmarkHandler; + KateBookmarkHandler *m_bookmarkHandler = nullptr; KUrlNavigator *m_urlNavigator; KDirOperator *m_dirOperator; KHistoryComboBox *m_filter; - QAction *m_autoSyncFolder; + QAction *m_autoSyncFolder = nullptr; KTextEditor::MainWindow *m_mainWindow; }; #endif // KATE_FILEBROWSER_H // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/filetree/autotests/filetree_model_test.cpp b/addons/filetree/autotests/filetree_model_test.cpp index 16420733a..b5b9da322 100644 --- a/addons/filetree/autotests/filetree_model_test.cpp +++ b/addons/filetree/autotests/filetree_model_test.cpp @@ -1,574 +1,573 @@ /* This file is part of the KDE project * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "filetree_model_test.h" #include "katefiletreemodel.h" #include "document_dummy.h" #include QTEST_GUILESS_MAIN(FileTreeModelTest) // BEGIN ResultNode class ResultNode { public: ResultNode() = default; // root node ResultNode(const ResultNode &other) : name(other.name) , dir(other.dir) , children(other.children) { } ResultNode(const char *_name, const bool _dir = false) : ResultNode(QString::fromLatin1(_name), _dir) { } ResultNode(const QString &_name, const bool _dir = false) : name(_name) , dir(_dir) - , children() { } ResultNode &operator<<(const ResultNode &node) { children << node; return *this; } bool operator!=(const ResultNode &other) const { return !(*this == other); } bool operator==(const ResultNode &other) const { return (other.name == name) && (other.dir == dir) && (other.children == children); } friend QDebug operator<<(QDebug s, const ResultNode &node) { s << node.toString(); return s; } friend void debugOutput(QString &s, const ResultNode &rootNode, const int level = 0) { for (int i = 0; i < level; i++) { s += QLatin1String(" "); } const QString name = rootNode.name.isEmpty() ? QStringLiteral("ROOT") : rootNode.name; s += QLatin1String("( ") + name; if (rootNode.dir) { s += QLatin1String(", {D}"); } if (rootNode.children.isEmpty()) { s += QLatin1String(" )"); } else { s += QLatin1String(",\n"); for (int i = 0; i < rootNode.children.size(); i++) { const ResultNode &node = rootNode.children[i]; debugOutput(s, node, level + 1); if ((i + 1) < rootNode.children.size()) { s += QLatin1Char('\n'); } } s += (level == 0) ? QLatin1String("\n);") : QLatin1String(")"); } } QString toString() const { QString out; debugOutput(out, *this, 0); return out; } QString name; bool dir = true; QList children; }; Q_DECLARE_METATYPE(ResultNode) namespace QTest { inline bool qCompare(const ResultNode &t1, const ResultNode &t2, const char *actual, const char *expected, const char *file, int line) { /* compare_helper is not helping that much, we need to prepare copy of data */ const QByteArray a = t1.toString().toLatin1(); const QByteArray b = t2.toString().toLatin1(); char *val1 = new char[a.size() + 1]; char *val2 = new char[b.size() + 1]; memcpy(val1, a.constData(), a.size() + 1); memcpy(val2, b.constData(), b.size() + 1); return compare_helper(t1 == t2, "Compared ResultNode trees are not the same", val1, val2, actual, expected, file, line); } } // END ResultNode void FileTreeModelTest::initTestCase() { } void FileTreeModelTest::cleanupTestCase() { } void FileTreeModelTest::init() { } void FileTreeModelTest::cleanup() { } void FileTreeModelTest::basic() { QScopedPointer d1(new DummyDocument()); QScopedPointer d2(new DummyDocument()); KateFileTreeModel m(this); QCOMPARE(m.rowCount(QModelIndex()), 0); m.documentOpened(d1.data()); QCOMPARE(m.rowCount(QModelIndex()), 1); m.documentOpened(d2.data()); QCOMPARE(m.rowCount(QModelIndex()), 2); } void FileTreeModelTest::buildTree_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("strangers") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///b/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("lvl1 strangers") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("stairs") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/bar.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << ResultNode("bar.txt"))); QTest::newRow("reverse stairs") << (QList() << new DummyDocument("file:///c/bar.txt") << new DummyDocument("file:///c/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << ResultNode("bar.txt") << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("matching") << (QList() << new DummyDocument("file:///a/x/foo.txt") << new DummyDocument("file:///b/x/bar.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("x", true) << ResultNode("foo.txt"))) << (ResultNode("b", true) << (ResultNode("x", true) << ResultNode("bar.txt")))); QTest::newRow("matching even more") << (QList() << new DummyDocument("file:///a/x/y/z/foo.txt") << new DummyDocument("file:///b/x/y/z/bar.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("x", true) << (ResultNode("y", true) << (ResultNode("z", true) << ResultNode("foo.txt"))))) << (ResultNode("b", true) << (ResultNode("x", true) << (ResultNode("y", true) << (ResultNode("z", true) << ResultNode("bar.txt")))))); QTest::newRow("matching with booby trap") << (QList() << new DummyDocument("file:///x/y/foo.txt") << new DummyDocument("file:///c/x/bar.txt") << new DummyDocument("file:///d/y/baz.txt")) << (ResultNode() << (ResultNode("x", true) << (ResultNode("y", true) << ResultNode("foo.txt"))) << (ResultNode("d", true) << (ResultNode("y", true) << ResultNode("baz.txt"))) << (ResultNode("c", true) << (ResultNode("x", true) << ResultNode("bar.txt")))); QTest::newRow("branches") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("branches (more)") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/c/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("c", true) << ResultNode("bar.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("bug347578") << (QList() << new DummyDocument("file:///f/g/a/b/c/d/e.txt") << new DummyDocument("file:///f/g/a/t/b/c/d/e.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("b", true) << (ResultNode("c", true) << (ResultNode("d", true) << ResultNode("e.txt")))) << (ResultNode("t", true) << (ResultNode("b", true) << (ResultNode("c", true) << (ResultNode("d", true) << ResultNode("e.txt"))))))); QTest::newRow("levels") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("d", true) << ResultNode("foo.txt"))); QTest::newRow("remote simple") << (QList() << new DummyDocument("http://example.org/foo.txt")) << (ResultNode() << (ResultNode("[example.org]", true) << ResultNode("foo.txt"))); QTest::newRow("remote nested") << (QList() << new DummyDocument("http://example.org/a/foo.txt")) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt"))); /* NOTE: this one is also not completely ok, is it? * on other hand, it would get confusing or overly leveled if opening * something like http://example.org/a/b/c/d/e/f/g.txt */ QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt")) << (ResultNode("[example.org]b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTree() { KateFileTreeModel m(this); QFETCH(const QList, documents); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::buildTreeBatch_data() { // the easiest way to verify the equality of those two calls:) buildTree_data(); } void FileTreeModelTest::buildTreeBatch() { KateFileTreeModel m(this); QFETCH(const QList, documents); QFETCH(ResultNode, nodes); QList list; for (DummyDocument *doc : documents) { list << doc; } m.documentsOpened(list); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::buildTreeBatchPrefill_data() { QTest::addColumn>("prefill"); QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("split") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << new DummyDocument("file:///b/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTreeBatchPrefill() { KateFileTreeModel m(this); QFETCH(const QList, prefill); QFETCH(const QList, documents); QFETCH(ResultNode, nodes); for (DummyDocument *doc : prefill) { m.documentOpened(doc); } QList list; for (DummyDocument *doc : documents) { list << doc; } m.documentsOpened(list); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(prefill); qDeleteAll(documents); } void FileTreeModelTest::walkTree(KateFileTreeModel &model, const QModelIndex &rootIndex, ResultNode &rootNode) { if (!model.hasChildren(rootIndex)) { return; } const int rows = model.rowCount(rootIndex); for (int i = 0; i < rows; i++) { const QModelIndex idx = model.index(i, 0, rootIndex); ResultNode node(model.data(idx).toString(), model.isDir(idx)); walkTree(model, idx, node); rootNode << node; } } void FileTreeModelTest::buildTreeFullPath_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("/a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << (ResultNode("/c/a", true) << ResultNode("foo.txt") << ResultNode("bar.txt")) << (ResultNode("/c/b", true) << ResultNode("bar.txt"))); /* This one and the case after can get a little bit tricky and * in some situation could end up in little bit confusing layout. * current root merge algorithm sees the divergent paths, so it * doesn't invoke merging. QTest::newRow("branches") << ( QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") ) << ( ResultNode() << (ResultNode("/c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) ); */ /* QTest::newRow("levels") << ( QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt") ) << ( ResultNode() << (ResultNode("/c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) << (ResultNode("/d", true) << ResultNode("foo.txt")) ); */ QTest::newRow("remote simple") << (QList() << new DummyDocument("http://example.org/foo.txt")) << (ResultNode() << (ResultNode("[example.org]", true) << ResultNode("foo.txt"))); QTest::newRow("remote nested") << (QList() << new DummyDocument("http://example.org/a/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]/a/b", true) << ResultNode("foo.txt"))); /* NOTE: see the similar testcase in buildTree */ QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/c/a/foo.txt") << new DummyDocument("http://example.org/c/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]/c/a", true) << ResultNode("foo.txt")) << (ResultNode("[example.org]/c/b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTreeFullPath() { KateFileTreeModel m(this); m.setShowFullPathOnRoots(true); QFETCH(const QList, documents); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::listMode_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (ResultNode() << ResultNode("foo.txt")); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << ResultNode("foo.txt") << ResultNode("bar.txt")); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << ResultNode("foo.txt") << ResultNode("bar.txt") << ResultNode("bar.txt")); QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (ResultNode() << ResultNode("[example.org]foo.txt") << ResultNode("[example.org]foo.txt")); } void FileTreeModelTest::listMode() { KateFileTreeModel m(this); m.setListMode(true); QFETCH(const QList, documents); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::deleteDocument_data() { QTest::addColumn>("documents"); QTest::addColumn>("remove"); QTest::addColumn("nodes"); QTest::newRow("empty") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << 0) << (ResultNode()); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (QList() << 0) << (ResultNode() << (ResultNode("a", true) << ResultNode("bar.txt"))); QTest::newRow("multiple") << (QList() << new DummyDocument("file:///a/foo0.txt") << new DummyDocument("file:///a/foo1.txt") << new DummyDocument("file:///a/foo2.txt") << new DummyDocument("file:///a/foo3.txt") << new DummyDocument("file:///a/foo4.txt") << new DummyDocument("file:///a/foo5.txt") << new DummyDocument("file:///a/foo6.txt") << new DummyDocument("file:///a/foo7.txt")) << (QList() << 1 << 2 << 4 << 6) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo0.txt") << ResultNode("foo3.txt") << ResultNode("foo5.txt") << ResultNode("foo7.txt"))); QTest::newRow("strangers") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///b/bar.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("branches") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("levels") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (QList() << 0) << (ResultNode() << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("d", true) << ResultNode("foo.txt"))); QTest::newRow("levels extra") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (QList() << 2) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::deleteDocument() { KateFileTreeModel m(this); QFETCH(const QList, documents); QFETCH(const QList, remove); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } for (const int &index : remove) { m.documentClosed(documents[index]); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::deleteDocumentBatch_data() { QTest::addColumn>("documents"); QTest::addColumn>("remove"); QTest::addColumn>("fail"); QTest::addColumn("nodes"); QTest::newRow("neo") << (QList() << new DummyDocument("file:///a/foo0.txt") << new DummyDocument("file:///a/foo1.txt") << new DummyDocument("file:///a/foo2.txt") << new DummyDocument("file:///a/foo3.txt") << new DummyDocument("file:///a/foo4.txt") << new DummyDocument("file:///a/foo5.txt") << new DummyDocument("file:///a/foo6.txt") << new DummyDocument("file:///a/foo7.txt")) << (QList() << 1 << 2 << 4 << 6) << (QList() << 2 << 4) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo0.txt") << ResultNode("foo2.txt") << ResultNode("foo3.txt") << ResultNode("foo4.txt") << ResultNode("foo5.txt") << ResultNode("foo7.txt"))); } void FileTreeModelTest::deleteDocumentBatch() { KateFileTreeModel m(this); QFETCH(const QList, documents); QFETCH(const QList, remove); QFETCH(const QList, fail); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } QList removing; for (const int &index : remove) { removing << documents[index]; } m.slotAboutToDeleteDocuments(removing); for (const int &index : remove) { if (!fail.contains(index)) { m.documentClosed(documents[index]); } } removing.clear(); for (const int &index : fail) { removing << documents[index]; } m.slotDocumentsDeleted(removing); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::rename_data() { QTest::addColumn>("documents"); QTest::addColumn("rename_idx"); QTest::addColumn("rename_url"); QTest::addColumn("nodes"); QTest::newRow("empty") << (QList() << new DummyDocument()) << 0 << QStringLiteral("file:///a/foo.txt") << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("moving") << (QList() << new DummyDocument("file:///a/foo.txt")) << 0 << QStringLiteral("file:///b/foo.txt") << (ResultNode() << (ResultNode("b", true) << ResultNode("foo.txt"))); QTest::newRow("splitting") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << 0 << QStringLiteral("file:///b/foo.txt") << (ResultNode() << (ResultNode("a", true) << ResultNode("bar.txt")) << (ResultNode("b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::rename() { KateFileTreeModel m(this); QFETCH(const QList, documents); QFETCH(int, rename_idx); QFETCH(QString, rename_url); QFETCH(ResultNode, nodes); for (DummyDocument *doc : documents) { m.documentOpened(doc); } documents[rename_idx]->setUrl(rename_url); m.documentNameChanged(documents[rename_idx]); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/filetree/katefiletreeplugin.h b/addons/filetree/katefiletreeplugin.h index 3c81d1c2f..526843e3e 100644 --- a/addons/filetree/katefiletreeplugin.h +++ b/addons/filetree/katefiletreeplugin.h @@ -1,141 +1,141 @@ /* This file is part of the KDE project Copyright (C) 2010 Thomas Fjellstrom This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KATE_FILETREE_PLUGIN_H #define KATE_FILETREE_PLUGIN_H #include #include #include #include #include #include #include #include "katefiletreepluginsettings.h" #include class KToolBar; class KateFileTree; class KateFileTreeModel; class KateFileTreeProxyModel; class KateFileTreeConfigPage; class KateFileTreePluginView; class KateFileTreePlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit KateFileTreePlugin(QObject *parent = nullptr, const QList & = QList()); ~KateFileTreePlugin() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; int configPages() const override; KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; const KateFileTreePluginSettings &settings(); void applyConfig(bool shadingEnabled, const QColor &viewShade, const QColor &editShade, bool listMode, int sortRole, bool showFulPath); public Q_SLOTS: void viewDestroyed(QObject *view); private: QList m_views; - KateFileTreeConfigPage *m_confPage; + KateFileTreeConfigPage *m_confPage = nullptr; KateFileTreePluginSettings m_settings; }; class KateFileTreePluginView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) public: /** * Constructor. */ KateFileTreePluginView(KTextEditor::MainWindow *mainWindow, KateFileTreePlugin *plug); /** * Virtual destructor. */ ~KateFileTreePluginView() override; void readSessionConfig(const KConfigGroup &config) override; void writeSessionConfig(KConfigGroup &config) override; /** * The file tree model. * @return the file tree model */ KateFileTreeModel *model(); /** * The file tree proxy model. * @return the file tree proxy model */ KateFileTreeProxyModel *proxy(); /** * The file tree. * @return the file tree */ KateFileTree *tree(); void setListMode(bool listMode); bool hasLocalPrefs(); void setHasLocalPrefs(bool); protected: void setupActions(); private: QWidget *m_toolView; KToolBar *m_toolbar; KateFileTree *m_fileTree; KateFileTreeProxyModel *m_proxyModel; KateFileTreeModel *m_documentModel; - bool m_hasLocalPrefs; + bool m_hasLocalPrefs = false; bool m_loadingDocuments; KateFileTreePlugin *m_plug; KTextEditor::MainWindow *m_mainWindow; private Q_SLOTS: void showToolView(); void hideToolView(); void showActiveDocument(); void activateDocument(KTextEditor::Document *); void viewChanged(KTextEditor::View * = nullptr); void documentOpened(KTextEditor::Document *); void documentClosed(KTextEditor::Document *); void viewModeChanged(bool); void sortRoleChanged(int); void slotAboutToCreateDocuments(); void slotDocumentsCreated(const QList &); void slotDocumentSave(); void slotDocumentSaveAs(); }; #endif // KATE_FILETREE_PLUGIN_H diff --git a/addons/gdbplugin/configview.h b/addons/gdbplugin/configview.h index 820f50547..e1e1297ef 100644 --- a/addons/gdbplugin/configview.h +++ b/addons/gdbplugin/configview.h @@ -1,125 +1,125 @@ // // configview.h // // Description: View for configuring the set of targets to be used with the debugger // // // Copyright (c) 2010 Ian Wakeling // Copyright (c) 2012 Kåre Särs // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License version 2 as published by the Free Software Foundation. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with this library; see the file COPYING.LIB. If not, write to // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301, USA. #ifndef CONFIGVIEW_H #define CONFIGVIEW_H #include "advanced_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct GDBTargetConf { QString executable; QString workDir; QString arguments; QString gdbCmd; QStringList customInit; QStringList srcPaths; }; class ConfigView : public QWidget { Q_OBJECT public: enum TargetStringOrder { NameIndex = 0, ExecIndex, WorkDirIndex, ArgsIndex, GDBIndex, CustomStartIndex }; ConfigView(QWidget *parent, KTextEditor::MainWindow *mainWin); ~ConfigView() override; public: void registerActions(KActionCollection *actionCollection); void readConfig(const KConfigGroup &config); void writeConfig(KConfigGroup &config); const GDBTargetConf currentTarget() const; bool takeFocusAlways() const; bool showIOTab() const; Q_SIGNALS: void showIO(bool show); private Q_SLOTS: void slotTargetEdited(const QString &newText); void slotTargetSelected(int index); void slotAddTarget(); void slotCopyTarget(); void slotDeleteTarget(); void slotAdvancedClicked(); void slotBrowseExec(); void slotBrowseDir(); protected: void resizeEvent(QResizeEvent *event) override; private: void saveCurrentToIndex(int index); void loadFromIndex(int index); void setAdvancedOptions(); private: KTextEditor::MainWindow *m_mainWindow; QComboBox *m_targetCombo; - int m_currentTarget; + int m_currentTarget = 0; QToolButton *m_addTarget; QToolButton *m_copyTarget; QToolButton *m_deleteTarget; QFrame *m_line; QLineEdit *m_executable; QToolButton *m_browseExe; QLineEdit *m_workingDirectory; QToolButton *m_browseDir; QLineEdit *m_arguments; QCheckBox *m_takeFocus; QCheckBox *m_redirectTerminal; QPushButton *m_advancedSettings; QBoxLayout *m_checBoxLayout; bool m_useBottomLayout; QLabel *m_execLabel; QLabel *m_workDirLabel; QLabel *m_argumentsLabel; - KSelectAction *m_targetSelectAction; + KSelectAction *m_targetSelectAction = nullptr; AdvancedGDBSettings *m_advanced; }; #endif diff --git a/addons/gdbplugin/debugview.h b/addons/gdbplugin/debugview.h index 4f2e9c290..3ad2a3574 100644 --- a/addons/gdbplugin/debugview.h +++ b/addons/gdbplugin/debugview.h @@ -1,125 +1,125 @@ // // debugview.h // // Description: Manages the interaction with GDB // // // Copyright (c) 2008-2010 Ian Wakeling // Copyright (c) 2010 Kåre Särs // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License version 2 as published by the Free Software Foundation. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with this library; see the file COPYING.LIB. If not, write to // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301, USA. #ifndef DEBUGVIEW_H #define DEBUGVIEW_H #include #include #include #include "configview.h" class DebugView : public QObject { Q_OBJECT public: DebugView(QObject *parent); ~DebugView() override; void runDebugger(const GDBTargetConf &conf, const QStringList &ioFifos); bool debuggerRunning() const; bool debuggerBusy() const; bool hasBreakpoint(QUrl const &url, int line); void toggleBreakpoint(QUrl const &url, int line); void movePC(QUrl const &url, int line); void runToCursor(QUrl const &url, int line); void issueCommand(QString const &cmd); public Q_SLOTS: void slotInterrupt(); void slotStepInto(); void slotStepOver(); void slotStepOut(); void slotContinue(); void slotKill(); void slotReRun(); void slotQueryLocals(bool display); private Q_SLOTS: void slotError(); void slotReadDebugStdOut(); void slotReadDebugStdErr(); void slotDebugFinished(int exitCode, QProcess::ExitStatus status); void issueNextCommand(); Q_SIGNALS: void debugLocationChanged(const QUrl &file, int lineNum); void breakPointSet(const QUrl &file, int lineNum); void breakPointCleared(const QUrl &file, int lineNum); void clearBreakpointMarks(); void stackFrameInfo(QString const &level, QString const &info); void stackFrameChanged(int level); void threadInfo(int number, bool active); void infoLocal(QString const &line); void outputText(const QString &text); void outputError(const QString &text); void readyForInput(bool ready); void programEnded(); void gdbEnded(); private: enum State { none, ready, executingCmd, listingBreakpoints, infoStack, infoArgs, printThis, infoLocals, infoThreads }; enum SubState { normal, stackFrameSeen, stackTraceSeen }; struct BreakPoint { int number; QUrl file; int line; }; private: void processLine(QString output); void processErrors(); void outputTextMaybe(const QString &text); QUrl resolveFileName(const QString &fileName); private: QProcess m_debugProcess; GDBTargetConf m_targetConf; QString m_ioPipeString; State m_state; SubState m_subState; QString m_currentFile; QString m_newFrameFile; - int m_newFrameLevel; + int m_newFrameLevel = 0; QStringList m_nextCommands; QString m_lastCommand; bool m_debugLocationChanged; QList m_breakPointList; QString m_outBuffer; QString m_errBuffer; QStringList m_errorList; bool m_queryLocals; }; #endif diff --git a/addons/gdbplugin/ioview.h b/addons/gdbplugin/ioview.h index e901f0072..8e8a11520 100644 --- a/addons/gdbplugin/ioview.h +++ b/addons/gdbplugin/ioview.h @@ -1,86 +1,86 @@ // // ioview.h // // Description: Widget that interacts with the debugged application // // // Copyright (c) 2010 Kåre Särs // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License version 2 as published by the Free Software Foundation. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with this library; see the file COPYING.LIB. If not, write to // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301, USA. #ifndef IOVIEW_H #define IOVIEW_H #include #include class QTextEdit; class QLineEdit; class QSocketNotifier; class IOView : public QWidget { Q_OBJECT public: IOView(QWidget *parent = nullptr); ~IOView() override; const QString stdinFifo(); const QString stdoutFifo(); const QString stderrFifo(); void enableInput(bool enable); void clearOutput(); public Q_SLOTS: void addStdOutText(const QString &text); void addStdErrText(const QString &text); private Q_SLOTS: void returnPressed(); void readOutput(); void readErrors(); Q_SIGNALS: void stdOutText(const QString &text); void stdErrText(const QString &text); private: void createFifos(); QString createFifo(const QString &prefix); QTextEdit *m_output; QLineEdit *m_input; QString m_stdinFifo; QString m_stdoutFifo; QString m_stderrFifo; QFile m_stdin; QFile m_stdout; QFile m_stderr; QFile m_stdoutD; QFile m_stderrD; - int m_stdoutFD; - int m_stderrFD; + int m_stdoutFD = 0; + int m_stderrFD = 0; - QSocketNotifier *m_stdoutNotifier; - QSocketNotifier *m_stderrNotifier; + QSocketNotifier *m_stdoutNotifier = nullptr; + QSocketNotifier *m_stderrNotifier = nullptr; }; #endif diff --git a/addons/kate-ctags/kate_ctags_plugin.h b/addons/kate-ctags/kate_ctags_plugin.h index 828bb5e45..3aebd051e 100644 --- a/addons/kate-ctags/kate_ctags_plugin.h +++ b/addons/kate-ctags/kate_ctags_plugin.h @@ -1,92 +1,92 @@ #ifndef KATE_CTAGS_PLUGIN_H #define KATE_CTAGS_PLUGIN_H /* Description : Kate CTags plugin * * Copyright (C) 2008-2011 by Kare Sars * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see . */ #include #include #include #include #include #include #include "kate_ctags_view.h" #include "ui_CTagsGlobalConfig.h" //******************************************************************/ class KateCTagsPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit KateCTagsPlugin(QObject *parent = nullptr, const QList & = QList()); ~KateCTagsPlugin() override { } QObject *createView(KTextEditor::MainWindow *mainWindow) override; int configPages() const override { return 1; } KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; void readConfig(); KateCTagsView *m_view = nullptr; }; //******************************************************************/ class KateCTagsConfigPage : public KTextEditor::ConfigPage { Q_OBJECT public: explicit KateCTagsConfigPage(QWidget *parent = nullptr, KateCTagsPlugin *plugin = nullptr); ~KateCTagsConfigPage() override { } QString name() const override; QString fullName() const override; QIcon icon() const override; void apply() override; void reset() override; void defaults() override { } private Q_SLOTS: void addGlobalTagTarget(); void delGlobalTagTarget(); void updateGlobalDB(); void updateDone(int exitCode, QProcess::ExitStatus status); private: bool listContains(const QString &target); QProcess m_proc; KateCTagsPlugin *m_plugin; - Ui_CTagsGlobalConfig m_confUi; + Ui_CTagsGlobalConfig m_confUi{}; }; #endif diff --git a/addons/kate-ctags/kate_ctags_view.h b/addons/kate-ctags/kate_ctags_view.h index 585824ca5..e3375040a 100644 --- a/addons/kate-ctags/kate_ctags_view.h +++ b/addons/kate-ctags/kate_ctags_view.h @@ -1,114 +1,114 @@ #ifndef KATE_CTAGS_VIEW_H #define KATE_CTAGS_VIEW_H /* Description : Kate CTags plugin * * Copyright (C) 2008-2011 by Kare Sars * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "tags.h" #include "ui_kate_ctags.h" const static QString DEFAULT_CTAGS_CMD = QStringLiteral("ctags -R --c++-types=+px --extra=+q --excmd=pattern --exclude=Makefile --exclude=."); typedef struct { QUrl url; KTextEditor::Cursor cursor; } TagJump; /******************************************************************/ class KateCTagsView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) public: KateCTagsView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainWin); ~KateCTagsView() override; // reimplemented: read and write session config void readSessionConfig(const KConfigGroup &config) override; void writeSessionConfig(KConfigGroup &config) override; public Q_SLOTS: void gotoDefinition(); void gotoDeclaration(); void lookupTag(); void stepBack(); void editLookUp(); void aboutToShow(); void tagHitClicked(QTreeWidgetItem *); void startEditTmr(); void addTagTarget(); void delTagTarget(); void updateSessionDB(); void updateDone(int exitCode, QProcess::ExitStatus status); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private Q_SLOTS: void resetCMD(); void handleEsc(QEvent *e); private: bool listContains(const QString &target); QString currentWord(); void setNewLookupText(const QString &newText); void displayHits(const Tags::TagList &list); void gotoTagForTypes(const QString &tag, QStringList const &types); void jumpToTag(const QString &file, const QString &pattern, const QString &word); QPointer m_mWin; QPointer m_toolView; - Ui::kateCtags m_ctagsUi; + Ui::kateCtags m_ctagsUi{}; QPointer m_menu; QAction *m_gotoDef; QAction *m_gotoDec; QAction *m_lookup; QProcess m_proc; QString m_commonDB; QTimer m_editTimer; QStack m_jumpStack; }; #endif diff --git a/addons/katebuild-plugin/plugin_katebuild.h b/addons/katebuild-plugin/plugin_katebuild.h index f9fa28e42..e7cc596ed 100644 --- a/addons/katebuild-plugin/plugin_katebuild.h +++ b/addons/katebuild-plugin/plugin_katebuild.h @@ -1,180 +1,180 @@ #ifndef PLUGIN_KATEBUILD_H #define PLUGIN_KATEBUILD_H /* plugin_katebuild.h Kate Plugin ** ** Copyright (C) 2008-2015 by Kåre Särs ** ** This code is almost a total rewrite of the GPL'ed Make plugin ** by Adriaan de Groot. */ /* ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program in a file called COPYING; if not, write to ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "targets.h" #include "ui_build.h" /******************************************************************/ class KateBuildView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) Q_PROPERTY(QUrl docUrl READ docUrl) public: enum ResultDetails { FullOutput, ParsedOutput, ErrorsAndWarnings, OnlyErrors }; enum TreeWidgetRoles { ErrorRole = Qt::UserRole + 1, DataRole }; enum ErrorCategory { CategoryInfo, CategoryWarning, CategoryError }; KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mw); ~KateBuildView() override; // reimplemented: read and write session config void readSessionConfig(const KConfigGroup &config) override; void writeSessionConfig(KConfigGroup &config) override; bool buildCurrentTarget(); QUrl docUrl(); private Q_SLOTS: // Building void slotSelectTarget(); void slotBuildActiveTarget(); void slotBuildPreviousTarget(); void slotBuildDefaultTarget(); bool slotStop(); // Parse output void slotProcExited(int exitCode, QProcess::ExitStatus exitStatus); void slotReadReadyStdErr(); void slotReadReadyStdOut(); // Selecting warnings/errors void slotNext(); void slotPrev(); void slotErrorSelected(QTreeWidgetItem *item); // Settings void targetSetNew(); void targetOrSetCopy(); void targetDelete(); void slotAddTargetClicked(); void slotDisplayMode(int mode); void handleEsc(QEvent *e); void slotViewChanged(); void slotDisplayOption(); void slotMarkClicked(KTextEditor::Document *doc, KTextEditor::Mark mark, bool &handled); void slotInvalidateMoving(KTextEditor::Document *doc); /** * keep track if the project plugin is alive and if the project map did change */ void slotPluginViewCreated(const QString &name, QObject *pluginView); void slotPluginViewDeleted(const QString &name, QObject *pluginView); void slotProjectMapChanged(); void slotAddProjectTarget(); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private: void processLine(const QString &); void addError(const QString &filename, const QString &line, const QString &column, const QString &message); bool startProcess(const QString &dir, const QString &command); bool checkLocal(const QUrl &dir); void clearBuildResults(); void displayBuildResult(const QString &message, KTextEditor::Message::MessageType level); void clearMarks(); void addMarks(KTextEditor::Document *doc, bool mark); KTextEditor::MainWindow *m_win; QWidget *m_toolView; - Ui::build m_buildUi; + Ui::build m_buildUi{}; QWidget *m_buildWidget; int m_outputWidgetWidth; TargetsUi *m_targetsUi; KProcess m_proc; QString m_stdOut; QString m_stdErr; QString m_currentlyBuildingTarget; bool m_buildCancelled; int m_displayModeBeforeBuild; QString m_make_dir; QStack m_make_dir_stack; QRegularExpression m_filenameDetector; QRegularExpression m_filenameDetectorIcpc; bool m_filenameDetectorGccWorked; - bool m_ninjaBuildDetected; + bool m_ninjaBuildDetected = false; QRegularExpression m_newDirDetector; - unsigned int m_numErrors; - unsigned int m_numWarnings; + unsigned int m_numErrors = 0; + unsigned int m_numWarnings = 0; QString m_prevItemContent; QModelIndex m_previousIndex; QPointer m_infoMessage; QPointer m_showMarks; QHash> m_markedDocs; /** * current project plugin view, if any */ QObject *m_projectPluginView = nullptr; }; typedef QList VariantList; /******************************************************************/ class KateBuildPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit KateBuildPlugin(QObject *parent = nullptr, const VariantList & = VariantList()); ~KateBuildPlugin() override { } QObject *createView(KTextEditor::MainWindow *mainWindow) override; }; #endif diff --git a/addons/katesql/katesqlconfigpage.h b/addons/katesql/katesqlconfigpage.h index e1c8e62ef..92e52d90c 100644 --- a/addons/katesql/katesqlconfigpage.h +++ b/addons/katesql/katesqlconfigpage.h @@ -1,57 +1,57 @@ /* Copyright (C) 2010 Marco Mentasti This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KATESQLCONFIGPAGE_H #define KATESQLCONFIGPAGE_H class OutputStyleWidget; class QCheckBox; #include "katesqlplugin.h" #include /// TODO: add options to change datetime and numbers format class KateSQLConfigPage : public KTextEditor::ConfigPage { Q_OBJECT public: explicit KateSQLConfigPage(QWidget *parent = nullptr); ~KateSQLConfigPage() override; QString name() const override; QString fullName() const override; QIcon icon() const override; public Q_SLOTS: void apply() override; void reset() override; void defaults() override; private: - KateSQLPlugin *m_plugin; + KateSQLPlugin *m_plugin = nullptr; QCheckBox *m_box; OutputStyleWidget *m_outputStyleWidget; Q_SIGNALS: void settingsChanged(); }; #endif // KATESQLCONFIGPAGE_H diff --git a/addons/katesql/katesqlview.cpp b/addons/katesql/katesqlview.cpp index f36e5e340..7efe96193 100644 --- a/addons/katesql/katesqlview.cpp +++ b/addons/katesql/katesqlview.cpp @@ -1,359 +1,358 @@ /* Copyright (C) 2010 Marco Mentasti This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katesqlview.h" #include "connectionmodel.h" #include "connectionwizard.h" #include "dataoutputmodel.h" #include "dataoutputview.h" #include "dataoutputwidget.h" #include "katesqlplugin.h" #include "outputwidget.h" #include "schemabrowserwidget.h" #include "schemawidget.h" #include "sqlmanager.h" #include "textoutputwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KateSQLView::KateSQLView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mw) : QObject(mw) - , KXMLGUIClient() , m_manager(new SQLManager(this)) , m_mainWindow(mw) { KXMLGUIClient::setComponentName(QStringLiteral("katesql"), i18n("Kate SQL Plugin")); setXMLFile(QStringLiteral("ui.rc")); m_outputToolView = mw->createToolView(plugin, QStringLiteral("kate_private_plugin_katesql_output"), KTextEditor::MainWindow::Bottom, QIcon::fromTheme(QStringLiteral("view-form-table")), i18nc("@title:window", "SQL Results")); m_schemaBrowserToolView = mw->createToolView(plugin, QStringLiteral("kate_private_plugin_katesql_schemabrowser"), KTextEditor::MainWindow::Left, QIcon::fromTheme(QStringLiteral("view-list-tree")), i18nc("@title:window", "SQL Schema Browser")); m_outputWidget = new KateSQLOutputWidget(m_outputToolView); m_schemaBrowserWidget = new SchemaBrowserWidget(m_schemaBrowserToolView, m_manager); m_connectionsComboBox = new KComboBox(false); m_connectionsComboBox->setEditable(false); m_connectionsComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); m_connectionsComboBox->setModel(m_manager->connectionModel()); setupActions(); m_mainWindow->guiFactory()->addClient(this); QMenu *sqlMenu = static_cast(factory()->container(QStringLiteral("SQL"), this)); m_connectionsGroup = new QActionGroup(sqlMenu); m_connectionsGroup->setExclusive(true); connect(sqlMenu, &QMenu::aboutToShow, this, &KateSQLView::slotSQLMenuAboutToShow); connect(m_connectionsGroup, &QActionGroup::triggered, this, &KateSQLView::slotConnectionSelectedFromMenu); connect(m_manager, &SQLManager::error, this, &KateSQLView::slotError); connect(m_manager, &SQLManager::success, this, &KateSQLView::slotSuccess); connect(m_manager, &SQLManager::queryActivated, this, &KateSQLView::slotQueryActivated); connect(m_manager, &SQLManager::connectionCreated, this, &KateSQLView::slotConnectionCreated); connect(m_manager, &SQLManager::connectionAboutToBeClosed, this, &KateSQLView::slotConnectionAboutToBeClosed); connect(m_connectionsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KateSQLView::slotConnectionChanged); stateChanged(QStringLiteral("has_connection_selected"), KXMLGUIClient::StateReverse); } KateSQLView::~KateSQLView() { m_mainWindow->guiFactory()->removeClient(this); delete m_outputToolView; delete m_schemaBrowserToolView; delete m_manager; } void KateSQLView::setupActions() { QAction *action; KActionCollection *collection = actionCollection(); action = collection->addAction(QStringLiteral("connection_create")); action->setText(i18nc("@action:inmenu", "Add connection...")); action->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect(action, &QAction::triggered, this, &KateSQLView::slotConnectionCreate); action = collection->addAction(QStringLiteral("connection_remove")); action->setText(i18nc("@action:inmenu", "Remove connection")); action->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); connect(action, &QAction::triggered, this, &KateSQLView::slotConnectionRemove); action = collection->addAction(QStringLiteral("connection_edit")); action->setText(i18nc("@action:inmenu", "Edit connection...")); action->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); connect(action, &QAction::triggered, this, &KateSQLView::slotConnectionEdit); action = collection->addAction(QStringLiteral("connection_reconnect")); action->setText(i18nc("@action:inmenu", "Reconnect")); action->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); connect(action, &QAction::triggered, this, &KateSQLView::slotConnectionReconnect); QWidgetAction *wa = new QWidgetAction(this); collection->addAction(QStringLiteral("connection_chooser"), wa); wa->setText(i18nc("@action:intoolbar", "Connection")); wa->setDefaultWidget(m_connectionsComboBox); action = collection->addAction(QStringLiteral("query_run")); action->setText(i18nc("@action:inmenu", "Run query")); action->setIcon(QIcon::fromTheme(QStringLiteral("quickopen"))); collection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_E)); connect(action, &QAction::triggered, this, &KateSQLView::slotRunQuery); /// TODO: stop sql query // action = collection->addAction("sql_stop"); // action->setText( i18n("Stop query") ); // action->setIcon( KIcon("process-stop") ); // action->setShortcut( QKeySequence(Qt::ALT + Qt::Key_F5) ); // connect( action , SIGNAL(triggered()) , this , SLOT(stopQuery())); } void KateSQLView::slotSQLMenuAboutToShow() { qDeleteAll(m_connectionsGroup->actions()); QMenu *sqlMenu = static_cast(factory()->container(QStringLiteral("SQL"), this)); QAction *before = action("query_run"); QAbstractItemModel *model = m_manager->connectionModel(); int rows = model->rowCount(QModelIndex()); for (int row = 0; row < rows; row++) { QModelIndex index = model->index(row, 0, QModelIndex()); Q_ASSERT(index.isValid()); QString connectionName = index.data(Qt::DisplayRole).toString(); QAction *act = new QAction(connectionName, m_connectionsGroup); act->setCheckable(true); if (m_connectionsComboBox->currentText() == connectionName) act->setChecked(true); sqlMenu->insertAction(before, act); } sqlMenu->insertSeparator(before); } void KateSQLView::slotConnectionSelectedFromMenu(QAction *action) { m_connectionsComboBox->setCurrentItem(action->text()); } void KateSQLView::slotConnectionChanged(const QString &connection) { stateChanged(QStringLiteral("has_connection_selected"), (connection.isEmpty()) ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse); m_schemaBrowserWidget->schemaWidget()->buildTree(connection); } void KateSQLView::slotGlobalSettingsChanged() { m_outputWidget->dataOutputWidget()->model()->readConfig(); } void KateSQLView::readSessionConfig(KConfigBase *config, const QString &groupPrefix) { KConfigGroup globalConfig(KSharedConfig::openConfig(), "KateSQLPlugin"); bool saveConnections = globalConfig.readEntry("SaveConnections", true); if (!saveConnections) return; KConfigGroup group(config, groupPrefix + QLatin1String(":connections")); m_manager->loadConnections(&group); QString lastConnection = group.readEntry("LastUsed"); if (m_connectionsComboBox->contains(lastConnection)) m_connectionsComboBox->setCurrentItem(lastConnection); } void KateSQLView::writeSessionConfig(KConfigBase *config, const QString &groupPrefix) { KConfigGroup group(config, groupPrefix + QLatin1String(":connections")); group.deleteGroup(); KConfigGroup globalConfig(KSharedConfig::openConfig(), "KateSQLPlugin"); bool saveConnections = globalConfig.readEntry("SaveConnections", true); if (saveConnections) { m_manager->saveConnections(&group); group.writeEntry("LastUsed", m_connectionsComboBox->currentText()); } config->sync(); } void KateSQLView::slotConnectionCreate() { Connection c; ConnectionWizard wizard(m_manager, &c); if (wizard.exec() != QDialog::Accepted) return; for (int i = 1; QSqlDatabase::contains(c.name); i++) c.name = QStringLiteral("%1 (%2)").arg(c.name).arg(i); m_manager->createConnection(c); if (m_manager->storeCredentials(c) != 0) qDebug() << "Connection credentials not saved"; } void KateSQLView::slotConnectionEdit() { int i = m_connectionsComboBox->currentIndex(); if (i == -1) return; ConnectionModel *model = m_manager->connectionModel(); Connection c = model->data(model->index(i), Qt::UserRole).value(); QString previousName = c.name; ConnectionWizard wizard(m_manager, &c); if (wizard.exec() != QDialog::Accepted) return; m_manager->removeConnection(previousName); m_manager->createConnection(c); if (m_manager->storeCredentials(c) != 0) qDebug() << "Connection credentials not saved"; } void KateSQLView::slotConnectionRemove() { QString connection = m_connectionsComboBox->currentText(); if (!connection.isEmpty()) m_manager->removeConnection(connection); } void KateSQLView::slotConnectionReconnect() { QString connection = m_connectionsComboBox->currentText(); if (!connection.isEmpty()) m_manager->reopenConnection(connection); } void KateSQLView::slotConnectionAboutToBeClosed(const QString &name) { /// must delete the QSqlQuery object inside the model before closing connection if (name == m_currentResultsetConnection) m_outputWidget->dataOutputWidget()->clearResults(); } void KateSQLView::slotRunQuery() { /// TODO: /// bind parameters dialog? QString connection = m_connectionsComboBox->currentText(); if (connection.isEmpty()) { slotConnectionCreate(); return; } KTextEditor::View *view = m_mainWindow->activeView(); if (!view) return; QString text = (view->selection()) ? view->selectionText() : view->document()->text(); text = text.trimmed(); if (text.isEmpty()) return; m_manager->runQuery(text, connection); } void KateSQLView::slotError(const QString &message) { m_outputWidget->textOutputWidget()->showErrorMessage(message); m_outputWidget->setCurrentWidget(m_outputWidget->textOutputWidget()); m_mainWindow->showToolView(m_outputToolView); } void KateSQLView::slotSuccess(const QString &message) { m_outputWidget->textOutputWidget()->showSuccessMessage(message); m_outputWidget->setCurrentWidget(m_outputWidget->textOutputWidget()); m_mainWindow->showToolView(m_outputToolView); } void KateSQLView::slotQueryActivated(QSqlQuery &query, const QString &connection) { if (query.isSelect()) { m_currentResultsetConnection = connection; m_outputWidget->dataOutputWidget()->showQueryResultSets(query); m_outputWidget->setCurrentWidget(m_outputWidget->dataOutputWidget()); m_mainWindow->showToolView(m_outputToolView); } } void KateSQLView::slotConnectionCreated(const QString &name) { m_connectionsComboBox->setCurrentItem(name); m_schemaBrowserWidget->schemaWidget()->buildTree(name); } // END KateSQLView diff --git a/addons/lspclient/lspclientconfigpage.cpp b/addons/lspclient/lspclientconfigpage.cpp index 2b23814df..c15b72bbf 100644 --- a/addons/lspclient/lspclientconfigpage.cpp +++ b/addons/lspclient/lspclientconfigpage.cpp @@ -1,246 +1,246 @@ /* SPDX-License-Identifier: MIT Copyright (C) 2019 Mark Nauwelaerts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "lspclientconfigpage.h" #include "lspclientplugin.h" #include "ui_lspconfigwidget.h" #include #include #include #include #include #include #include LSPClientConfigPage::LSPClientConfigPage(QWidget *parent, LSPClientPlugin *plugin) : KTextEditor::ConfigPage(parent) , m_plugin(plugin) { ui = new Ui::LspConfigWidget(); ui->setupUi(this); // fix-up our two text edits to be proper JSON file editors for (auto textEdit : {ui->userConfig, static_cast(ui->defaultConfig)}) { // setup JSON highlighter for the default json stuff auto highlighter = new KSyntaxHighlighting::SyntaxHighlighter(textEdit->document()); highlighter->setDefinition(m_repository.definitionForFileName(QStringLiteral("settings.json"))); // we want mono-spaced font textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); // we want to have the proper theme for the current palette const auto theme = (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme); auto pal = qApp->palette(); if (theme.isValid()) { pal.setColor(QPalette::Base, theme.editorColor(KSyntaxHighlighting::Theme::BackgroundColor)); pal.setColor(QPalette::Highlight, theme.editorColor(KSyntaxHighlighting::Theme::TextSelection)); } textEdit->setPalette(pal); highlighter->setTheme(theme); } // setup default json settings QFile defaultConfigFile(QStringLiteral(":/lspclient/settings.json")); defaultConfigFile.open(QIODevice::ReadOnly); Q_ASSERT(defaultConfigFile.isOpen()); ui->defaultConfig->setPlainText(QString::fromUtf8(defaultConfigFile.readAll())); // setup default config path as placeholder to show user where it is ui->edtConfigPath->setPlaceholderText(m_plugin->m_defaultConfigPath.toLocalFile()); reset(); for (const auto &cb : {ui->chkSymbolDetails, ui->chkSymbolExpand, ui->chkSymbolSort, ui->chkSymbolTree, ui->chkComplDoc, ui->chkRefDeclaration, ui->chkDiagnostics, ui->chkDiagnosticsMark, ui->chkMessages, ui->chkOnTypeFormatting, ui->chkIncrementalSync, ui->chkSemanticHighlighting, ui->chkAutoHover}) connect(cb, &QCheckBox::toggled, this, &LSPClientConfigPage::changed); connect(ui->comboMessagesSwitch, static_cast(&QComboBox::currentIndexChanged), this, [this](int) { changed(); }); connect(ui->edtConfigPath, &KUrlRequester::textChanged, this, &LSPClientConfigPage::configUrlChanged); connect(ui->edtConfigPath, &KUrlRequester::urlSelected, this, &LSPClientConfigPage::configUrlChanged); connect(ui->userConfig, &QTextEdit::textChanged, this, &LSPClientConfigPage::configTextChanged); // custom control logic auto h = [this]() { bool enabled = ui->chkDiagnostics->isChecked(); ui->chkDiagnosticsHighlight->setEnabled(enabled); ui->chkDiagnosticsMark->setEnabled(enabled); enabled = ui->chkMessages->isChecked(); ui->comboMessagesSwitch->setEnabled(enabled); }; connect(this, &LSPClientConfigPage::changed, this, h); } LSPClientConfigPage::~LSPClientConfigPage() { delete ui; } QString LSPClientConfigPage::name() const { return QString(i18n("LSP Client")); } QString LSPClientConfigPage::fullName() const { return QString(i18n("LSP Client")); } QIcon LSPClientConfigPage::icon() const { return QIcon::fromTheme(QLatin1String("code-context")); } void LSPClientConfigPage::apply() { m_plugin->m_symbolDetails = ui->chkSymbolDetails->isChecked(); m_plugin->m_symbolTree = ui->chkSymbolTree->isChecked(); m_plugin->m_symbolExpand = ui->chkSymbolExpand->isChecked(); m_plugin->m_symbolSort = ui->chkSymbolSort->isChecked(); m_plugin->m_complDoc = ui->chkComplDoc->isChecked(); m_plugin->m_refDeclaration = ui->chkRefDeclaration->isChecked(); m_plugin->m_diagnostics = ui->chkDiagnostics->isChecked(); m_plugin->m_diagnosticsHighlight = ui->chkDiagnosticsHighlight->isChecked(); m_plugin->m_diagnosticsMark = ui->chkDiagnosticsMark->isChecked(); m_plugin->m_autoHover = ui->chkAutoHover->isChecked(); m_plugin->m_onTypeFormatting = ui->chkOnTypeFormatting->isChecked(); m_plugin->m_incrementalSync = ui->chkIncrementalSync->isChecked(); m_plugin->m_semanticHighlighting = ui->chkSemanticHighlighting->isChecked(); m_plugin->m_messages = ui->chkMessages->isChecked(); m_plugin->m_messagesAutoSwitch = ui->comboMessagesSwitch->currentIndex(); m_plugin->m_configPath = ui->edtConfigPath->url(); // own scope to ensure file is flushed before we signal below in writeConfig! { QFile configFile(m_plugin->configPath().toLocalFile()); configFile.open(QIODevice::WriteOnly); if (configFile.isOpen()) { configFile.write(ui->userConfig->toPlainText().toUtf8()); } } m_plugin->writeConfig(); } void LSPClientConfigPage::reset() { ui->chkSymbolDetails->setChecked(m_plugin->m_symbolDetails); ui->chkSymbolTree->setChecked(m_plugin->m_symbolTree); ui->chkSymbolExpand->setChecked(m_plugin->m_symbolExpand); ui->chkSymbolSort->setChecked(m_plugin->m_symbolSort); ui->chkComplDoc->setChecked(m_plugin->m_complDoc); ui->chkRefDeclaration->setChecked(m_plugin->m_refDeclaration); ui->chkDiagnostics->setChecked(m_plugin->m_diagnostics); ui->chkDiagnosticsHighlight->setChecked(m_plugin->m_diagnosticsHighlight); ui->chkDiagnosticsMark->setChecked(m_plugin->m_diagnosticsMark); ui->chkAutoHover->setChecked(m_plugin->m_autoHover); ui->chkOnTypeFormatting->setChecked(m_plugin->m_onTypeFormatting); ui->chkIncrementalSync->setChecked(m_plugin->m_incrementalSync); ui->chkSemanticHighlighting->setChecked(m_plugin->m_semanticHighlighting); ui->chkMessages->setChecked(m_plugin->m_messages); ui->comboMessagesSwitch->setCurrentIndex(m_plugin->m_messagesAutoSwitch); ui->edtConfigPath->setUrl(m_plugin->m_configPath); readUserConfig(m_plugin->configPath().toLocalFile()); } void LSPClientConfigPage::defaults() { reset(); } void LSPClientConfigPage::readUserConfig(const QString &fileName) { QFile configFile(fileName); configFile.open(QIODevice::ReadOnly); if (configFile.isOpen()) { ui->userConfig->setPlainText(QString::fromUtf8(configFile.readAll())); } else { ui->userConfig->clear(); } updateConfigTextErrorState(); } void LSPClientConfigPage::updateConfigTextErrorState() { const auto data = ui->userConfig->toPlainText().toUtf8(); if (data.isEmpty()) { ui->userConfigError->setText(i18n("No JSON data to validate.")); return; } // check json validity - QJsonParseError error; + QJsonParseError error{}; auto json = QJsonDocument::fromJson(data, &error); if (error.error == QJsonParseError::NoError) { if (json.isObject()) { ui->userConfigError->setText(i18n("JSON data is valid.")); } else { ui->userConfigError->setText(i18n("JSON data is invalid: no JSON object")); } } else { ui->userConfigError->setText(i18n("JSON data is invalid: %1", error.errorString())); } } void LSPClientConfigPage::configTextChanged() { // check for errors updateConfigTextErrorState(); // remember changed changed(); } void LSPClientConfigPage::configUrlChanged() { // re-read config readUserConfig(ui->edtConfigPath->url().isEmpty() ? m_plugin->m_defaultConfigPath.toLocalFile() : ui->edtConfigPath->url().toLocalFile()); // remember changed changed(); } diff --git a/addons/lspclient/lspclienthover.cpp b/addons/lspclient/lspclienthover.cpp index cbbf581dc..212a36388 100644 --- a/addons/lspclient/lspclienthover.cpp +++ b/addons/lspclient/lspclienthover.cpp @@ -1,117 +1,116 @@ /* SPDX-License-Identifier: MIT Copyright (C) 2019 Mark Nauwelaerts Copyright (C) 2019 Christoph Cullmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "lspclienthover.h" #include "lspclientplugin.h" #include "lspclient_debug.h" #include #include #include #include #include class LSPClientHoverImpl : public LSPClientHover { Q_OBJECT typedef LSPClientHoverImpl self_type; QSharedPointer m_manager; QSharedPointer m_server; LSPClientServer::RequestHandle m_handle; public: LSPClientHoverImpl(QSharedPointer manager) - : LSPClientHover() - , m_manager(std::move(manager)) + : m_manager(std::move(manager)) , m_server(nullptr) { } void setServer(QSharedPointer server) override { m_server = server; } /** * This function is called whenever the users hovers over text such * that the text hint delay passes. Then, textHint() is called * for each registered TextHintProvider. * * Return the text hint (possibly Qt richtext) for @p view at @p position. * * If you do not have any contents to show, just return an empty QString(). * * \param view the view that requests the text hint * \param position text cursor under the mouse position * \return text tool tip to be displayed, may be Qt richtext */ QString textHint(KTextEditor::View *view, const KTextEditor::Cursor &position) override { // hack: delayed handling of tooltip on our own, the API is too dumb for a-sync feedback ;=) if (m_server) { QPointer v(view); auto h = [this, v, position](const LSPHover &info) { if (!v || info.contents.isEmpty()) { return; } // combine contents elements to one string QString finalTooltip; for (auto &element : info.contents) { if (!finalTooltip.isEmpty()) { finalTooltip.append(QLatin1Char('\n')); } finalTooltip.append(element.value); } // we need to cut this a bit if too long until we have // something more sophisticated than a tool tip for it if (finalTooltip.size() > 512) { finalTooltip.resize(512); finalTooltip.append(QStringLiteral("...")); } // show tool tip: think about a better way for "large" stuff QToolTip::showText(v->mapToGlobal(v->cursorToCoordinate(position)), finalTooltip); }; m_handle.cancel() = m_server->documentHover(view->document()->url(), position, this, h); } return QString(); } }; LSPClientHover *LSPClientHover::new_(QSharedPointer manager) { return new LSPClientHoverImpl(std::move(manager)); } #include "lspclienthover.moc" diff --git a/addons/lspclient/lspclientplugin.h b/addons/lspclient/lspclientplugin.h index 5dfc36e22..d91649e6d 100644 --- a/addons/lspclient/lspclientplugin.h +++ b/addons/lspclient/lspclientplugin.h @@ -1,89 +1,89 @@ /* SPDX-License-Identifier: MIT Copyright (C) 2019 Mark Nauwelaerts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LSPCLIENTPLUGIN_H #define LSPCLIENTPLUGIN_H #include #include #include #include class LSPClientPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit LSPClientPlugin(QObject *parent = nullptr, const QList & = QList()); ~LSPClientPlugin() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; int configPages() const override; KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; void readConfig(); void writeConfig() const; // path for local setting files, auto-created on load const QString m_settingsPath; // default config path const QUrl m_defaultConfigPath; // settings - bool m_symbolDetails; - bool m_symbolExpand; - bool m_symbolTree; - bool m_symbolSort; - bool m_complDoc; - bool m_refDeclaration; - bool m_diagnostics; - bool m_diagnosticsHighlight; - bool m_diagnosticsMark; - bool m_messages; - int m_messagesAutoSwitch; - bool m_autoHover; - bool m_onTypeFormatting; - bool m_incrementalSync; + bool m_symbolDetails = false; + bool m_symbolExpand = false; + bool m_symbolTree = false; + bool m_symbolSort = false; + bool m_complDoc = false; + bool m_refDeclaration = false; + bool m_diagnostics = false; + bool m_diagnosticsHighlight = false; + bool m_diagnosticsMark = false; + bool m_messages = false; + int m_messagesAutoSwitch = 0; + bool m_autoHover = false; + bool m_onTypeFormatting = false; + bool m_incrementalSync = false; QUrl m_configPath; - bool m_semanticHighlighting; + bool m_semanticHighlighting = false; // debug mode? bool m_debugMode = false; // get current config path QUrl configPath() const { return m_configPath.isEmpty() ? m_defaultConfigPath : m_configPath; } private: Q_SIGNALS: // signal settings update void update() const; }; #endif diff --git a/addons/lspclient/lspclientservermanager.cpp b/addons/lspclient/lspclientservermanager.cpp index 623fb90a7..1699aed27 100644 --- a/addons/lspclient/lspclientservermanager.cpp +++ b/addons/lspclient/lspclientservermanager.cpp @@ -1,819 +1,819 @@ /* SPDX-License-Identifier: MIT Copyright (C) 2019 Mark Nauwelaerts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* see plugins.docbook lspclient-configuration * for client configuration documentation */ #include "lspclientservermanager.h" #include "lspclient_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // helper to find a proper root dir for the given document & file name that indicate the root dir static QString rootForDocumentAndRootIndicationFileName(KTextEditor::Document *document, const QString &rootIndicationFileName) { // search only feasible if document is local file if (!document->url().isLocalFile()) { return QString(); } // search root upwards QDir dir(QFileInfo(document->url().toLocalFile()).absolutePath()); QSet seenDirectories; while (!seenDirectories.contains(dir.absolutePath())) { // update guard seenDirectories.insert(dir.absolutePath()); // the file that indicates the root dir is there => all fine if (dir.exists(rootIndicationFileName)) { return dir.absolutePath(); } // else: cd up, if possible or abort if (!dir.cdUp()) { break; } } // no root found, bad luck return QString(); } #include // local helper; // recursively merge top json top onto bottom json static QJsonObject merge(const QJsonObject &bottom, const QJsonObject &top) { QJsonObject result; for (auto item = top.begin(); item != top.end(); item++) { const auto &key = item.key(); if (item.value().isObject()) { result.insert(key, merge(bottom.value(key).toObject(), item.value().toObject())); } else { result.insert(key, item.value()); } } // parts only in bottom for (auto item = bottom.begin(); item != bottom.end(); item++) { if (!result.contains(item.key())) { result.insert(item.key(), item.value()); } } return result; } // helper guard to handle revision (un)lock struct RevisionGuard { QPointer m_doc; KTextEditor::MovingInterface *m_movingInterface = nullptr; qint64 m_revision = -1; RevisionGuard(KTextEditor::Document *doc = nullptr) : m_doc(doc) , m_movingInterface(qobject_cast(doc)) { Q_ASSERT(m_movingInterface); m_revision = m_movingInterface->revision(); m_movingInterface->lockRevision(m_revision); } // really only need/allow this one (out of 5) RevisionGuard(RevisionGuard &&other) : RevisionGuard(nullptr) { std::swap(m_doc, other.m_doc); std::swap(m_movingInterface, other.m_movingInterface); std::swap(m_revision, other.m_revision); } void release() { m_movingInterface = nullptr; m_revision = -1; } ~RevisionGuard() { // NOTE: hopefully the revision is still valid at this time if (m_doc && m_movingInterface && m_revision >= 0) { m_movingInterface->unlockRevision(m_revision); } } }; class LSPClientRevisionSnapshotImpl : public LSPClientRevisionSnapshot { Q_OBJECT typedef LSPClientRevisionSnapshotImpl self_type; // std::map has more relaxed constraints on value_type std::map m_guards; Q_SLOT void clearRevisions(KTextEditor::Document *doc) { for (auto &item : m_guards) { if (item.second.m_doc == doc) { item.second.release(); } } } public: void add(KTextEditor::Document *doc) { Q_ASSERT(doc); // make sure revision is cleared when needed and no longer used (to unlock or otherwise) // see e.g. implementation in katetexthistory.cpp and assert's in place there auto conn = connect(doc, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document *)), this, SLOT(clearRevisions(KTextEditor::Document *))); Q_ASSERT(conn); conn = connect(doc, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document *)), this, SLOT(clearRevisions(KTextEditor::Document *))); Q_ASSERT(conn); m_guards.emplace(doc->url(), doc); } void find(const QUrl &url, KTextEditor::MovingInterface *&miface, qint64 &revision) const override { auto it = m_guards.find(url); if (it != m_guards.end()) { miface = it->second.m_movingInterface; revision = it->second.m_revision; } else { miface = nullptr; revision = -1; } } }; // helper class to sync document changes to LSP server class LSPClientServerManagerImpl : public LSPClientServerManager { Q_OBJECT typedef LSPClientServerManagerImpl self_type; struct ServerInfo { QSharedPointer server; // config specified server url QString url; QTime started; int failcount = 0; // pending settings to be submitted QJsonValue settings; }; struct DocumentInfo { QSharedPointer server; KTextEditor::MovingInterface *movingInterface; QUrl url; qint64 version; bool open : 1; bool modified : 1; // used for incremental update (if non-empty) QList changes; }; LSPClientPlugin *m_plugin; KTextEditor::MainWindow *m_mainWindow; // merged default and user config QJsonObject m_serverConfig; // root -> (mode -> server) QMap> m_servers; QHash m_docs; bool m_incrementalSync = false; // highlightingModeRegex => language id std::vector> m_highlightingModeRegexToLanguageId; // cache of highlighting mode => language id, to avoid massive regex matching QHash m_highlightingModeToLanguageIdCache; typedef QVector> ServerList; public: LSPClientServerManagerImpl(LSPClientPlugin *plugin, KTextEditor::MainWindow *mainWin) : m_plugin(plugin) , m_mainWindow(mainWin) { connect(plugin, &LSPClientPlugin::update, this, &self_type::updateServerConfig); QTimer::singleShot(100, this, &self_type::updateServerConfig); } ~LSPClientServerManagerImpl() override { // stop everything as we go down // several stages; // stage 1; request shutdown of all servers (in parallel) // (give that some time) // stage 2; send TERM // stage 3; send KILL // stage 1 QEventLoop q; QTimer t; connect(&t, &QTimer::timeout, &q, &QEventLoop::quit); /* some msleep are used below which is somewhat BAD as it blocks/hangs * the mainloop, however there is not much alternative: * + running an inner mainloop leads to event processing, * which could trigger an unexpected sequence of 'events' * such as (re)loading plugin that is currently still unloading * (consider scenario of fast-clicking enable/disable of LSP plugin) * + could reduce or forego the sleep, but that increases chances * on an unclean shutdown of LSP server, which may or may not * be able to handle that properly (so let's try and be a polite * client and try to avoid that to some degree) * So we are left with a minor sleep compromise ... */ int count = 0; for (const auto &el : m_servers) { for (const auto &si : el) { auto &s = si.server; if (!s) continue; disconnect(s.data(), nullptr, this, nullptr); if (s->state() != LSPClientServer::State::None) { auto handler = [&q, &count, s]() { if (s->state() != LSPClientServer::State::None) { if (--count == 0) { q.quit(); } } }; connect(s.data(), &LSPClientServer::stateChanged, this, handler); ++count; s->stop(-1, -1); } } } QThread::msleep(500); // stage 2 and 3 count = 0; for (count = 0; count < 2; ++count) { for (const auto &el : m_servers) { for (const auto &si : el) { auto &s = si.server; if (!s) continue; s->stop(count == 0 ? 1 : -1, count == 0 ? -1 : 1); } } QThread::msleep(100); } } // map (highlight)mode to lsp languageId QString languageId(const QString &mode) { // query cache first const auto cacheIt = m_highlightingModeToLanguageIdCache.find(mode); if (cacheIt != m_highlightingModeToLanguageIdCache.end()) return cacheIt.value(); // match via regexes + cache result for (auto it : m_highlightingModeRegexToLanguageId) { if (it.first.match(mode).hasMatch()) { m_highlightingModeToLanguageIdCache[mode] = it.second; return it.second; } } // else: we have no matching server! m_highlightingModeToLanguageIdCache[mode] = QString(); return QString(); } void setIncrementalSync(bool inc) override { m_incrementalSync = inc; } QSharedPointer findServer(KTextEditor::Document *document, bool updatedoc = true) override { if (!document || document->url().isEmpty()) return nullptr; auto it = m_docs.find(document); auto server = it != m_docs.end() ? it->server : nullptr; if (!server) { if ((server = _findServer(document))) trackDocument(document, server); } if (server && updatedoc) update(server.data(), false); return server; } QSharedPointer findServer(KTextEditor::View *view, bool updatedoc = true) override { return view ? findServer(view->document(), updatedoc) : nullptr; } // restart a specific server or all servers if server == nullptr void restart(LSPClientServer *server) override { ServerList servers; // find entry for server(s) and move out for (auto &m : m_servers) { for (auto it = m.begin(); it != m.end();) { if (!server || it->server.data() == server) { servers.push_back(it->server); it = m.erase(it); } else { ++it; } } } restart(servers); } qint64 revision(KTextEditor::Document *doc) override { auto it = m_docs.find(doc); return it != m_docs.end() ? it->version : -1; } LSPClientRevisionSnapshot *snapshot(LSPClientServer *server) override { auto result = new LSPClientRevisionSnapshotImpl; for (auto it = m_docs.begin(); it != m_docs.end(); ++it) { if (it->server == server) { // sync server to latest revision that will be recorded update(it.key(), false); result->add(it.key()); } } return result; } private: void showMessage(const QString &msg, KTextEditor::Message::MessageType level) { // inform interested view(er) which will decide how/where to show emit LSPClientServerManager::showMessage(level, msg); } // caller ensures that servers are no longer present in m_servers void restart(const ServerList &servers) { // close docs for (const auto &server : servers) { // controlling server here, so disable usual state tracking response disconnect(server.data(), nullptr, this, nullptr); for (auto it = m_docs.begin(); it != m_docs.end();) { auto &item = it.value(); if (item.server == server) { // no need to close if server not in proper state if (server->state() != LSPClientServer::State::Running) { item.open = false; } it = _close(it, true); } else { ++it; } } } // helper captures servers auto stopservers = [servers](int t, int k) { for (const auto &server : servers) { server->stop(t, k); } }; // trigger server shutdown now stopservers(-1, -1); // initiate delayed stages (TERM and KILL) // async, so give a bit more time QTimer::singleShot(2 * TIMEOUT_SHUTDOWN, this, [stopservers]() { stopservers(1, -1); }); QTimer::singleShot(4 * TIMEOUT_SHUTDOWN, this, [stopservers]() { stopservers(-1, 1); }); // as for the start part // trigger interested parties, which will again request a server as needed // let's delay this; less chance for server instances to trip over each other QTimer::singleShot(6 * TIMEOUT_SHUTDOWN, this, [this]() { emit serverChanged(); }); } void onStateChanged(LSPClientServer *server) { if (server->state() == LSPClientServer::State::Running) { // send settings if pending for (auto &m : m_servers) { for (auto &si : m) { if (si.server.data() == server && !si.settings.isUndefined()) { server->didChangeConfiguration(si.settings); } } } // clear for normal operation emit serverChanged(); } else if (server->state() == LSPClientServer::State::None) { // went down // find server info to see how bad this is // if this is an occasional termination/crash ... ok then // if this happens quickly (bad/missing server, wrong cmdline/config), then no restart QSharedPointer sserver; QString url; bool retry = true; for (auto &m : m_servers) { for (auto &si : m) { if (si.server.data() == server) { url = si.url; if (si.started.secsTo(QTime::currentTime()) < 60) { ++si.failcount; } // clear the entry, which will be re-filled if needed // otherwise, leave it in place as a dead mark not to re-create one in _findServer if (si.failcount < 2) { std::swap(sserver, si.server); } else { sserver = si.server; retry = false; } } } } auto action = retry ? i18n("Restarting") : i18n("NOT Restarting"); showMessage(i18n("Server terminated unexpectedly ... %1 [%2] [homepage: %3] ", action, server->cmdline().join(QLatin1Char(' ')), url), KTextEditor::Message::Warning); if (sserver) { // sserver might still be in m_servers // but since it died already bringing it down will have no (ill) effect restart({sserver}); } } } QSharedPointer _findServer(KTextEditor::Document *document) { // compute the LSP standardized language id, none found => no change auto langId = languageId(document->highlightingMode()); if (langId.isEmpty()) return nullptr; QObject *projectView = m_mainWindow->pluginView(QStringLiteral("kateprojectplugin")); const auto projectBase = QDir(projectView ? projectView->property("projectBaseDir").toString() : QString()); const auto &projectMap = projectView ? projectView->property("projectMap").toMap() : QVariantMap(); // merge with project specific auto projectConfig = QJsonDocument::fromVariant(projectMap).object().value(QStringLiteral("lspclient")).toObject(); auto serverConfig = merge(m_serverConfig, projectConfig); // locate server config QJsonValue config; QSet used; // reduce langId auto realLangId = langId; while (true) { qCInfo(LSPCLIENT) << "language id " << langId; used << langId; config = serverConfig.value(QStringLiteral("servers")).toObject().value(langId); if (config.isObject()) { const auto &base = config.toObject().value(QStringLiteral("use")).toString(); // basic cycle detection if (!base.isEmpty() && !used.contains(base)) { langId = base; continue; } } break; } if (!config.isObject()) return nullptr; // merge global settings serverConfig = merge(serverConfig.value(QStringLiteral("global")).toObject(), config.toObject()); QString rootpath; auto rootv = serverConfig.value(QStringLiteral("root")); if (rootv.isString()) { auto sroot = rootv.toString(); if (QDir::isAbsolutePath(sroot)) { rootpath = sroot; } else if (!projectBase.isEmpty()) { rootpath = QDir(projectBase).absoluteFilePath(sroot); } } /** * no explicit set root dir? search for a matching root based on some name filters * this is required for some LSP servers like rls that don't handle that on their own like * clangd does */ if (rootpath.isEmpty()) { const auto fileNamesForDetection = serverConfig.value(QStringLiteral("rootIndicationFileNames")); if (fileNamesForDetection.isArray()) { // we try each file name alternative in the listed order // this allows to have preferences for (auto name : fileNamesForDetection.toArray()) { if (name.isString()) { rootpath = rootForDocumentAndRootIndicationFileName(document, name.toString()); if (!rootpath.isEmpty()) { break; } } } } } // last fallback: home directory if (rootpath.isEmpty()) { rootpath = QDir::homePath(); } auto root = QUrl::fromLocalFile(rootpath); auto &serverinfo = m_servers[root][langId]; auto &server = serverinfo.server; if (!server) { QStringList cmdline; // choose debug command line for debug mode, fallback to command auto vcmdline = serverConfig.value(m_plugin->m_debugMode ? QStringLiteral("commandDebug") : QStringLiteral("command")); if (vcmdline.isUndefined()) { vcmdline = serverConfig.value(QStringLiteral("command")); } auto scmdline = vcmdline.toString(); if (!scmdline.isEmpty()) { cmdline = scmdline.split(QLatin1Char(' ')); } else { for (const auto &c : vcmdline.toArray()) { cmdline.push_back(c.toString()); } } if (cmdline.length() > 0) { server.reset(new LSPClientServer(cmdline, root, realLangId, serverConfig.value(QStringLiteral("initializationOptions")))); connect(server.data(), &LSPClientServer::stateChanged, this, &self_type::onStateChanged, Qt::UniqueConnection); if (!server->start(m_plugin)) { showMessage(i18n("Failed to start server: %1", cmdline.join(QLatin1Char(' '))), KTextEditor::Message::Error); } else { showMessage(i18n("Started server %2: %1", cmdline.join(QLatin1Char(' ')), serverDescription(server.data())), KTextEditor::Message::Positive); } serverinfo.settings = serverConfig.value(QStringLiteral("settings")); serverinfo.started = QTime::currentTime(); serverinfo.url = serverConfig.value(QStringLiteral("url")).toString(); // leave failcount as-is } } return (server && server->state() == LSPClientServer::State::Running) ? server : nullptr; } void updateServerConfig() { // default configuration, compiled into plugin resource, reading can't fail QFile defaultConfigFile(QStringLiteral(":/lspclient/settings.json")); defaultConfigFile.open(QIODevice::ReadOnly); Q_ASSERT(defaultConfigFile.isOpen()); m_serverConfig = QJsonDocument::fromJson(defaultConfigFile.readAll()).object(); // consider specified configuration if existing const auto configPath = m_plugin->configPath().toLocalFile(); if (!configPath.isEmpty() && QFile::exists(configPath)) { QFile f(configPath); if (f.open(QIODevice::ReadOnly)) { const auto data = f.readAll(); if (!data.isEmpty()) { - QJsonParseError error; + QJsonParseError error{}; auto json = QJsonDocument::fromJson(data, &error); if (error.error == QJsonParseError::NoError) { if (json.isObject()) { m_serverConfig = merge(m_serverConfig, json.object()); } else { showMessage(i18n("Failed to parse server configuration '%1': no JSON object", configPath), KTextEditor::Message::Error); } } else { showMessage(i18n("Failed to parse server configuration '%1': %2", configPath, error.errorString()), KTextEditor::Message::Error); } } } else { showMessage(i18n("Failed to read server configuration: %1", configPath), KTextEditor::Message::Error); } } // build regex of highlightingMode => language id m_highlightingModeRegexToLanguageId.clear(); m_highlightingModeToLanguageIdCache.clear(); const auto servers = m_serverConfig.value(QLatin1String("servers")).toObject(); for (auto it = servers.begin(); it != servers.end(); ++it) { // get highlighting mode regex for this server, if not set, fallback to just the name QString highlightingModeRegex = it.value().toObject().value(QLatin1String("highlightingModeRegex")).toString(); if (highlightingModeRegex.isEmpty()) { highlightingModeRegex = it.key(); } m_highlightingModeRegexToLanguageId.emplace_back(QRegularExpression(highlightingModeRegex, QRegularExpression::CaseInsensitiveOption), it.key()); } // we could (but do not) perform restartAll here; // for now let's leave that up to user // but maybe we do have a server now where not before, so let's signal emit serverChanged(); } void trackDocument(KTextEditor::Document *doc, const QSharedPointer &server) { auto it = m_docs.find(doc); if (it == m_docs.end()) { KTextEditor::MovingInterface *miface = qobject_cast(doc); it = m_docs.insert(doc, {server, miface, doc->url(), 0, false, false, {}}); // track document connect(doc, &KTextEditor::Document::documentUrlChanged, this, &self_type::untrack, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::highlightingModeChanged, this, &self_type::untrack, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::aboutToClose, this, &self_type::untrack, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::destroyed, this, &self_type::untrack, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::textChanged, this, &self_type::onTextChanged, Qt::UniqueConnection); // in case of incremental change connect(doc, &KTextEditor::Document::textInserted, this, &self_type::onTextInserted, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::textRemoved, this, &self_type::onTextRemoved, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::lineWrapped, this, &self_type::onLineWrapped, Qt::UniqueConnection); connect(doc, &KTextEditor::Document::lineUnwrapped, this, &self_type::onLineUnwrapped, Qt::UniqueConnection); } else { it->server = server; } } decltype(m_docs)::iterator _close(decltype(m_docs)::iterator it, bool remove) { if (it != m_docs.end()) { if (it->open) { // release server side (use url as registered with) (it->server)->didClose(it->url); it->open = false; } if (remove) { disconnect(it.key(), nullptr, this, nullptr); it = m_docs.erase(it); } } return it; } void _close(KTextEditor::Document *doc, bool remove) { auto it = m_docs.find(doc); if (it != m_docs.end()) { _close(it, remove); } } void untrack(QObject *doc) { _close(qobject_cast(doc), true); emit serverChanged(); } void close(KTextEditor::Document *doc) { _close(doc, false); } void update(const decltype(m_docs)::iterator &it, bool force) { auto doc = it.key(); if (it != m_docs.end() && it->server) { it->version = it->movingInterface->revision(); if (!m_incrementalSync) { it->changes.clear(); } if (it->open) { if (it->modified || force) { (it->server)->didChange(it->url, it->version, (it->changes.empty()) ? doc->text() : QString(), it->changes); } } else { (it->server)->didOpen(it->url, it->version, languageId(doc->highlightingMode()), doc->text()); it->open = true; } it->modified = false; it->changes.clear(); } } void update(KTextEditor::Document *doc, bool force) override { update(m_docs.find(doc), force); } void update(LSPClientServer *server, bool force) { for (auto it = m_docs.begin(); it != m_docs.end(); ++it) { if (it->server == server) { update(it, force); } } } void onTextChanged(KTextEditor::Document *doc) { auto it = m_docs.find(doc); if (it != m_docs.end()) { it->modified = true; } } DocumentInfo *getDocumentInfo(KTextEditor::Document *doc) { if (!m_incrementalSync) return nullptr; auto it = m_docs.find(doc); if (it != m_docs.end() && it->server) { const auto &caps = it->server->capabilities(); if (caps.textDocumentSync == LSPDocumentSyncKind::Incremental) { return &(*it); } } return nullptr; } void onTextInserted(KTextEditor::Document *doc, const KTextEditor::Cursor &position, const QString &text) { auto info = getDocumentInfo(doc); if (info) { info->changes.push_back({LSPRange {position, position}, text}); } } void onTextRemoved(KTextEditor::Document *doc, const KTextEditor::Range &range, const QString &text) { (void)text; auto info = getDocumentInfo(doc); if (info) { info->changes.push_back({range, QString()}); } } void onLineWrapped(KTextEditor::Document *doc, const KTextEditor::Cursor &position) { // so a 'newline' has been inserted at position // could have been UNIX style or other kind, let's ask the document auto text = doc->text({position, {position.line() + 1, 0}}); onTextInserted(doc, position, text); } void onLineUnwrapped(KTextEditor::Document *doc, int line) { // lines line-1 and line got replaced by current content of line-1 Q_ASSERT(line > 0); auto info = getDocumentInfo(doc); if (info) { LSPRange oldrange {{line - 1, 0}, {line + 1, 0}}; LSPRange newrange {{line - 1, 0}, {line, 0}}; auto text = doc->text(newrange); info->changes.push_back({oldrange, text}); } } }; QSharedPointer LSPClientServerManager::new_(LSPClientPlugin *plugin, KTextEditor::MainWindow *mainWin) { return QSharedPointer(new LSPClientServerManagerImpl(plugin, mainWin)); } #include "lspclientservermanager.moc" diff --git a/addons/openheader/plugin_kateopenheader.cpp b/addons/openheader/plugin_kateopenheader.cpp index cf8bc6f83..c78d1cabe 100644 --- a/addons/openheader/plugin_kateopenheader.cpp +++ b/addons/openheader/plugin_kateopenheader.cpp @@ -1,233 +1,232 @@ /* This file is part of the KDE project Copyright (C) 2001 Joseph Wenninger Copyright (C) 2009 Erlend Hamberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugin_kateopenheader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KateOpenHeaderFactory, "kateopenheaderplugin.json", registerPlugin();) // K_EXPORT_PLUGIN(KateOpenHeaderFactory(KAboutData("kateopenheader","kateopenheader",ki18n("Open Header"), "0.1", ki18n("Open header for a source file"), KAboutData::License_LGPL_V2)) ) PluginViewKateOpenHeader::PluginViewKateOpenHeader(PluginKateOpenHeader *plugin, KTextEditor::MainWindow *mainwindow) : KTextEditor::Command(QStringList() << QStringLiteral("toggle-header"), mainwindow) - , KXMLGUIClient() , m_plugin(plugin) , m_mainWindow(mainwindow) { KXMLGUIClient::setComponentName(QStringLiteral("kateopenheaderplugin"), i18n("Open Header")); setXMLFile(QStringLiteral("ui.rc")); QAction *a = actionCollection()->addAction(QStringLiteral("file_openheader")); a->setText(i18n("Open .h/.cpp/.c")); actionCollection()->setDefaultShortcut(a, Qt::Key_F12); connect(a, &QAction::triggered, plugin, &PluginKateOpenHeader::slotOpenHeader); mainwindow->guiFactory()->addClient(this); } PluginViewKateOpenHeader::~PluginViewKateOpenHeader() { m_mainWindow->guiFactory()->removeClient(this); } PluginKateOpenHeader::PluginKateOpenHeader(QObject *parent, const QList &) : KTextEditor::Plugin(parent) { } PluginKateOpenHeader::~PluginKateOpenHeader() { } QObject *PluginKateOpenHeader::createView(KTextEditor::MainWindow *mainWindow) { return new PluginViewKateOpenHeader(this, mainWindow); } void PluginKateOpenHeader::slotOpenHeader() { KTextEditor::Application *application = KTextEditor::Editor::instance()->application(); if (!application->activeMainWindow()) return; KTextEditor::View *kv(application->activeMainWindow()->activeView()); if (!kv) return; QUrl url = kv->document()->url(); if ((!url.isValid()) || (url.isEmpty())) return; qDebug() << "Trying to open opposite of " << url.toString(); qDebug() << "Trying to open opposite of toLocalFile:" << url.toLocalFile(); qDebug() << "Trying to open opposite of path:" << url.path(); QFileInfo info(url.path()); QString extension = info.suffix().toLower(); QStringList headers(QStringList() << QStringLiteral("h") << QStringLiteral("H") << QStringLiteral("hh") << QStringLiteral("hpp") << QStringLiteral("cuh")); QStringList sources(QStringList() << QStringLiteral("c") << QStringLiteral("cpp") << QStringLiteral("cc") << QStringLiteral("cp") << QStringLiteral("cxx") << QStringLiteral("m") << QStringLiteral("cu")); if (sources.contains(extension)) { if (tryOpenInternal(url, headers)) return; tryOpen(url, headers); } else if (headers.contains(extension)) { if (tryOpenInternal(url, sources)) return; tryOpen(url, sources); } } bool PluginKateOpenHeader::tryOpenInternal(const QUrl &url, const QStringList &extensions) { KTextEditor::Application *application = KTextEditor::Editor::instance()->application(); if (!application->activeMainWindow()) return false; qDebug() << "Trying to find already opened" << url.toString() << " with extensions " << extensions.join(QLatin1Char(' ')); QString basename = QFileInfo(url.path()).baseName(); QUrl newURL(url); for (const auto &extension : extensions) { setFileName(&newURL, basename + QStringLiteral(".") + extension); KTextEditor::Document *doc = application->findUrl(newURL); if (doc) { application->activeMainWindow()->openUrl(newURL); return true; } setFileName(&newURL, basename + QStringLiteral(".") + extension.toUpper()); doc = application->findUrl(newURL); if (doc) { application->activeMainWindow()->openUrl(newURL); return true; } } return false; } void PluginKateOpenHeader::tryOpen(const QUrl &url, const QStringList &extensions) { KTextEditor::Application *application = KTextEditor::Editor::instance()->application(); if (!application->activeMainWindow()) return; qDebug() << "Trying to open " << url.toString() << " with extensions " << extensions.join(QLatin1Char(' ')); QString basename = QFileInfo(url.path()).baseName(); QUrl newURL(url); for (const auto &extension : extensions) { setFileName(&newURL, basename + QStringLiteral(".") + extension); if (fileExists(newURL)) { application->activeMainWindow()->openUrl(newURL); return; } setFileName(&newURL, basename + QStringLiteral(".") + extension.toUpper()); if (fileExists(newURL)) { application->activeMainWindow()->openUrl(newURL); return; } } } bool PluginKateOpenHeader::fileExists(const QUrl &url) { if (url.isLocalFile()) { return QFile::exists(url.toLocalFile()); } KIO::JobFlags flags = KIO::DefaultFlags; KIO::StatJob *job = KIO::stat(url, flags); KJobWidgets::setWindow(job, KTextEditor::Editor::instance()->application()->activeMainWindow()->window()); job->setSide(KIO::StatJob::DestinationSide /*SourceSide*/); job->exec(); return !job->error(); } void PluginKateOpenHeader::setFileName(QUrl *url, const QString &_txt) { url->setFragment(QString()); int i = 0; while (i < _txt.length() && _txt[i] == QLatin1Char('/')) { ++i; } QString tmp = i ? _txt.mid(i) : _txt; QString path = url->path(); if (path.isEmpty()) #ifdef Q_OS_WIN path = url->isLocalFile() ? QDir::rootPath() : QStringLiteral("/"); #else path = QDir::rootPath(); #endif else { int lastSlash = path.lastIndexOf(QLatin1Char('/')); if (lastSlash == -1) { path.clear(); // there's only the file name, remove it } else if (!path.endsWith(QLatin1Char('/'))) { path.truncate(lastSlash + 1); // keep the "/" } } path += tmp; url->setPath(path); } bool PluginViewKateOpenHeader::exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &) { Q_UNUSED(view) Q_UNUSED(cmd) Q_UNUSED(msg) m_plugin->slotOpenHeader(); return true; } bool PluginViewKateOpenHeader::help(KTextEditor::View *view, const QString &cmd, QString &msg) { Q_UNUSED(view) Q_UNUSED(cmd) msg = i18n( "

toggle-header — switch between header and corresponding c/cpp file

" "

usage: toggle-header

" "

When editing C or C++ code, this command will switch between a header file and " "its corresponding C/C++ file or vice versa.

" "

For example, if you are editing myclass.cpp, toggle-header will change " "to myclass.h if this file is available.

" "

Pairs of the following filename suffixes will work:
" " Header files: h, H, hh, hpp
" " Source files: c, cpp, cc, cp, cxx

"); return true; } #include "plugin_kateopenheader.moc" diff --git a/addons/project/kateproject.cpp b/addons/project/kateproject.cpp index 8930ebdc0..0ae203c1d 100644 --- a/addons/project/kateproject.cpp +++ b/addons/project/kateproject.cpp @@ -1,451 +1,449 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateproject.h" #include "kateprojectplugin.h" #include "kateprojectworker.h" #include #include #include #include #include #include #include #include #include #include #include #include KateProject::KateProject(ThreadWeaver::Queue *weaver, KateProjectPlugin *plugin) - : QObject() - , m_fileLastModified() - , m_notesDocument(nullptr) + : m_notesDocument(nullptr) , m_untrackedDocumentsRoot(nullptr) , m_weaver(weaver) , m_plugin(plugin) { } KateProject::~KateProject() { saveNotesDocument(); } bool KateProject::loadFromFile(const QString &fileName) { /** * bail out if already fileName set! */ if (!m_fileName.isEmpty()) { return false; } /** * set new filename and base directory */ m_fileName = fileName; m_baseDir = QFileInfo(m_fileName).canonicalPath(); /** * trigger reload */ return reload(); } bool KateProject::reload(bool force) { QVariantMap map = readProjectFile(); if (map.isEmpty()) { m_fileLastModified = QDateTime(); } else { m_fileLastModified = QFileInfo(m_fileName).lastModified(); m_globalProject = map; } return load(m_globalProject, force); } QVariantMap KateProject::readProjectFile() const { QFile file(m_fileName); if (!file.open(QFile::ReadOnly)) { return QVariantMap(); } /** * parse the whole file, bail out again on error! */ const QByteArray jsonData = file.readAll(); - QJsonParseError parseError; + QJsonParseError parseError{}; QJsonDocument project(QJsonDocument::fromJson(jsonData, &parseError)); if (parseError.error != QJsonParseError::NoError) { return QVariantMap(); } /** * convenience; align with auto-generated projects * generate 'name' and 'files' if not specified explicitly, * so those parts need not be given if only wishes to specify additional * project configuration (e.g. build, ctags) */ if (project.isObject()) { auto dir = QFileInfo(m_fileName).dir(); auto object = project.object(); auto name = object[QStringLiteral("name")]; if (name.isUndefined() || name.isNull()) { name = dir.dirName(); } auto files = object[QStringLiteral("files")]; if (files.isUndefined() || files.isNull()) { // support all we can, could try to detect, // but it will be sorted out upon loading anyway QJsonArray afiles; for (const auto &t : {QStringLiteral("git"), QStringLiteral("hg"), QStringLiteral("svn"), QStringLiteral("darcs")}) { afiles.push_back(QJsonObject {{t, true}}); } files = afiles; } project.setObject(object); } return project.toVariant().toMap(); } bool KateProject::loadFromData(const QVariantMap &globalProject, const QString &directory) { m_baseDir = directory; m_fileName = QDir(directory).filePath(QStringLiteral(".kateproject")); m_globalProject = globalProject; return load(globalProject); } bool KateProject::load(const QVariantMap &globalProject, bool force) { /** * no name, bad => bail out */ if (globalProject[QStringLiteral("name")].toString().isEmpty()) { return false; } /** * support out-of-source project files */ if (!globalProject[QStringLiteral("directory")].toString().isEmpty()) { m_baseDir = QFileInfo(globalProject[QStringLiteral("directory")].toString()).canonicalFilePath(); } /** * anything changed? * else be done without forced reload! */ if (!force && (m_projectMap == globalProject)) { return true; } /** * setup global attributes in this object */ m_projectMap = globalProject; // emit that we changed stuff emit projectMapChanged(); // trigger loading of project in background thread QString indexDir; if (m_plugin->getIndexEnabled()) { indexDir = m_plugin->getIndexDirectory().toLocalFile(); // if empty, use regular tempdir if (indexDir.isEmpty()) { indexDir = QDir::tempPath(); } } auto w = new KateProjectWorker(m_baseDir, indexDir, m_projectMap, force); connect(w, &KateProjectWorker::loadDone, this, &KateProject::loadProjectDone); connect(w, &KateProjectWorker::loadIndexDone, this, &KateProject::loadIndexDone); m_weaver->stream() << w; // we are done here return true; } void KateProject::loadProjectDone(const KateProjectSharedQStandardItem &topLevel, KateProjectSharedQMapStringItem file2Item) { m_model.clear(); m_model.invisibleRootItem()->appendColumn(topLevel->takeColumn(0)); m_file2Item = std::move(file2Item); /** * readd the documents that are open atm */ m_untrackedDocumentsRoot = nullptr; for (auto i = m_documents.constBegin(); i != m_documents.constEnd(); i++) { registerDocument(i.key()); } emit modelChanged(); } void KateProject::loadIndexDone(KateProjectSharedProjectIndex projectIndex) { /** * move to our project */ m_projectIndex = std::move(projectIndex); /** * notify external world that data is available */ emit indexChanged(); } QString KateProject::projectLocalFileName(const QString &suffix) const { /** * nothing on empty file names for project * should not happen */ if (m_baseDir.isEmpty() || suffix.isEmpty()) { return QString(); } /** * compute full file name */ return m_baseDir + QStringLiteral(".kateproject.") + suffix; } QTextDocument *KateProject::notesDocument() { /** * already there? */ if (m_notesDocument) { return m_notesDocument; } /** * else create it */ m_notesDocument = new QTextDocument(this); m_notesDocument->setDocumentLayout(new QPlainTextDocumentLayout(m_notesDocument)); /** * get file name */ const QString notesFileName = projectLocalFileName(QStringLiteral("notes")); if (notesFileName.isEmpty()) { return m_notesDocument; } /** * and load text if possible */ QFile inFile(notesFileName); if (inFile.open(QIODevice::ReadOnly)) { QTextStream inStream(&inFile); inStream.setCodec("UTF-8"); m_notesDocument->setPlainText(inStream.readAll()); } /** * and be done */ return m_notesDocument; } void KateProject::saveNotesDocument() { /** * no notes document, nothing to do */ if (!m_notesDocument) { return; } /** * get content & filename */ const QString content = m_notesDocument->toPlainText(); const QString notesFileName = projectLocalFileName(QStringLiteral("notes")); if (notesFileName.isEmpty()) { return; } /** * no content => unlink file, if there */ if (content.isEmpty()) { if (QFile::exists(notesFileName)) { QFile::remove(notesFileName); } return; } /** * else: save content to file */ QFile outFile(projectLocalFileName(QStringLiteral("notes"))); if (outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream outStream(&outFile); outStream.setCodec("UTF-8"); outStream << content; } } void KateProject::slotModifiedChanged(KTextEditor::Document *document) { KateProjectItem *item = itemForFile(m_documents.value(document)); if (!item) { return; } item->slotModifiedChanged(document); } void KateProject::slotModifiedOnDisk(KTextEditor::Document *document, bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) { KateProjectItem *item = itemForFile(m_documents.value(document)); if (!item) { return; } item->slotModifiedOnDisk(document, isModified, reason); } void KateProject::registerDocument(KTextEditor::Document *document) { // remember the document, if not already there if (!m_documents.contains(document)) { m_documents[document] = document->url().toLocalFile(); } // try to get item for the document KateProjectItem *item = itemForFile(document->url().toLocalFile()); // if we got one, we are done, else create a dummy! if (item) { disconnect(document, &KTextEditor::Document::modifiedChanged, this, &KateProject::slotModifiedChanged); disconnect(document, SIGNAL(modifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)), this, SLOT(slotModifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason))); item->slotModifiedChanged(document); /*FIXME item->slotModifiedOnDisk(document,document->isModified(),qobject_cast(document)->modifiedOnDisk()); FIXME*/ connect(document, &KTextEditor::Document::modifiedChanged, this, &KateProject::slotModifiedChanged); connect(document, SIGNAL(modifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)), this, SLOT(slotModifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason))); return; } registerUntrackedDocument(document); } void KateProject::registerUntrackedDocument(KTextEditor::Document *document) { // perhaps create the parent item if (!m_untrackedDocumentsRoot) { m_untrackedDocumentsRoot = new KateProjectItem(KateProjectItem::Directory, i18n("")); m_model.insertRow(0, m_untrackedDocumentsRoot); } // create document item QFileInfo fileInfo(document->url().toLocalFile()); KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::File, fileInfo.fileName()); fileItem->setData(document->url().toLocalFile(), Qt::ToolTipRole); fileItem->slotModifiedChanged(document); connect(document, &KTextEditor::Document::modifiedChanged, this, &KateProject::slotModifiedChanged); connect(document, SIGNAL(modifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)), this, SLOT(slotModifiedOnDisk(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason))); bool inserted = false; for (int i = 0; i < m_untrackedDocumentsRoot->rowCount(); ++i) { if (m_untrackedDocumentsRoot->child(i)->data(Qt::UserRole).toString() > document->url().toLocalFile()) { m_untrackedDocumentsRoot->insertRow(i, fileItem); inserted = true; break; } } if (!inserted) { m_untrackedDocumentsRoot->appendRow(fileItem); } fileItem->setData(document->url().toLocalFile(), Qt::UserRole); fileItem->setData(QVariant(true), Qt::UserRole + 3); if (!m_file2Item) { m_file2Item = KateProjectSharedQMapStringItem(new QMap()); } (*m_file2Item)[document->url().toLocalFile()] = fileItem; } void KateProject::unregisterDocument(KTextEditor::Document *document) { if (!m_documents.contains(document)) { return; } disconnect(document, &KTextEditor::Document::modifiedChanged, this, &KateProject::slotModifiedChanged); const QString &file = m_documents.value(document); if (m_untrackedDocumentsRoot) { KateProjectItem *item = static_cast(itemForFile(file)); if (item && item->data(Qt::UserRole + 3).toBool()) { unregisterUntrackedItem(item); m_file2Item->remove(file); } } m_documents.remove(document); } void KateProject::unregisterUntrackedItem(const KateProjectItem *item) { for (int i = 0; i < m_untrackedDocumentsRoot->rowCount(); ++i) { if (m_untrackedDocumentsRoot->child(i) == item) { m_untrackedDocumentsRoot->removeRow(i); break; } } if (m_untrackedDocumentsRoot->rowCount() < 1) { m_model.removeRow(0); m_untrackedDocumentsRoot = nullptr; } } diff --git a/addons/project/kateprojectcompletion.h b/addons/project/kateprojectcompletion.h index 7b138b4c0..a05283d08 100644 --- a/addons/project/kateprojectcompletion.h +++ b/addons/project/kateprojectcompletion.h @@ -1,97 +1,97 @@ /* This file is part of the Kate project. * * Copyright (C) 2010 Christoph Cullmann * Copyright (C) 2003 Anders Lund * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATE_PROJECT_COMPLETION_H #define KATE_PROJECT_COMPLETION_H #include #include #include #include /** * Project wide completion support. */ class KateProjectCompletion : public KTextEditor::CodeCompletionModel, public KTextEditor::CodeCompletionModelControllerInterface { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) public: /** * Construct project completion. * @param plugin our plugin */ KateProjectCompletion(class KateProjectPlugin *plugin); /** * Deconstruct project completion. */ ~KateProjectCompletion() override; /** * This function is responsible to generating / updating the list of current * completions. The default implementation does nothing. * * When implementing this function, remember to call setRowCount() (or implement * rowCount()), and to generate the appropriate change notifications (for instance * by calling QAbstractItemModel::reset()). * @param view The view to generate completions for * @param range The range of text to generate completions for * */ void completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType) override; bool shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) override; bool shouldAbortCompletion(KTextEditor::View *view, const KTextEditor::Range &range, const QString ¤tCompletion) override; void saveMatches(KTextEditor::View *view, const KTextEditor::Range &range); int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; MatchReaction matchingItem(const QModelIndex &matched) override; KTextEditor::Range completionRange(KTextEditor::View *view, const KTextEditor::Cursor &position) override; void allMatches(QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range &range) const; private: /** * our plugin view */ KateProjectPlugin *m_plugin; /** * model with matching data */ QStandardItemModel m_matches; /** * automatic invocation? */ - bool m_automatic; + bool m_automatic = false; }; #endif diff --git a/addons/project/kateprojectinfoview.cpp b/addons/project/kateprojectinfoview.cpp index 47f0ae949..d0d25c266 100644 --- a/addons/project/kateprojectinfoview.cpp +++ b/addons/project/kateprojectinfoview.cpp @@ -1,81 +1,80 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectinfoview.h" #include "kateprojectinfoviewcodeanalysis.h" #include "kateprojectinfoviewindex.h" #include "kateprojectinfoviewnotes.h" #include "kateprojectinfoviewterminal.h" #include "kateprojectpluginview.h" #include "klocalizedstring.h" #include KateProjectInfoView::KateProjectInfoView(KateProjectPluginView *pluginView, KateProject *project) - : QTabWidget() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_project(project) { /** * skip terminal toolviews if no terminal aka KonsolePart around */ if (KateProjectInfoViewTerminal::pluginFactory()) { /** * terminal for the directory with the .kateproject file inside */ const QString projectPath = QFileInfo(QFileInfo(m_project->fileName()).path()).canonicalFilePath(); if (!projectPath.isEmpty()) { addTab(new KateProjectInfoViewTerminal(pluginView, projectPath), i18n("Terminal (.kateproject)")); } /** * terminal for the base directory, if different to directory of .kateproject */ const QString basePath = QFileInfo(m_project->baseDir()).canonicalFilePath(); if (!basePath.isEmpty() && projectPath != basePath) { addTab(new KateProjectInfoViewTerminal(pluginView, basePath), i18n("Terminal (Base)")); } } /** * index */ addTab(new KateProjectInfoViewIndex(pluginView, project), i18n("Code Index")); /** * code analysis */ addTab(new KateProjectInfoViewCodeAnalysis(pluginView, project), i18n("Code Analysis")); /** * notes */ addTab(new KateProjectInfoViewNotes(pluginView, project), i18n("Notes")); } KateProjectInfoView::~KateProjectInfoView() { } void KateProjectInfoView::showEvent(QShowEvent *) { setFocusProxy(currentWidget()); } diff --git a/addons/project/kateprojectinfoviewcodeanalysis.cpp b/addons/project/kateprojectinfoviewcodeanalysis.cpp index f5d57b802..55ae0389f 100644 --- a/addons/project/kateprojectinfoviewcodeanalysis.cpp +++ b/addons/project/kateprojectinfoviewcodeanalysis.cpp @@ -1,249 +1,248 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectinfoviewcodeanalysis.h" #include "kateprojectcodeanalysistool.h" #include "kateprojectpluginview.h" #include "tools/kateprojectcodeanalysisselector.h" #include #include #include #include #include #include KateProjectInfoViewCodeAnalysis::KateProjectInfoViewCodeAnalysis(KateProjectPluginView *pluginView, KateProject *project) - : QWidget() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_project(project) , m_messageWidget(nullptr) , m_startStopAnalysis(new QPushButton(i18n("Start Analysis..."))) , m_treeView(new QTreeView(this)) , m_model(new QStandardItemModel(m_treeView)) , m_analyzer(nullptr) , m_analysisTool(nullptr) , m_toolSelector(new QComboBox()) { /** * default style */ m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_treeView->setUniformRowHeights(true); m_treeView->setRootIsDecorated(false); m_model->setHorizontalHeaderLabels(QStringList() << i18n("File") << i18n("Line") << i18n("Severity") << i18n("Message")); /** * attach model * kill selection model */ QItemSelectionModel *m = m_treeView->selectionModel(); m_treeView->setModel(m_model); delete m; m_treeView->setSortingEnabled(true); m_treeView->sortByColumn(1, Qt::AscendingOrder); m_treeView->sortByColumn(2, Qt::AscendingOrder); /** * Connect selection change callback * and attach model to code analysis selector */ connect(m_toolSelector, static_cast(&QComboBox::currentIndexChanged), this, &KateProjectInfoViewCodeAnalysis::slotToolSelectionChanged); m_toolSelector->setModel(KateProjectCodeAnalysisSelector::model(this)); /** * layout widget */ QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); // top: selector and buttons... QHBoxLayout *hlayout = new QHBoxLayout; layout->addLayout(hlayout); hlayout->setSpacing(0); hlayout->addWidget(m_toolSelector); auto infoButton = new QPushButton(QIcon::fromTheme(QStringLiteral("documentinfo")), QString(), this); infoButton->setFocusPolicy(Qt::FocusPolicy::TabFocus); connect(infoButton, &QPushButton::clicked, this, [this]() { QToolTip::showText(QCursor::pos(), m_toolInfoText); }); hlayout->addWidget(infoButton); hlayout->addWidget(m_startStopAnalysis); hlayout->addStretch(); // below: result list... layout->addWidget(m_treeView); setLayout(layout); /** * connect needed signals */ connect(m_startStopAnalysis, &QPushButton::clicked, this, &KateProjectInfoViewCodeAnalysis::slotStartStopClicked); connect(m_treeView, &QTreeView::clicked, this, &KateProjectInfoViewCodeAnalysis::slotClicked); } KateProjectInfoViewCodeAnalysis::~KateProjectInfoViewCodeAnalysis() { delete m_analyzer; } void KateProjectInfoViewCodeAnalysis::slotToolSelectionChanged(int) { m_analysisTool = m_toolSelector->currentData(Qt::UserRole + 1).value(); m_toolInfoText = i18n("%1

The tool will be run on all project files which match this list of file extensions:

%2", m_analysisTool->description(), m_analysisTool->fileExtensions()); } void KateProjectInfoViewCodeAnalysis::slotStartStopClicked() { /** * get files for the external tool */ m_analysisTool = m_toolSelector->currentData(Qt::UserRole + 1).value(); m_analysisTool->setProject(m_project); /** * clear existing entries */ m_model->removeRows(0, m_model->rowCount(), QModelIndex()); /** * launch selected tool */ delete m_analyzer; m_analyzer = new QProcess; m_analyzer->setProcessChannelMode(QProcess::MergedChannels); connect(m_analyzer, &QProcess::readyRead, this, &KateProjectInfoViewCodeAnalysis::slotReadyRead); connect(m_analyzer, static_cast(&QProcess::finished), this, &KateProjectInfoViewCodeAnalysis::finished); m_analyzer->start(m_analysisTool->path(), m_analysisTool->arguments()); if (m_messageWidget) { delete m_messageWidget; m_messageWidget = nullptr; } if (!m_analyzer->waitForStarted()) { m_messageWidget = new KMessageWidget(this); m_messageWidget->setCloseButtonVisible(true); m_messageWidget->setMessageType(KMessageWidget::Warning); m_messageWidget->setWordWrap(false); m_messageWidget->setText(m_analysisTool->notInstalledMessage()); static_cast(layout())->addWidget(m_messageWidget); m_messageWidget->animatedShow(); return; } m_startStopAnalysis->setEnabled(false); /** * write files list and close write channel */ const QString stdinMessage = m_analysisTool->stdinMessages(); if (!stdinMessage.isEmpty()) { m_analyzer->write(stdinMessage.toLocal8Bit()); } m_analyzer->closeWriteChannel(); } void KateProjectInfoViewCodeAnalysis::slotReadyRead() { /** * get results of analysis */ while (m_analyzer->canReadLine()) { /** * get one line, split it, skip it, if too few elements */ QString line = QString::fromLocal8Bit(m_analyzer->readLine()); QStringList elements = m_analysisTool->parseLine(line); if (elements.size() < 4) { continue; } /** * feed into model */ QList items; QStandardItem *fileNameItem = new QStandardItem(QFileInfo(elements[0]).fileName()); fileNameItem->setToolTip(elements[0]); items << fileNameItem; items << new QStandardItem(elements[1]); items << new QStandardItem(elements[2]); const auto message = elements[3].simplified(); auto messageItem = new QStandardItem(message); messageItem->setToolTip(message); items << messageItem; m_model->appendRow(items); } /** * tree view polish ;) */ m_treeView->resizeColumnToContents(2); m_treeView->resizeColumnToContents(1); m_treeView->resizeColumnToContents(0); } void KateProjectInfoViewCodeAnalysis::slotClicked(const QModelIndex &index) { /** * get path */ QString filePath = m_model->item(index.row(), 0)->toolTip(); if (filePath.isEmpty()) { return; } /** * create view */ KTextEditor::View *view = m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath)); if (!view) { return; } /** * set cursor, if possible */ int line = m_model->item(index.row(), 1)->text().toInt(); if (line >= 1) { view->setCursorPosition(KTextEditor::Cursor(line - 1, 0)); } } void KateProjectInfoViewCodeAnalysis::finished(int exitCode, QProcess::ExitStatus) { m_startStopAnalysis->setEnabled(true); m_messageWidget = new KMessageWidget(this); m_messageWidget->setCloseButtonVisible(true); m_messageWidget->setWordWrap(false); if (m_analysisTool->isSuccessfulExitCode(exitCode)) { // normally 0 is successful but there are exceptions m_messageWidget->setMessageType(KMessageWidget::Information); m_messageWidget->setText(i18np("Analysis on %1 file finished.", "Analysis on %1 files finished.", m_analysisTool->getActualFilesCount())); } else { // unfortunately, output was eaten by slotReadyRead() // TODO: get stderr output, show it here m_messageWidget->setMessageType(KMessageWidget::Warning); m_messageWidget->setText(i18np("Analysis on %1 file failed with exit code %2.", "Analysis on %1 files failed with exit code %2.", m_analysisTool->getActualFilesCount(), exitCode)); } static_cast(layout())->addWidget(m_messageWidget); m_messageWidget->animatedShow(); } diff --git a/addons/project/kateprojectinfoviewnotes.cpp b/addons/project/kateprojectinfoviewnotes.cpp index 6fdd6e81e..80cb88190 100644 --- a/addons/project/kateprojectinfoviewnotes.cpp +++ b/addons/project/kateprojectinfoviewnotes.cpp @@ -1,45 +1,44 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Joseph Wenninger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectinfoviewnotes.h" #include "kateprojectpluginview.h" #include KateProjectInfoViewNotes::KateProjectInfoViewNotes(KateProjectPluginView *pluginView, KateProject *project) - : QWidget() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_project(project) , m_edit(new QPlainTextEdit()) { /* * layout widget */ QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); layout->addWidget(m_edit); setLayout(layout); m_edit->setDocument(project->notesDocument()); setFocusProxy(m_edit); } KateProjectInfoViewNotes::~KateProjectInfoViewNotes() { } diff --git a/addons/project/kateprojectinfoviewterminal.cpp b/addons/project/kateprojectinfoviewterminal.cpp index bd232ce88..5e54b3643 100644 --- a/addons/project/kateprojectinfoviewterminal.cpp +++ b/addons/project/kateprojectinfoviewterminal.cpp @@ -1,122 +1,121 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectinfoviewterminal.h" #include "kateprojectpluginview.h" #include #include #include KPluginFactory *KateProjectInfoViewTerminal::s_pluginFactory = nullptr; KateProjectInfoViewTerminal::KateProjectInfoViewTerminal(KateProjectPluginView *pluginView, const QString &directory) - : QWidget() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_directory(directory) , m_konsolePart(nullptr) { /** * layout widget */ m_layout = new QVBoxLayout(this); m_layout->setSpacing(0); m_layout->setContentsMargins(0, 0, 0, 0); } KateProjectInfoViewTerminal::~KateProjectInfoViewTerminal() { /** * avoid endless loop */ if (m_konsolePart) { disconnect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &KateProjectInfoViewTerminal::loadTerminal); } } KPluginFactory *KateProjectInfoViewTerminal::pluginFactory() { if (s_pluginFactory) { return s_pluginFactory; } return s_pluginFactory = KPluginLoader(QStringLiteral("konsolepart")).factory(); } void KateProjectInfoViewTerminal::showEvent(QShowEvent *) { /** * we delay the terminal construction until we have some part to have a usable WINDOWID, see bug 411965 */ if (!m_konsolePart) { loadTerminal(); } } void KateProjectInfoViewTerminal::loadTerminal() { /** * null in any case, if loadTerminal fails below and we are in the destroyed event */ m_konsolePart = nullptr; setFocusProxy(nullptr); /** * we shall not arrive here without a factory, if it is not there, no terminal toolview shall be created */ Q_ASSERT(pluginFactory()); /** * create part */ m_konsolePart = pluginFactory()->create(this, this); if (!m_konsolePart) { return; } /** * init locale translation stuff */ // FIXME KF5 KGlobal::locale()->insertCatalog("konsole"); /** * switch to right directory */ qobject_cast(m_konsolePart)->showShellInDir(m_directory); /** * add to widget */ m_layout->addWidget(m_konsolePart->widget()); setFocusProxy(m_konsolePart->widget()); /** * guard destruction, create new terminal! */ connect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &KateProjectInfoViewTerminal::loadTerminal); connect(m_konsolePart, SIGNAL(overrideShortcut(QKeyEvent *, bool &)), this, SLOT(overrideShortcut(QKeyEvent *, bool &))); } void KateProjectInfoViewTerminal::overrideShortcut(QKeyEvent *, bool &override) { /** * let konsole handle all shortcuts */ override = true; } diff --git a/addons/project/kateprojectplugin.cpp b/addons/project/kateprojectplugin.cpp index 6906a6415..928b30a97 100644 --- a/addons/project/kateprojectplugin.cpp +++ b/addons/project/kateprojectplugin.cpp @@ -1,470 +1,473 @@ /* This file is part of the Kate project. * * Copyright (C) 2010 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectplugin.h" #include "kateproject.h" #include "kateprojectconfigpage.h" #include "kateprojectpluginview.h" #include #include #include #include // delete, when we depend on KF 5.63 #include #include #include #include #include #include #include #ifdef HAVE_CTERMID #include #include #include #include #include #endif namespace { const QString ProjectFileName = QStringLiteral(".kateproject"); const QString GitFolderName = QStringLiteral(".git"); const QString SubversionFolderName = QStringLiteral(".svn"); const QString MercurialFolderName = QStringLiteral(".hg"); const QString GitConfig = QStringLiteral("git"); const QString SubversionConfig = QStringLiteral("subversion"); const QString MercurialConfig = QStringLiteral("mercurial"); const QStringList DefaultConfig = QStringList() << GitConfig << SubversionConfig << MercurialConfig; } KateProjectPlugin::KateProjectPlugin(QObject *parent, const QList &) : KTextEditor::Plugin(parent) , m_completion(this) , m_autoGit(true) , m_autoSubversion(true) , m_autoMercurial(true) + , m_indexEnabled(false) + , m_multiProjectCompletion(false) + , m_multiProjectGoto(false) , m_weaver(new ThreadWeaver::Queue(this)) { qRegisterMetaType("KateProjectSharedQStandardItem"); qRegisterMetaType("KateProjectSharedQMapStringItem"); qRegisterMetaType("KateProjectSharedProjectIndex"); connect(KTextEditor::Editor::instance()->application(), &KTextEditor::Application::documentCreated, this, &KateProjectPlugin::slotDocumentCreated); connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &KateProjectPlugin::slotDirectoryChanged); // read configuration prior to cwd project setup below readConfig(); #ifdef HAVE_CTERMID /** * open project for our current working directory, if this kate has a terminal * https://stackoverflow.com/questions/1312922/detect-if-stdin-is-a-terminal-or-pipe-in-c-c-qt */ char tty[L_ctermid + 1] = {0}; ctermid(tty); int fd = ::open(tty, O_RDONLY); if (fd >= 0) { projectForDir(QDir::current()); ::close(fd); } #endif for (auto document : KTextEditor::Editor::instance()->application()->documents()) { slotDocumentCreated(document); } registerVariables(); } KateProjectPlugin::~KateProjectPlugin() { unregisterVariables(); for (KateProject *project : m_projects) { m_fileWatcher.removePath(QFileInfo(project->fileName()).canonicalPath()); delete project; } m_projects.clear(); m_weaver->shutDown(); delete m_weaver; } QObject *KateProjectPlugin::createView(KTextEditor::MainWindow *mainWindow) { return new KateProjectPluginView(this, mainWindow); } int KateProjectPlugin::configPages() const { return 1; } KTextEditor::ConfigPage *KateProjectPlugin::configPage(int number, QWidget *parent) { if (number != 0) { return nullptr; } return new KateProjectConfigPage(parent, this); } KateProject *KateProjectPlugin::createProjectForFileName(const QString &fileName) { KateProject *project = new KateProject(m_weaver, this); if (!project->loadFromFile(fileName)) { delete project; return nullptr; } m_projects.append(project); m_fileWatcher.addPath(QFileInfo(fileName).canonicalPath()); emit projectCreated(project); return project; } KateProject *KateProjectPlugin::projectForDir(QDir dir) { /** * search project file upwards * with recursion guard * do this first for all level and only after this fails try to invent projects * otherwise one e.g. invents projects for .kateproject tree structures with sub .git clones */ QSet seenDirectories; std::vector directoryStack; while (!seenDirectories.contains(dir.absolutePath())) { // update guard seenDirectories.insert(dir.absolutePath()); // remember directory for later project creation based on other criteria directoryStack.push_back(dir.absolutePath()); // check for project and load it if found const QString canonicalPath = dir.canonicalPath(); const QString canonicalFileName = dir.filePath(ProjectFileName); for (KateProject *project : m_projects) { if (project->baseDir() == canonicalPath || project->fileName() == canonicalFileName) { return project; } } // project file found => done if (dir.exists(ProjectFileName)) { return createProjectForFileName(canonicalFileName); } // else: cd up, if possible or abort if (!dir.cdUp()) { break; } } /** * if we arrive here, we found no .kateproject * => we want to invent a project based on e.g. version control system info */ for (const QString &dir : directoryStack) { // try to invent project based on version control stuff KateProject *project = nullptr; if ((project = detectGit(dir)) || (project = detectSubversion(dir)) || (project = detectMercurial(dir))) { return project; } } // no project found, bad luck return nullptr; } KateProject *KateProjectPlugin::projectForUrl(const QUrl &url) { if (url.isEmpty() || !url.isLocalFile()) { return nullptr; } return projectForDir(QFileInfo(url.toLocalFile()).absoluteDir()); } void KateProjectPlugin::slotDocumentCreated(KTextEditor::Document *document) { connect(document, &KTextEditor::Document::documentUrlChanged, this, &KateProjectPlugin::slotDocumentUrlChanged); connect(document, &KTextEditor::Document::destroyed, this, &KateProjectPlugin::slotDocumentDestroyed); slotDocumentUrlChanged(document); } void KateProjectPlugin::slotDocumentDestroyed(QObject *document) { if (KateProject *project = m_document2Project.value(document)) { project->unregisterDocument(static_cast(document)); } m_document2Project.remove(document); } void KateProjectPlugin::slotDocumentUrlChanged(KTextEditor::Document *document) { KateProject *project = projectForUrl(document->url()); if (KateProject *project = m_document2Project.value(document)) { project->unregisterDocument(document); } if (!project) { m_document2Project.remove(document); } else { m_document2Project[document] = project; } if (KateProject *project = m_document2Project.value(document)) { project->registerDocument(document); } } void KateProjectPlugin::slotDirectoryChanged(const QString &path) { QString fileName = QDir(path).filePath(ProjectFileName); for (KateProject *project : m_projects) { if (project->fileName() == fileName) { QDateTime lastModified = QFileInfo(fileName).lastModified(); if (project->fileLastModified().isNull() || (lastModified > project->fileLastModified())) { project->reload(); } break; } } } KateProject *KateProjectPlugin::detectGit(const QDir &dir) { // allow .git as dir and file (file for git worktree stuff, https://git-scm.com/docs/git-worktree) if (m_autoGit && dir.exists(GitFolderName)) { return createProjectForRepository(QStringLiteral("git"), dir); } return nullptr; } KateProject *KateProjectPlugin::detectSubversion(const QDir &dir) { if (m_autoSubversion && dir.exists(SubversionFolderName) && QFileInfo(dir, SubversionFolderName).isDir()) { return createProjectForRepository(QStringLiteral("svn"), dir); } return nullptr; } KateProject *KateProjectPlugin::detectMercurial(const QDir &dir) { if (m_autoMercurial && dir.exists(MercurialFolderName) && QFileInfo(dir, MercurialFolderName).isDir()) { return createProjectForRepository(QStringLiteral("hg"), dir); } return nullptr; } KateProject *KateProjectPlugin::createProjectForRepository(const QString &type, const QDir &dir) { QVariantMap cnf, files; files[type] = 1; cnf[QStringLiteral("name")] = dir.dirName(); cnf[QStringLiteral("files")] = (QVariantList() << files); KateProject *project = new KateProject(m_weaver, this); project->loadFromData(cnf, dir.canonicalPath()); m_projects.append(project); emit projectCreated(project); return project; } void KateProjectPlugin::setAutoRepository(bool onGit, bool onSubversion, bool onMercurial) { m_autoGit = onGit; m_autoSubversion = onSubversion; m_autoMercurial = onMercurial; writeConfig(); } bool KateProjectPlugin::autoGit() const { return m_autoGit; } bool KateProjectPlugin::autoSubversion() const { return m_autoSubversion; } bool KateProjectPlugin::autoMercurial() const { return m_autoMercurial; } void KateProjectPlugin::setIndex(bool enabled, const QUrl &directory) { m_indexEnabled = enabled; m_indexDirectory = directory; writeConfig(); } bool KateProjectPlugin::getIndexEnabled() const { return m_indexEnabled; } QUrl KateProjectPlugin::getIndexDirectory() const { return m_indexDirectory; } bool KateProjectPlugin::multiProjectCompletion() const { return m_multiProjectCompletion; } bool KateProjectPlugin::multiProjectGoto() const { return m_multiProjectGoto; } void KateProjectPlugin::setMultiProject(bool completion, bool gotoSymbol) { m_multiProjectCompletion = completion; m_multiProjectGoto = gotoSymbol; writeConfig(); } void KateProjectPlugin::readConfig() { KConfigGroup config(KSharedConfig::openConfig(), "project"); QStringList autorepository = config.readEntry("autorepository", DefaultConfig); m_autoGit = m_autoSubversion = m_autoMercurial = false; if (autorepository.contains(GitConfig)) { m_autoGit = true; } if (autorepository.contains(SubversionConfig)) { m_autoSubversion = true; } if (autorepository.contains(MercurialConfig)) { m_autoMercurial = true; } m_indexEnabled = config.readEntry("index", false); m_indexDirectory = config.readEntry("indexDirectory", QUrl()); m_multiProjectCompletion = config.readEntry("multiProjectCompletion", false); m_multiProjectGoto = config.readEntry("multiProjectCompletion", false); emit configUpdated(); } void KateProjectPlugin::writeConfig() { KConfigGroup config(KSharedConfig::openConfig(), "project"); QStringList repos; if (m_autoGit) { repos << GitConfig; } if (m_autoSubversion) { repos << SubversionConfig; } if (m_autoMercurial) { repos << MercurialConfig; } config.writeEntry("autorepository", repos); config.writeEntry("index", m_indexEnabled); config.writeEntry("indexDirectory", m_indexDirectory); config.writeEntry("multiProjectCompletion", m_multiProjectCompletion); config.writeEntry("multiProjectGoto", m_multiProjectGoto); emit configUpdated(); } #if KTEXTEDITOR_VERSION >= QT_VERSION_CHECK(5, 63, 0) static KateProjectPlugin *findProjectPlugin() { auto plugin = KTextEditor::Editor::instance()->application()->plugin(QStringLiteral("kateprojectplugin")); return qobject_cast(plugin); } #endif void KateProjectPlugin::registerVariables() { #if KTEXTEDITOR_VERSION >= QT_VERSION_CHECK(5, 63, 0) auto editor = KTextEditor::Editor::instance(); editor->registerVariableMatch(QStringLiteral("Project:Path"), i18n("Full path to current project excluding the file name."), [](const QStringView &, KTextEditor::View *view) { if (!view) { return QString(); } auto projectPlugin = findProjectPlugin(); if (!projectPlugin) { return QString(); } auto kateProject = findProjectPlugin()->projectForUrl(view->document()->url()); if (!kateProject) { return QString(); } return QDir(kateProject->baseDir()).absolutePath(); }); editor->registerVariableMatch(QStringLiteral("Project:NativePath"), i18n("Full path to current project excluding the file name, with native path separator (backslash on Windows)."), [](const QStringView &, KTextEditor::View *view) { if (!view) { return QString(); } auto projectPlugin = findProjectPlugin(); if (!projectPlugin) { return QString(); } auto kateProject = findProjectPlugin()->projectForUrl(view->document()->url()); if (!kateProject) { return QString(); } return QDir::toNativeSeparators(QDir(kateProject->baseDir()).absolutePath()); }); #endif } void KateProjectPlugin::unregisterVariables() { #if KTEXTEDITOR_VERSION >= QT_VERSION_CHECK(5, 63, 0) auto editor = KTextEditor::Editor::instance(); editor->unregisterVariableMatch(QStringLiteral("Project:Path")); editor->unregisterVariableMatch(QStringLiteral("Project:NativePath")); #endif } diff --git a/addons/project/kateprojectview.cpp b/addons/project/kateprojectview.cpp index 59034be9e..d94a4f9a6 100644 --- a/addons/project/kateprojectview.cpp +++ b/addons/project/kateprojectview.cpp @@ -1,89 +1,88 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectview.h" #include "kateprojectpluginview.h" #include #include #include #include #include #include #include KateProjectView::KateProjectView(KateProjectPluginView *pluginView, KateProject *project) - : QWidget() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_project(project) , m_treeView(new KateProjectViewTree(pluginView, project)) , m_filter(new KLineEdit()) { /** * layout tree view and co. */ QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_treeView); layout->addWidget(m_filter); setLayout(layout); // let tree get focus for keyboard selection of file to open setFocusProxy(m_treeView); /** * setup filter line edit */ m_filter->setPlaceholderText(i18n("Filter...")); m_filter->setClearButtonEnabled(true); connect(m_filter, &KLineEdit::textChanged, this, &KateProjectView::filterTextChanged); } KateProjectView::~KateProjectView() { } void KateProjectView::selectFile(const QString &file) { m_treeView->selectFile(file); } void KateProjectView::openSelectedDocument() { m_treeView->openSelectedDocument(); } void KateProjectView::filterTextChanged(const QString &filterText) { /** * filter */ static_cast(m_treeView->model())->setFilterFixedString(filterText); /** * expand */ if (!filterText.isEmpty()) { QTimer::singleShot(100, m_treeView, &QTreeView::expandAll); } } diff --git a/addons/project/kateprojectviewtree.cpp b/addons/project/kateprojectviewtree.cpp index 2eb81f005..b53ce8bdc 100644 --- a/addons/project/kateprojectviewtree.cpp +++ b/addons/project/kateprojectviewtree.cpp @@ -1,159 +1,158 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectviewtree.h" #include "kateprojectpluginview.h" #include "kateprojecttreeviewcontextmenu.h" #include #include #include #include KateProjectViewTree::KateProjectViewTree(KateProjectPluginView *pluginView, KateProject *project) - : QTreeView() - , m_pluginView(pluginView) + : m_pluginView(pluginView) , m_project(project) { /** * default style */ setHeaderHidden(true); setEditTriggers(QAbstractItemView::NoEditTriggers); setAllColumnsShowFocus(true); setFocusPolicy(Qt::NoFocus); /** * attach view => project * do this once, model is stable for whole project life time * kill selection model * create sort proxy model */ QItemSelectionModel *m = selectionModel(); QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(this); // sortModel->setFilterRole(SortFilterRole); // sortModel->setSortRole(SortFilterRole); sortModel->setRecursiveFilteringEnabled(true); sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); sortModel->setSourceModel(m_project->model()); setModel(sortModel); delete m; /** * connect needed signals * we use activated + clicked as we want "always" single click activation + keyboard focus / enter working */ connect(this, &KateProjectViewTree::activated, this, &KateProjectViewTree::slotClicked); connect(this, &KateProjectViewTree::clicked, this, &KateProjectViewTree::slotClicked); connect(m_project, &KateProject::modelChanged, this, &KateProjectViewTree::slotModelChanged); /** * trigger once some slots */ slotModelChanged(); } KateProjectViewTree::~KateProjectViewTree() { } void KateProjectViewTree::selectFile(const QString &file) { /** * get item if any */ QStandardItem *item = m_project->itemForFile(file); if (!item) { return; } /** * select it */ QModelIndex index = static_cast(model())->mapFromSource(m_project->model()->indexFromItem(item)); scrollTo(index, QAbstractItemView::EnsureVisible); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear | QItemSelectionModel::Select); } void KateProjectViewTree::openSelectedDocument() { /** * anything selected? */ QModelIndexList selecteStuff = selectedIndexes(); if (selecteStuff.isEmpty()) { return; } /** * open document for first element, if possible */ QString filePath = selecteStuff[0].data(Qt::UserRole).toString(); if (!filePath.isEmpty()) { m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath)); } } void KateProjectViewTree::slotClicked(const QModelIndex &index) { /** * open document, if any usable user data */ QString filePath = index.data(Qt::UserRole).toString(); if (!filePath.isEmpty()) { m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath)); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear | QItemSelectionModel::Select); } } void KateProjectViewTree::slotModelChanged() { /** * model was updated * perhaps we need to highlight again new file */ KTextEditor::View *activeView = m_pluginView->mainWindow()->activeView(); if (activeView && activeView->document()->url().isLocalFile()) { selectFile(activeView->document()->url().toLocalFile()); } } void KateProjectViewTree::contextMenuEvent(QContextMenuEvent *event) { /** * get path file path or don't do anything */ QModelIndex index = selectionModel()->currentIndex(); QString filePath = index.data(Qt::UserRole).toString(); if (filePath.isEmpty()) { QTreeView::contextMenuEvent(event); return; } KateProjectTreeViewContextMenu menu; menu.exec(filePath, viewport()->mapToGlobal(event->pos()), this); event->accept(); } diff --git a/addons/project/kateprojectworker.cpp b/addons/project/kateprojectworker.cpp index e7d6d7392..c9a8466c1 100644 --- a/addons/project/kateprojectworker.cpp +++ b/addons/project/kateprojectworker.cpp @@ -1,493 +1,491 @@ /* This file is part of the Kate project. * * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateprojectworker.h" #include "kateproject.h" #include #include #include #include #include #include #include #include #include KateProjectWorker::KateProjectWorker(const QString &baseDir, const QString &indexDir, const QVariantMap &projectMap, bool force) - : QObject() - , ThreadWeaver::Job() - , m_baseDir(baseDir) + : m_baseDir(baseDir) , m_indexDir(indexDir) , m_projectMap(projectMap) , m_force(force) { Q_ASSERT(!m_baseDir.isEmpty()); } void KateProjectWorker::run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *) { /** * Create dummy top level parent item and empty map inside shared pointers * then load the project recursively */ KateProjectSharedQStandardItem topLevel(new QStandardItem()); KateProjectSharedQMapStringItem file2Item(new QMap()); loadProject(topLevel.data(), m_projectMap, file2Item.data()); /** * create some local backup of some data we need for further processing! */ QStringList files = file2Item->keys(); emit loadDone(topLevel, file2Item); // trigger index loading, will internally handle enable/disabled loadIndex(files, m_force); } void KateProjectWorker::loadProject(QStandardItem *parent, const QVariantMap &project, QMap *file2Item) { /** * recurse to sub-projects FIRST */ QVariantList subGroups = project[QStringLiteral("projects")].toList(); for (const QVariant &subGroupVariant : subGroups) { /** * convert to map and get name, else skip */ QVariantMap subProject = subGroupVariant.toMap(); const QString keyName = QStringLiteral("name"); if (subProject[keyName].toString().isEmpty()) { continue; } /** * recurse */ QStandardItem *subProjectItem = new KateProjectItem(KateProjectItem::Project, subProject[keyName].toString()); loadProject(subProjectItem, subProject, file2Item); parent->appendRow(subProjectItem); } /** * load all specified files */ const QString keyFiles = QStringLiteral("files"); QVariantList files = project[keyFiles].toList(); for (const QVariant &fileVariant : files) { loadFilesEntry(parent, fileVariant.toMap(), file2Item); } } /** * small helper to construct directory parent items * @param dir2Item map for path => item * @param path current path we need item for * @return correct parent item for given path, will reuse existing ones */ static QStandardItem *directoryParent(QMap &dir2Item, QString path) { /** * throw away simple / */ if (path == QLatin1String("/")) { path = QString(); } /** * quick check: dir already seen? */ if (dir2Item.contains(path)) { return dir2Item[path]; } /** * else: construct recursively */ int slashIndex = path.lastIndexOf(QLatin1Char('/')); /** * no slash? * simple, no recursion, append new item toplevel */ if (slashIndex < 0) { dir2Item[path] = new KateProjectItem(KateProjectItem::Directory, path); dir2Item[QString()]->appendRow(dir2Item[path]); return dir2Item[path]; } /** * else, split and recurse */ const QString leftPart = path.left(slashIndex); const QString rightPart = path.right(path.size() - (slashIndex + 1)); /** * special handling if / with nothing on one side are found */ if (leftPart.isEmpty() || rightPart.isEmpty()) { return directoryParent(dir2Item, leftPart.isEmpty() ? rightPart : leftPart); } /** * else: recurse on left side */ dir2Item[path] = new KateProjectItem(KateProjectItem::Directory, rightPart); directoryParent(dir2Item, leftPart)->appendRow(dir2Item[path]); return dir2Item[path]; } void KateProjectWorker::loadFilesEntry(QStandardItem *parent, const QVariantMap &filesEntry, QMap *file2Item) { QDir dir(m_baseDir); if (!dir.cd(filesEntry[QStringLiteral("directory")].toString())) { return; } QStringList files = findFiles(dir, filesEntry); if (files.isEmpty()) { return; } files.sort(Qt::CaseInsensitive); /** * construct paths first in tree and items in a map */ QMap dir2Item; dir2Item[QString()] = parent; QList> item2ParentPath; for (const QString &filePath : files) { /** * skip dupes */ if (file2Item->contains(filePath)) { continue; } /** * get file info and skip NON-files */ QFileInfo fileInfo(filePath); if (!fileInfo.isFile()) { continue; } /** * construct the item with right directory prefix * already hang in directories in tree */ KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::File, fileInfo.fileName()); fileItem->setData(filePath, Qt::ToolTipRole); // get the directory's relative path to the base directory QString dirRelPath = dir.relativeFilePath(fileInfo.absolutePath()); // if the relative path is ".", clean it up if (dirRelPath == QLatin1Char('.')) { dirRelPath = QString(); } item2ParentPath.append(QPair(fileItem, directoryParent(dir2Item, dirRelPath))); fileItem->setData(filePath, Qt::UserRole); (*file2Item)[filePath] = fileItem; } /** * plug in the file items to the tree */ auto i = item2ParentPath.constBegin(); while (i != item2ParentPath.constEnd()) { i->second->appendRow(i->first); ++i; } } QStringList KateProjectWorker::findFiles(const QDir &dir, const QVariantMap &filesEntry) { const bool recursive = !filesEntry.contains(QLatin1String("recursive")) || filesEntry[QStringLiteral("recursive")].toBool(); if (filesEntry[QStringLiteral("git")].toBool()) { return filesFromGit(dir, recursive); } else if (filesEntry[QStringLiteral("hg")].toBool()) { return filesFromMercurial(dir, recursive); } else if (filesEntry[QStringLiteral("svn")].toBool()) { return filesFromSubversion(dir, recursive); } else if (filesEntry[QStringLiteral("darcs")].toBool()) { return filesFromDarcs(dir, recursive); } else { QStringList files = filesEntry[QStringLiteral("list")].toStringList(); if (files.empty()) { QStringList filters = filesEntry[QStringLiteral("filters")].toStringList(); files = filesFromDirectory(dir, recursive, filters); } return files; } } QStringList KateProjectWorker::filesFromGit(const QDir &dir, bool recursive) { /** * query files via ls-files and make them absolute afterwards */ const QStringList relFiles = gitLsFiles(dir); QStringList files; for (const QString &relFile : relFiles) { if (!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) { continue; } files.append(dir.absolutePath() + QLatin1Char('/') + relFile); } return files; } QStringList KateProjectWorker::gitLsFiles(const QDir &dir) { /** * git ls-files -z results a bytearray where each entry is \0-terminated. * NOTE: Without -z, Umlauts such as "Der Bäcker/Das Brötchen.txt" do not work (#389415) * * use --recurse-submodules, there since git 2.11 (released 2016) * our own submodules handling code leads to file duplicates */ QStringList args; args << QStringLiteral("ls-files") << QStringLiteral("-z") << QStringLiteral("--recurse-submodules") << QStringLiteral("."); QProcess git; git.setWorkingDirectory(dir.absolutePath()); git.start(QStringLiteral("git"), args); QStringList files; if (!git.waitForStarted() || !git.waitForFinished(-1)) { return files; } const QList byteArrayList = git.readAllStandardOutput().split('\0'); for (const QByteArray &byteArray : byteArrayList) { files << QString::fromUtf8(byteArray); } return files; } QStringList KateProjectWorker::filesFromMercurial(const QDir &dir, bool recursive) { QStringList files; QProcess hg; hg.setWorkingDirectory(dir.absolutePath()); QStringList args; args << QStringLiteral("manifest") << QStringLiteral("."); hg.start(QStringLiteral("hg"), args); if (!hg.waitForStarted() || !hg.waitForFinished(-1)) { return files; } const QStringList relFiles = QString::fromLocal8Bit(hg.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts); for (const QString &relFile : relFiles) { if (!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) { continue; } files.append(dir.absolutePath() + QLatin1Char('/') + relFile); } return files; } QStringList KateProjectWorker::filesFromSubversion(const QDir &dir, bool recursive) { QStringList files; QProcess svn; svn.setWorkingDirectory(dir.absolutePath()); QStringList args; args << QStringLiteral("status") << QStringLiteral("--verbose") << QStringLiteral("."); if (recursive) { args << QStringLiteral("--depth=infinity"); } else { args << QStringLiteral("--depth=files"); } svn.start(QStringLiteral("svn"), args); if (!svn.waitForStarted() || !svn.waitForFinished(-1)) { return files; } /** * get output and split up into lines */ const QStringList lines = QString::fromLocal8Bit(svn.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts); /** * remove start of line that is no filename, sort out unknown and ignore */ bool first = true; int prefixLength = -1; for (const QString &line : lines) { /** * get length of stuff to cut */ if (first) { /** * try to find ., else fail */ prefixLength = line.lastIndexOf(QLatin1Char('.')); if (prefixLength < 0) { break; } /** * skip first */ first = false; continue; } /** * get file, if not unknown or ignored * prepend directory path */ if ((line.size() > prefixLength) && line[0] != QLatin1Char('?') && line[0] != QLatin1Char('I')) { files.append(dir.absolutePath() + QLatin1Char('/') + line.right(line.size() - prefixLength)); } } return files; } QStringList KateProjectWorker::filesFromDarcs(const QDir &dir, bool recursive) { QStringList files; const QString cmd = QStringLiteral("darcs"); QString root; { QProcess darcs; darcs.setWorkingDirectory(dir.absolutePath()); QStringList args; args << QStringLiteral("list") << QStringLiteral("repo"); darcs.start(cmd, args); if (!darcs.waitForStarted() || !darcs.waitForFinished(-1)) return files; auto str = QString::fromLocal8Bit(darcs.readAllStandardOutput()); QRegularExpression exp(QStringLiteral("Root: ([^\\n\\r]*)")); auto match = exp.match(str); if (!match.hasMatch()) return files; root = match.captured(1); } QStringList relFiles; { QProcess darcs; QStringList args; darcs.setWorkingDirectory(dir.absolutePath()); args << QStringLiteral("list") << QStringLiteral("files") << QStringLiteral("--no-directories") << QStringLiteral("--pending"); darcs.start(cmd, args); if (!darcs.waitForStarted() || !darcs.waitForFinished(-1)) return files; relFiles = QString::fromLocal8Bit(darcs.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts); } for (const QString &relFile : relFiles) { const QString path = dir.relativeFilePath(root + QLatin1String("/") + relFile); if ((!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) || (recursive && (relFile.indexOf(QLatin1String("..")) == 0))) { continue; } files.append(dir.absoluteFilePath(path)); } return files; } QStringList KateProjectWorker::filesFromDirectory(const QDir &_dir, bool recursive, const QStringList &filters) { QStringList files; QDir dir(_dir); dir.setFilter(QDir::Files); if (!filters.isEmpty()) { dir.setNameFilters(filters); } /** * construct flags for iterator */ QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags; if (recursive) { flags = flags | QDirIterator::Subdirectories; } /** * create iterator and collect all files */ QDirIterator dirIterator(dir, flags); while (dirIterator.hasNext()) { dirIterator.next(); files.append(dirIterator.filePath()); } return files; } void KateProjectWorker::loadIndex(const QStringList &files, bool force) { const QString keyCtags = QStringLiteral("ctags"); const QVariantMap ctagsMap = m_projectMap[keyCtags].toMap(); /** * load index, if enabled * before this was default on, which is dangerous for large repositories, e.g. out-of-memory or out-of-disk * if specified in project map; use that setting, otherwise fall back to global setting */ bool indexEnabled = !m_indexDir.isEmpty(); auto indexValue = ctagsMap[QStringLiteral("enable")]; if (!indexValue.isNull()) { indexEnabled = indexValue.toBool(); } if (!indexEnabled) { emit loadIndexDone(KateProjectSharedProjectIndex()); return; } /** * create new index, this will do the loading in the constructor * wrap it into shared pointer for transfer to main thread */ KateProjectSharedProjectIndex index(new KateProjectIndex(m_baseDir, m_indexDir, files, ctagsMap, force)); emit loadIndexDone(index); } diff --git a/addons/project/kateprojectworker.h b/addons/project/kateprojectworker.h index fb39e39c8..a43bebfe6 100644 --- a/addons/project/kateprojectworker.h +++ b/addons/project/kateprojectworker.h @@ -1,110 +1,110 @@ /* This file is part of the Kate project. * * Copyright (C) 2010 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATE_PROJECT_WORKER_H #define KATE_PROJECT_WORKER_H #include "kateproject.h" #include "kateprojectitem.h" #include #include #include class QDir; /** * Class representing a project background worker. * This worker will build up the model for the project on load and do other stuff in the background. */ class KateProjectWorker : public QObject, public ThreadWeaver::Job { Q_OBJECT public: /** * Type for QueuedConnection */ typedef QMap MapString2Item; explicit KateProjectWorker(const QString &baseDir, const QString &indexDir, const QVariantMap &projectMap, bool force); void run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) override; Q_SIGNALS: void loadDone(KateProjectSharedQStandardItem topLevel, KateProjectSharedQMapStringItem file2Item); void loadIndexDone(KateProjectSharedProjectIndex index); private: /** * Load one project inside the project tree. * Fill data from JSON storage to model and recurse to sub-projects. * @param parent parent standard item in the model * @param project variant map for this group * @param file2Item mapping file => item, will be filled */ void loadProject(QStandardItem *parent, const QVariantMap &project, QMap *file2Item); /** * Load one files entry in the current parent item. * @param parent parent standard item in the model * @param filesEntry one files entry specification to load * @param file2Item mapping file => item, will be filled */ void loadFilesEntry(QStandardItem *parent, const QVariantMap &filesEntry, QMap *file2Item); /** * Load index for whole project. * @param files list of all project files to index */ void loadIndex(const QStringList &files, bool force); QStringList findFiles(const QDir &dir, const QVariantMap &filesEntry); QStringList filesFromGit(const QDir &dir, bool recursive); QStringList filesFromMercurial(const QDir &dir, bool recursive); QStringList filesFromSubversion(const QDir &dir, bool recursive); QStringList filesFromDarcs(const QDir &dir, bool recursive); QStringList filesFromDirectory(const QDir &dir, bool recursive, const QStringList &filters); QStringList gitLsFiles(const QDir &dir); private: /** * our project, only as QObject, we only send messages back and forth! */ - QObject *m_project; + QObject *m_project = nullptr; /** * project base directory name */ const QString m_baseDir; /** * index directory */ const QString m_indexDir; const QVariantMap m_projectMap; const bool m_force; }; #endif diff --git a/addons/replicode/replicodesettings.h b/addons/replicode/replicodesettings.h index cf3cc4df3..746fbee82 100644 --- a/addons/replicode/replicodesettings.h +++ b/addons/replicode/replicodesettings.h @@ -1,96 +1,96 @@ /* This file is part of the KDE project Copyright (C) 2014 Martin Sandsmark This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REPLICODESETTINGS_H #define REPLICODESETTINGS_H #include class QIODevice; class ReplicodeSettings : public QObject { Q_OBJECT public: explicit ReplicodeSettings(QObject *parent = nullptr); void load(); void save(); void setDefaults(); /////// // Load QString userOperatorPath; QString userClassPath; QString sourcePath; /////// // Init - int basePeriod; - int reductionCoreCount; - int timeCoreCount; + int basePeriod = 0; + int reductionCoreCount = 0; + int timeCoreCount = 0; ///////// // System - int perfSamplingPeriod; - float floatTolerance; - int timeTolerance; - int primaryTimeHorizon; - int secondaryTimeHorizon; + int perfSamplingPeriod = 0; + float floatTolerance = 0; + int timeTolerance = 0; + int primaryTimeHorizon = 0; + int secondaryTimeHorizon = 0; // Model - float mdlInertiaSuccessRateThreshold; - int mdlInertiaCountThreshold; + float mdlInertiaSuccessRateThreshold = 0; + int mdlInertiaCountThreshold = 0; // Targeted Pattern Extractor - float tpxDeltaSuccessRateThreshold; - int tpxTimehorizon; + float tpxDeltaSuccessRateThreshold = 0; + int tpxTimehorizon{}; // Simulation - int minimumSimulationTimeHorizon; - int maximumSimulationTimeHorizon; - float simulationTimeHorizon; + int minimumSimulationTimeHorizon = 0; + int maximumSimulationTimeHorizon = 0; + float simulationTimeHorizon = 0; //////// // Debug - bool debug; - int notificationMarkerResilience; - int goalPredictionSuccessResilience; - int debugWindows; - int traceLevels; - - bool getObjects; - bool decompileObjects; + bool debug = false; + int notificationMarkerResilience = 0; + int goalPredictionSuccessResilience = 0; + int debugWindows = 0; + int traceLevels = 0; + + bool getObjects = false; + bool decompileObjects = false; QString decompilationFilePath; - bool ignoreNamedObjects; + bool ignoreNamedObjects = false; QString objectsPath; - bool testObjects; + bool testObjects = false; ////// // Run - int runTime; - int probeLevel; + int runTime = 0; + int probeLevel = 0; - bool getModels; - bool decompileModels; - bool ignoreNamedModels; + bool getModels = false; + bool decompileModels = false; + bool ignoreNamedModels = false; QString modelsPath; - bool testModels; + bool testModels = false; }; #endif // REPLICODESETTINGS_H diff --git a/addons/replicode/replicodeview.h b/addons/replicode/replicodeview.h index 2ee19e151..f7202123d 100644 --- a/addons/replicode/replicodeview.h +++ b/addons/replicode/replicodeview.h @@ -1,76 +1,76 @@ /* This file is part of the KDE project Copyright (C) 2014 Martin Sandsmark This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REPLICODEVIEW_H #define REPLICODEVIEW_H #include #include #include #include class QListWidgetItem; class QListWidget; class QTemporaryFile; class QProcess; class QListWidget; class QPushButton; class QAction; class ReplicodeConfig; class ReplicodeView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) public: explicit ReplicodeView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainWindow); ~ReplicodeView() override; void readSessionConfig(const KConfigGroup &) override { } void writeSessionConfig(KConfigGroup &) override { } private Q_SLOTS: void runReplicode(); void stopReplicode(); void replicodeFinished(); void gotStderr(); void gotStdout(); void runErrored(QProcess::ProcessError); void outputClicked(QListWidgetItem *item); void viewChanged(); private: KTextEditor::MainWindow *m_mainWindow; QProcess *m_executor; QListWidget *m_replicodeOutput; QWidget *m_toolview; QWidget *m_configSidebar; QPushButton *m_runButton; QPushButton *m_stopButton; QAction *m_runAction; QAction *m_stopAction; ReplicodeConfig *m_configView; - bool m_completed; + bool m_completed = false; }; #endif diff --git a/addons/search/FolderFilesList.h b/addons/search/FolderFilesList.h index 09e8be3e3..fbf9fdb9d 100644 --- a/addons/search/FolderFilesList.h +++ b/addons/search/FolderFilesList.h @@ -1,68 +1,68 @@ /* Kate search plugin * * Copyright (C) 2013 by Kåre Särs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file called COPYING; if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifndef FolderFilesList_h #define FolderFilesList_h #include #include #include #include #include #include class FolderFilesList : public QThread { Q_OBJECT public: FolderFilesList(QObject *parent = nullptr); ~FolderFilesList() override; void run() override; void generateList(const QString &folder, bool recursive, bool hidden, bool symlinks, bool binary, const QString &types, const QString &excludes); QStringList fileList(); public Q_SLOTS: void cancelSearch(); Q_SIGNALS: void searching(const QString &path); private: void checkNextItem(const QFileInfo &item); private: QString m_folder; QStringList m_files; - bool m_cancelSearch; + bool m_cancelSearch = false; - bool m_recursive; - bool m_hidden; - bool m_symlinks; - bool m_binary; + bool m_recursive = false; + bool m_hidden = false; + bool m_symlinks = false; + bool m_binary = false; QStringList m_types; QVector m_excludeList; QElapsedTimer m_time; }; #endif diff --git a/addons/search/plugin_search.h b/addons/search/plugin_search.h index fc9c931ed..e44a9be6a 100644 --- a/addons/search/plugin_search.h +++ b/addons/search/plugin_search.h @@ -1,238 +1,238 @@ /* Kate search plugin * * Copyright (C) 2011-2013 by Kåre Särs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file called COPYING; if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifndef _PLUGIN_SEARCH_H_ #define _PLUGIN_SEARCH_H_ #include #include #include #include #include #include #include #include #include #include #include "ui_results.h" #include "ui_search.h" #include "FolderFilesList.h" #include "SearchDiskFiles.h" #include "replace_matches.h" #include "search_open_files.h" class KateSearchCommand; namespace KTextEditor { class MovingRange; } class Results : public QWidget, public Ui::Results { Q_OBJECT public: Results(QWidget *parent = nullptr); int matches = 0; QRegularExpression regExp; bool useRegExp = false; - bool matchCase; + bool matchCase = false; QString replaceStr; int searchPlaceIndex = 0; QString treeRootText; }; // This class keeps the focus inside the S&R plugin when pressing tab/shift+tab by overriding focusNextPrevChild() class ContainerWidget : public QWidget { Q_OBJECT public: ContainerWidget(QWidget *parent) : QWidget(parent) { } Q_SIGNALS: void nextFocus(QWidget *currentWidget, bool *found, bool next); protected: bool focusNextPrevChild(bool next) override; }; class KatePluginSearch : public KTextEditor::Plugin { Q_OBJECT public: explicit KatePluginSearch(QObject *parent = nullptr, const QList & = QList()); ~KatePluginSearch() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; private: KateSearchCommand *m_searchCommand = nullptr; }; class KatePluginSearchView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) public: enum SearchPlaces { CurrentFile, OpenFiles, Folder, Project, AllProjects }; KatePluginSearchView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainWindow, KTextEditor::Application *application); ~KatePluginSearchView() override; void readSessionConfig(const KConfigGroup &config) override; void writeSessionConfig(KConfigGroup &config) override; public Q_SLOTS: void startSearch(); void setSearchString(const QString &pattern); void navigateFolderUp(); void setCurrentFolder(); void setSearchPlace(int place); void goToNextMatch(); void goToPreviousMatch(); private Q_SLOTS: void openSearchView(); void handleEsc(QEvent *e); void nextFocus(QWidget *currentWidget, bool *found, bool next); void addTab(); void tabCloseRequested(int index); void toggleOptions(bool show); void searchContextMenu(const QPoint &pos); void replaceContextMenu(const QPoint &pos); void searchPlaceChanged(); void startSearchWhileTyping(); void folderFileListChanged(); void matchFound(const QString &url, const QString &fileName, const QString &lineContent, int matchLen, int startLine, int startColumn, int endLine, int endColumn); void addMatchMark(KTextEditor::Document *doc, QTreeWidgetItem *item); void searchDone(); void searchWhileTypingDone(); void indicateMatch(bool hasMatch); void searching(const QString &file); void itemSelected(QTreeWidgetItem *item); void clearMarks(); void clearDocMarks(KTextEditor::Document *doc); void replaceSingleMatch(); void replaceChecked(); void replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile); void replaceDone(); void docViewChanged(); void resultTabChanged(int index); void expandResults(); void updateResultsRootItem(); /** * keep track if the project plugin is alive and if the project file did change */ void slotPluginViewCreated(const QString &name, QObject *pluginView); void slotPluginViewDeleted(const QString &name, QObject *pluginView); void slotProjectFileNameChanged(); protected: bool eventFilter(QObject *obj, QEvent *ev) override; void addHeaderItem(); private: QTreeWidgetItem *rootFileItem(const QString &url, const QString &fName); QStringList filterFiles(const QStringList &files) const; void onResize(const QSize &size); - Ui::SearchDialog m_ui; + Ui::SearchDialog m_ui{}; QWidget *m_toolView; KTextEditor::Application *m_kateApp; SearchOpenFiles m_searchOpenFiles; FolderFilesList m_folderFilesList; SearchDiskFiles m_searchDiskFiles; ReplaceMatches m_replacer; - QAction *m_matchCase; - QAction *m_useRegExp; + QAction *m_matchCase = nullptr; + QAction *m_useRegExp = nullptr; Results *m_curResults; bool m_searchJustOpened; int m_projectSearchPlaceIndex; bool m_searchDiskFilesDone; bool m_searchOpenFilesDone; bool m_isSearchAsYouType; bool m_isLeftRight; QString m_resultBaseDir; QList m_matchRanges; QTimer m_changeTimer; QTimer m_updateSumaryTimer; QPointer m_infoMessage; /** * current project plugin view, if any */ QObject *m_projectPluginView; /** * our main window */ KTextEditor::MainWindow *m_mainWindow; }; class KateSearchCommand : public KTextEditor::Command { Q_OBJECT public: KateSearchCommand(QObject *parent); Q_SIGNALS: void setSearchPlace(int place); void setCurrentFolder(); void setSearchString(const QString &pattern); void startSearch(); void newTab(); // // KTextEditor::Command // public: bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) override; bool help(KTextEditor::View *view, const QString &cmd, QString &msg) override; }; #endif // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/search/replace_matches.h b/addons/search/replace_matches.h index 00fae1b65..3a34c0bf3 100644 --- a/addons/search/replace_matches.h +++ b/addons/search/replace_matches.h @@ -1,88 +1,88 @@ /* Kate search plugin * * Copyright (C) 2011 by Kåre Särs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file called COPYING; if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifndef _REPLACE_MATCHES_H_ #define _REPLACE_MATCHES_H_ #include #include #include #include #include #include #include #include class ReplaceMatches : public QObject { Q_OBJECT public: enum MatchData { FileUrlRole = Qt::UserRole, FileNameRole, StartLineRole, StartColumnRole, EndLineRole, EndColumnRole, MatchLenRole, PreMatchRole, MatchRole, PostMatchRole, ReplacedRole, ReplacedTextRole, }; ReplaceMatches(QObject *parent = nullptr); void setDocumentManager(KTextEditor::Application *manager); bool replaceMatch(KTextEditor::Document *doc, QTreeWidgetItem *item, const KTextEditor::Range &range, const QRegularExpression ®Exp, const QString &replaceTxt); bool replaceSingleMatch(KTextEditor::Document *doc, QTreeWidgetItem *item, const QRegularExpression ®Exp, const QString &replaceTxt); void replaceChecked(QTreeWidget *tree, const QRegularExpression ®exp, const QString &replace); KTextEditor::Document *findNamed(const QString &name); public Q_SLOTS: void cancelReplace(); private Q_SLOTS: void doReplaceNextMatch(); Q_SIGNALS: void replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile); void replaceDone(); private: void updateTreeViewItems(QTreeWidgetItem *fileItem); KTextEditor::Application *m_manager = nullptr; QTreeWidget *m_tree = nullptr; int m_rootIndex = -1; int m_childStartIndex = -1; QVector m_currentMatches; QVector m_currentReplaced; QRegularExpression m_regExp; QString m_replaceText; - bool m_cancelReplace; + bool m_cancelReplace = false; QElapsedTimer m_progressTime; }; #endif diff --git a/addons/sessionapplet/katesessionsservice.cpp b/addons/sessionapplet/katesessionsservice.cpp index abc6fb4a6..cf7b718d9 100644 --- a/addons/sessionapplet/katesessionsservice.cpp +++ b/addons/sessionapplet/katesessionsservice.cpp @@ -1,38 +1,37 @@ /******************************************************************** This file is part of the KDE project. Copyright (C) 2014 Joseph Wenninger based on clipboard engine: Copyright (C) 2014 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "katesessionsservice.h" #include "katesessionsengine.h" #include "katesessionsjob.h" #include KateSessionsService::KateSessionsService(KateSessionsEngine *engine, const QString &uuid) - : Plasma::Service() - , m_engine(engine) + : m_engine(engine) , m_uuid(uuid) { setName(QStringLiteral("org.kde.plasma.katesessions")); } Plasma::ServiceJob *KateSessionsService::createJob(const QString &operation, QVariantMap ¶meters) { qDebug() << "creating KateSessionsJob"; return new KateSessionsJob(m_engine, m_uuid, operation, parameters, this); } diff --git a/addons/symbolviewer/plugin_katesymbolviewer.h b/addons/symbolviewer/plugin_katesymbolviewer.h index bc2ea121b..36fde0eaf 100644 --- a/addons/symbolviewer/plugin_katesymbolviewer.h +++ b/addons/symbolviewer/plugin_katesymbolviewer.h @@ -1,176 +1,176 @@ /*************************************************************************** plugin_katesymbolviewer.h - description ------------------- begin : Apr 2 2003 author : 2003 Massimo Callegari email : massimocallegari@yahoo.it ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef _PLUGIN_KATE_SYMBOLVIEWER_H_ #define _PLUGIN_KATE_SYMBOLVIEWER_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * Plugin's config page */ class KatePluginSymbolViewerConfigPage : public KTextEditor::ConfigPage { Q_OBJECT friend class KatePluginSymbolViewer; public: explicit KatePluginSymbolViewerConfigPage(QObject *parent = nullptr, QWidget *parentWidget = nullptr); ~KatePluginSymbolViewerConfigPage() override; /** * Reimplemented from KTextEditor::ConfigPage * just emits configPageApplyRequest( this ). */ QString name() const override; QString fullName() const override; QIcon icon() const override; void apply() override; void reset() override { ; } void defaults() override { ; } Q_SIGNALS: /** * Ask the plugin to set initial values */ void configPageApplyRequest(KatePluginSymbolViewerConfigPage *); /** * Ask the plugin to apply changes */ void configPageInitRequest(KatePluginSymbolViewerConfigPage *); private: QCheckBox *viewReturns; QCheckBox *expandTree; QCheckBox *treeView; QCheckBox *sortSymbols; }; class KatePluginSymbolViewer; class KatePluginSymbolViewerView : public QObject, public KXMLGUIClient { Q_OBJECT friend class KatePluginSymbolViewer; public: KatePluginSymbolViewerView(KatePluginSymbolViewer *plugin, KTextEditor::MainWindow *mw); ~KatePluginSymbolViewerView() override; public Q_SLOTS: void displayOptionChanged(); void parseSymbols(); void slotDocChanged(); void goToSymbol(QTreeWidgetItem *); void slotShowContextMenu(const QPoint &); void cursorPositionChanged(); QTreeWidgetItem *newActveItem(int &currMinLine, int currLine, QTreeWidgetItem *item); void updateCurrTreeItem(); void slotDocEdited(); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private: KTextEditor::MainWindow *m_mainWindow; KatePluginSymbolViewer *m_plugin; QMenu *m_popup; QWidget *m_toolview; QTreeWidget *m_symbols; QAction *m_treeOn; // FIXME Rename other actions accordingly QAction *m_sort; // m_sortOn etc QAction *m_macro; QAction *m_struct; QAction *m_func; QAction *m_typesOn; QAction *m_expandOn; QTimer m_updateTimer; QTimer m_currItemTimer; - int m_oldCursorLine; + int m_oldCursorLine = 0; void updatePixmapScroll(); void parseCppSymbols(void); void parseTclSymbols(void); void parseFortranSymbols(void); void parsePerlSymbols(void); void parsePythonSymbols(void); void parseRubySymbols(void); void parseXsltSymbols(void); void parseXMLSymbols(void); void parsePhpSymbols(void); void parseBashSymbols(void); void parseEcmaSymbols(void); }; class KatePluginSymbolViewer : public KTextEditor::Plugin { friend class KatePluginSymbolViewerView; Q_OBJECT public: explicit KatePluginSymbolViewer(QObject *parent = nullptr, const QList & = QList()); ~KatePluginSymbolViewer() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; int configPages() const override { return 1; } KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; public Q_SLOTS: void applyConfig(KatePluginSymbolViewerConfigPage *p); private: QSet m_views; }; // icons #include "icons.xpm" #endif diff --git a/addons/tabswitcher/tabswitchertreeview.cpp b/addons/tabswitcher/tabswitchertreeview.cpp index f9d471c4f..1eebca2ab 100644 --- a/addons/tabswitcher/tabswitchertreeview.cpp +++ b/addons/tabswitcher/tabswitchertreeview.cpp @@ -1,77 +1,76 @@ /* This file is part of the KDE project Copyright (C) 2014 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tabswitchertreeview.h" #include "tabswitcher.h" #include #include -TabSwitcherTreeView::TabSwitcherTreeView() - : QTreeView() +TabSwitcherTreeView::TabSwitcherTreeView() { setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::SingleSelection); // setUniformItemSizes(true); setTextElideMode(Qt::ElideMiddle); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHeaderHidden(true); setRootIsDecorated(false); } int TabSwitcherTreeView::sizeHintWidth() const { return sizeHintForColumn(0) + sizeHintForColumn(1); } void TabSwitcherTreeView::resizeColumnsToContents() { resizeColumnToContents(0); resizeColumnToContents(1); } void TabSwitcherTreeView::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control) { emit itemActivated(selectionModel()->currentIndex()); event->accept(); hide(); } else { QTreeView::keyReleaseEvent(event); } } void TabSwitcherTreeView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { event->accept(); hide(); } else { QTreeView::keyPressEvent(event); } } void TabSwitcherTreeView::showEvent(QShowEvent *event) { resizeColumnsToContents(); QTreeView::showEvent(event); } diff --git a/addons/tabswitcher/tests/tstestapp.cpp b/addons/tabswitcher/tests/tstestapp.cpp index 0d75dd004..081cd23a7 100644 --- a/addons/tabswitcher/tests/tstestapp.cpp +++ b/addons/tabswitcher/tests/tstestapp.cpp @@ -1,148 +1,148 @@ /* This file is part of the KDE project * * Copyright (C) 2018 Gregor Mi * Copyright (C) 2019 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "tstestapp.h" #include "../tabswitcherfilesmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static KTextEditor::Document *addDoc(const QString &path) { auto doc = KTextEditor::Editor::instance()->createDocument(nullptr); doc->openUrl(QUrl::fromLocalFile(path)); return doc; } class TsTestApp::Impl { public: void insert_1_item() { model.insertDocument(0, addDoc(QStringLiteral("/home/user2/folder1/abc.d"))); treeview1->resizeColumnToContents(0); } void remove_1_item() { model.removeRow(0); treeview1->resizeColumnToContents(0); } void set_items_cutoff_bug() { model.clear(); auto icon = QIcon::fromTheme(QLatin1String("document-export")); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/logs/notifications/multimedia-system.log"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/dev/src/kservicemenueditor-0.2a/servicemenueditor"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/kde/src/kdesrc-build/kdesrc-build"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/node_modules/autolinker/README.md"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/node_modules/autolinker/package.json"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/node_modules/autolinker/LICENSE"))); model.insertDocument(model.rowCount(), addDoc(QStringLiteral("/home/gregor/node_modules/asynckit/package.json"))); treeview1->resizeColumnToContents(0); } public: detail::TabswitcherFilesModel model; - QTreeView *treeview1; + QTreeView *treeview1 = nullptr; }; TsTestApp::TsTestApp(QWidget *parent) : QMainWindow(parent) , impl_(new TsTestApp::Impl) { setGeometry(0, 0, 1024, 800); setCentralWidget(new QWidget(this)); auto l = new QVBoxLayout(); centralWidget()->setLayout(l); auto hl = new QHBoxLayout(); l->addLayout(hl); auto buttonInsert1 = new QPushButton(QStringLiteral("Ins 1 item"), this); connect(buttonInsert1, &QPushButton::clicked, this, [=] { impl_->insert_1_item(); }); hl->addWidget(buttonInsert1); auto buttonRemove1 = new QPushButton(QStringLiteral("Del 1 item"), this); connect(buttonRemove1, &QPushButton::clicked, this, [=] { impl_->remove_1_item(); }); hl->addWidget(buttonRemove1); auto buttonSetTestSet1 = new QPushButton(QStringLiteral("set_items_cutoff_bug"), this); connect(buttonSetTestSet1, &QPushButton::clicked, this, [=] { impl_->set_items_cutoff_bug(); }); hl->addWidget(buttonSetTestSet1); impl_->treeview1 = new QTreeView(this); l->addWidget(impl_->treeview1); impl_->treeview1->setHeaderHidden(true); impl_->treeview1->setRootIsDecorated(false); auto icon = QIcon::fromTheme(QLatin1String("edit-undo")); impl_->model.insertDocument(impl_->model.rowCount(), addDoc(QStringLiteral("/home/gm/projects/proj1/src/file1.h"))); impl_->model.insertDocument(impl_->model.rowCount(), addDoc(QStringLiteral("/home/gm/projects/proj1/src/file2.cpp"))); impl_->model.insertDocument(impl_->model.rowCount(), addDoc(QStringLiteral("/home/gm/dev/file3.py"))); impl_->model.insertDocument(impl_->model.rowCount(), addDoc(QStringLiteral("/home/gm/dev/file3kjaskdfkljasdfklj089asdfkjklasdjf90asdfsdfkj.py"))); impl_->model.insertDocument(impl_->model.rowCount(), addDoc(QStringLiteral("/home/gm/dev/proj2/asldfkjasdfk/asdlfkjasd;faf/;ajsdkfgjaskdfgasdf/file3.py"))); // impl_->insert_a_item(); // impl_->remove_a_item(); impl_->model.rowCount(); impl_->model.item(0); impl_->model.index(0, 0); impl_->treeview1->setModel(&impl_->model); impl_->treeview1->resizeColumnToContents(0); impl_->treeview1->resizeColumnToContents(1); auto listview1 = new QListView(this); l->addWidget(listview1); listview1->setModel(&impl_->model); auto treeview2 = new QTreeView(this); l->addWidget(treeview2); } TsTestApp::~TsTestApp() { } int main(int argc, char *argv[]) { QApplication app(argc, argv); TsTestApp w; w.show(); return app.exec(); } diff --git a/addons/xmlcheck/plugin_katexmlcheck.cpp b/addons/xmlcheck/plugin_katexmlcheck.cpp index e6c3b5561..c55d6efa9 100644 --- a/addons/xmlcheck/plugin_katexmlcheck.cpp +++ b/addons/xmlcheck/plugin_katexmlcheck.cpp @@ -1,394 +1,393 @@ /*************************************************************************** plugin_katexmlcheck.cpp - checks XML files using xmllint ------------------- begin : 2002-07-06 copyright : (C) 2002 by Daniel Naber email : daniel.naber@t-online.de ***************************************************************************/ /*************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ /* -fixme: show dock if "Validate XML" is selected (doesn't currently work when Kate was just started and the dockwidget isn't yet visible) -fixme(?): doesn't correctly disappear when deactivated in config */ // TODO: // Cleanup unneeded headers // Find resources and translate i18n messages // all translations were deleted in https://websvn.kde.org/?limit_changes=0&view=revision&revision=1433517 // What to do with catalogs? What is it for? // Implement hot key shortcut to do xml validation // Remove copyright above due to author orphaned this plugin? // Possibility to check only well-formdness without validation // Hide output in dock when switching to another tab // Make ability to validate against xml schema and then edit docbook // Should del space in [km] strang in katexmlcheck.desktop? // Which variant should I choose? QUrl.adjusted(rm filename).path() or QUrl.toString(rm filename|rm schema) // What about replace xmllint xmlstarlet or something? // Maybe use QXmlReader to take dtds and xsds? #include "plugin_katexmlcheck.h" #include //#include "plugin_katexmlcheck.moc" this goes to end #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(PluginKateXMLCheckFactory, "katexmlcheck.json", registerPlugin();) PluginKateXMLCheck::PluginKateXMLCheck(QObject *const parent, const QVariantList &) : KTextEditor::Plugin(parent) { qDebug() << "PluginXmlCheck()"; } PluginKateXMLCheck::~PluginKateXMLCheck() { } QObject *PluginKateXMLCheck::createView(KTextEditor::MainWindow *mainWindow) { return new PluginKateXMLCheckView(this, mainWindow); } //--------------------------------- PluginKateXMLCheckView::PluginKateXMLCheckView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainwin) : QObject(mainwin) - , KXMLGUIClient() , m_mainWindow(mainwin) { KXMLGUIClient::setComponentName(QStringLiteral("katexmlcheck"), i18n("Kate XML check")); // where i18n resources? setXMLFile(QStringLiteral("ui.rc")); dock = m_mainWindow->createToolView(plugin, QStringLiteral("kate_plugin_xmlcheck_ouputview"), KTextEditor::MainWindow::Bottom, QIcon::fromTheme(QStringLiteral("misc")), i18n("XML Checker Output")); listview = new QTreeWidget(dock); m_tmp_file = nullptr; QAction *a = actionCollection()->addAction(QStringLiteral("xml_check")); a->setText(i18n("Validate XML")); connect(a, &QAction::triggered, this, &PluginKateXMLCheckView::slotValidate); // TODO?: //(void) new KAction ( i18n("Indent XML"), KShortcut(), this, // SLOT(slotIndent()), actionCollection(), "xml_indent" ); listview->setFocusPolicy(Qt::NoFocus); QStringList headers; headers << i18n("#"); headers << i18n("Line"); headers << i18n("Column"); headers << i18n("Message"); listview->setHeaderLabels(headers); listview->setRootIsDecorated(false); connect(listview, &QTreeWidget::itemClicked, this, &PluginKateXMLCheckView::slotClicked); QHeaderView *header = listview->header(); header->setSectionResizeMode(0, QHeaderView::ResizeToContents); header->setSectionResizeMode(1, QHeaderView::ResizeToContents); header->setSectionResizeMode(2, QHeaderView::ResizeToContents); /* TODO?: invalidate the listview when document has changed Kate::View *kv = application()->activeMainWindow()->activeView(); if( ! kv ) { qDebug() << "Warning: no Kate::View"; return; } connect(kv, SIGNAL(modifiedChanged()), this, SLOT(slotUpdate())); */ connect(&m_proc, static_cast(&QProcess::finished), this, &PluginKateXMLCheckView::slotProcExited); // we currently only want errors: m_proc.setProcessChannelMode(QProcess::SeparateChannels); // m_proc.setProcessChannelMode(QProcess::ForwardedChannels); // For Debugging. Do not use this. mainwin->guiFactory()->addClient(this); } PluginKateXMLCheckView::~PluginKateXMLCheckView() { m_mainWindow->guiFactory()->removeClient(this); delete m_tmp_file; delete dock; } void PluginKateXMLCheckView::slotProcExited(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitCode); // FIXME: doesn't work correct the first time: // if( m_dockwidget->isDockBackPossible() ) { // m_dockwidget->dockBack(); // } if (exitStatus != QProcess::NormalExit) { QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, QStringLiteral("1").rightJustified(4, ' ')); item->setText(3, QStringLiteral("Validate process crashed.")); listview->addTopLevelItem(item); return; } qDebug() << "slotProcExited()"; QApplication::restoreOverrideCursor(); delete m_tmp_file; QString proc_stderr = QString::fromLocal8Bit(m_proc.readAllStandardError()); m_tmp_file = nullptr; listview->clear(); uint list_count = 0; uint err_count = 0; if (!m_validating) { // no i18n here, so we don't get an ugly English<->Non-english mixup: QString msg; if (m_dtdname.isEmpty()) { msg = QStringLiteral("No DOCTYPE found, will only check well-formedness."); } else { msg = '\'' + m_dtdname + "' not found, will only check well-formedness."; } QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, QStringLiteral("1").rightJustified(4, ' ')); item->setText(3, msg); listview->addTopLevelItem(item); list_count++; } if (!proc_stderr.isEmpty()) { QStringList lines = proc_stderr.split('\n', QString::SkipEmptyParts); QString linenumber, msg; int line_count = 0; for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) { QString line = *it; line_count++; int semicolon_1 = line.indexOf(':'); int semicolon_2 = line.indexOf(':', semicolon_1 + 1); int semicolon_3 = line.indexOf(':', semicolon_2 + 2); int caret_pos = line.indexOf('^'); if (semicolon_1 != -1 && semicolon_2 != -1 && semicolon_3 != -1) { linenumber = line.mid(semicolon_1 + 1, semicolon_2 - semicolon_1 - 1).trimmed(); linenumber = linenumber.rightJustified(6, ' '); // for sorting numbers msg = line.mid(semicolon_3 + 1, line.length() - semicolon_3 - 1).trimmed(); } else if (caret_pos != -1 || line_count == lines.size()) { // TODO: this fails if "^" occurs in the real text?! if (line_count == lines.size() && caret_pos == -1) { msg = msg + '\n' + line; } QString col = QString::number(caret_pos); if (col == QLatin1String("-1")) { col = QLatin1String(""); } err_count++; list_count++; QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, QString::number(list_count).rightJustified(4, ' ')); item->setText(1, linenumber); item->setTextAlignment(1, (item->textAlignment(1) & ~Qt::AlignHorizontal_Mask) | Qt::AlignRight); item->setText(2, col); item->setTextAlignment(2, (item->textAlignment(2) & ~Qt::AlignHorizontal_Mask) | Qt::AlignRight); item->setText(3, msg); listview->addTopLevelItem(item); } else { msg = msg + '\n' + line; } } } if (err_count == 0) { QString msg; if (m_validating) { msg = QStringLiteral("No errors found, document is valid."); // no i18n here } else { msg = QStringLiteral("No errors found, document is well-formed."); // no i18n here } QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, QString::number(list_count + 1).rightJustified(4, ' ')); item->setText(3, msg); listview->addTopLevelItem(item); } } void PluginKateXMLCheckView::slotClicked(QTreeWidgetItem *item, int column) { Q_UNUSED(column); qDebug() << "slotClicked"; if (item) { bool ok = true; uint line = item->text(1).toUInt(&ok); bool ok2 = true; uint column = item->text(2).toUInt(&ok); if (ok && ok2) { KTextEditor::View *kv = m_mainWindow->activeView(); if (!kv) return; kv->setCursorPosition(KTextEditor::Cursor(line - 1, column)); } } } void PluginKateXMLCheckView::slotUpdate() { qDebug() << "slotUpdate() (not implemented yet)"; } bool PluginKateXMLCheckView::slotValidate() { qDebug() << "slotValidate()"; m_mainWindow->showToolView(dock); m_validating = false; m_dtdname = QLatin1String(""); KTextEditor::View *kv = m_mainWindow->activeView(); if (!kv) return false; delete m_tmp_file; m_tmp_file = new QTemporaryFile(); if (!m_tmp_file->open()) { qDebug() << "Error (slotValidate()): could not create '" << m_tmp_file->fileName() << "': " << m_tmp_file->errorString(); KMessageBox::error(nullptr, i18n("Error: Could not create " "temporary file '%1'.", m_tmp_file->fileName())); delete m_tmp_file; m_tmp_file = nullptr; return false; } QTextStream s(m_tmp_file); s << kv->document()->text(); s.flush(); QString exe = QStandardPaths::findExecutable(QStringLiteral("xmllint")); if (exe.isEmpty()) { exe = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("xmllint")); } // qDebug() << "exe=" <findResource("data", "ksgmltools2/customization/catalog.xml"); // qDebug() << "catalogs: " << catalogs; // setenv("XML_CATALOG_FILES", QFile::encodeName( catalogs ).data(), 1); // } // qDebug() << "**catalogs: " << getenv("XML_CATALOG_FILES"); QStringList args; args << QStringLiteral("--noout"); // tell xmllint the working path of the document's file, if possible. // otherwise it will not find relative DTDs // I should give path to location of file, but remove filename // I can make QUrl.adjusted(rm filename).path() // or QUrl.toString(rm filename|rm schema) // Result is the same. Which variant should I choose? // QString path = kv->document()->url().adjusted(QUrl::RemoveFilename).path(); // xmllint uses space- or colon-separated path option, so spaces should be encoded to %20. It is done with EncodeSpaces. // Now what about colons in file names or paths? // This way xmllint works normally: // xmllint --noout --path "/home/user/my/with:colon/" --valid "/home/user/my/with:colon/demo-1.xml" // but because this plugin makes temp file path to file is another and this way xmllint refuses to find dtd: // xmllint --noout --path "/home/user/my/with:colon/" --valid "/tmp/kate.X23725" // As workaround we can encode ':' with %3A QString path = kv->document()->url().toString(QUrl::RemoveFilename | QUrl::PreferLocalFile | QUrl::EncodeSpaces); path.replace(':', QLatin1String("%3A")); // because of such inconvenience with xmllint and paths, maybe switch to xmlstarlet? qDebug() << "path=" << path; if (!path.isEmpty()) { args << QStringLiteral("--path") << path; } // heuristic: assume that the doctype is in the first 10,000 bytes: QString text_start = kv->document()->text().left(10000); // remove comments before looking for doctype (as a doctype might be commented out // and needs to be ignored then): QRegExp re(""); re.setMinimal(true); text_start.remove(re); QRegExp re_doctype("fileName(); qDebug() << "m_tmp_file->fileName()=" << m_tmp_file->fileName(); m_proc.start(exe, args); qDebug() << "m_proc.program():" << m_proc.program(); // I want to see parameters qDebug() << "args=" << args; qDebug() << "exit code:" << m_proc.exitCode(); if (!m_proc.waitForStarted(-1)) { KMessageBox::error(nullptr, i18n("Error: Failed to execute xmllint. Please make " "sure that xmllint is installed. It is part of libxml2.")); return false; } QApplication::setOverrideCursor(Qt::WaitCursor); return true; } #include "plugin_katexmlcheck.moc" diff --git a/addons/xmlcheck/plugin_katexmlcheck.h b/addons/xmlcheck/plugin_katexmlcheck.h index be47157f4..24b2951e0 100644 --- a/addons/xmlcheck/plugin_katexmlcheck.h +++ b/addons/xmlcheck/plugin_katexmlcheck.h @@ -1,83 +1,83 @@ /*************************************************************************** plugin_katexmlcheck.h ------------------- begin : 2002-07-06 copyright : (C) 2002 by Daniel Naber email : daniel.naber@t-online.de ***************************************************************************/ /*************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #ifndef PLUGIN_KATEXMLCHECK_H #define PLUGIN_KATEXMLCHECK_H #include #include #include #include #include #include #include #include class QTreeWidget; class QTreeWidgetItem; class QTemporaryFile; class QProcess; class PluginKateXMLCheckView : public QObject, public KXMLGUIClient { Q_OBJECT public: PluginKateXMLCheckView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainwin); ~PluginKateXMLCheckView() override; KTextEditor::MainWindow *m_mainWindow; QWidget *dock; public Q_SLOTS: bool slotValidate(); void slotClicked(QTreeWidgetItem *item, int column); void slotProcExited(int exitCode, QProcess::ExitStatus exitStatus); void slotUpdate(); private: QTemporaryFile *m_tmp_file; - KParts::ReadOnlyPart *part; - bool m_validating; + KParts::ReadOnlyPart *part = nullptr; + bool m_validating = false; QProcess m_proc; QString m_proc_stderr; QString m_dtdname; QTreeWidget *listview; }; class PluginKateXMLCheck : public KTextEditor::Plugin { Q_OBJECT public: explicit PluginKateXMLCheck(QObject *parent = nullptr, const QVariantList & = QVariantList()); ~PluginKateXMLCheck() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; }; #endif // PLUGIN_KATEXMLCHECK_H diff --git a/addons/xmltools/plugin_katexmltools.cpp b/addons/xmltools/plugin_katexmltools.cpp index c45427fed..559243fa3 100644 --- a/addons/xmltools/plugin_katexmltools.cpp +++ b/addons/xmltools/plugin_katexmltools.cpp @@ -1,1083 +1,1082 @@ /*************************************************************************** pluginKatexmltools.cpp List elements, attributes, attribute values and entities allowed by DTD. Needs a DTD in XML format ( as produced by dtdparse ) for most features. copyright : ( C ) 2001-2002 by Daniel Naber email : daniel.naber@t-online.de Copyright (C) 2005 by Anders Lund KDE SC 4 version (C) 2010 Tomas Trnka ***************************************************************************/ /*************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or ( at your option ) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ /* README: The basic idea is this: certain keyEvents(), namely [<&" ], trigger a completion box. This is intended as a help for editing. There are some cases where the XML spec is not followed, e.g. one can add the same attribute twice to an element. Also see the user documentation. If backspace is pressed after a completion popup was closed, the popup will re-open. This way typos can be corrected and the popup will reappear, which is quite comfortable. FIXME: -( docbook ) : insert space between the quotes, press "de" and return -> only "d" inserted -The "Insert Element" dialog isn't case insensitive, but it should be -See the "fixme"'s in the code TODO: -check for mem leaks -add "Go to opening/parent tag"? -check doctype to get top-level element -can undo behaviour be improved?, e.g. the plugins internal deletions of text don't have to be an extra step -don't offer entities if inside tag but outside attribute value -Support for more than one namespace at the same time ( e.g. XSLT + XSL-FO )? =>This could also be handled in the XSLT DTD fragment, as described in the XSLT 1.0 spec, but then at it will only show you HTML elements! =>So better "Assign meta DTD" and "Add meta DTD", the latter will expand the current meta DTD -Option to insert empty element in form -Show expanded entities with QChar::QChar( int rc ) + unicode font -Don't ignore entities defined in the document's prologue -Only offer 'valid' elements, i.e. don't take the elements as a set but check if the DTD is matched ( order, number of occurrences, ... ) -Maybe only read the meta DTD file once, then store the resulting QMap on disk ( using QDataStream )? We'll then have to compare timeOf_cacheFile <-> timeOf_metaDtd. -Try to use libxml */ #include "plugin_katexmltools.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(PluginKateXMLToolsFactory, "katexmltools.json", registerPlugin();) PluginKateXMLTools::PluginKateXMLTools(QObject *const parent, const QVariantList &) : KTextEditor::Plugin(parent) { } PluginKateXMLTools::~PluginKateXMLTools() { } QObject *PluginKateXMLTools::createView(KTextEditor::MainWindow *mainWindow) { return new PluginKateXMLToolsView(mainWindow); } PluginKateXMLToolsView::PluginKateXMLToolsView(KTextEditor::MainWindow *mainWin) : QObject(mainWin) - , KXMLGUIClient() , m_mainWindow(mainWin) , m_model(this) { // qDebug() << "PluginKateXMLTools constructor called"; KXMLGUIClient::setComponentName(QStringLiteral("katexmltools"), i18n("Kate XML Tools")); setXMLFile(QStringLiteral("ui.rc")); QAction *actionInsert = new QAction(i18n("&Insert Element..."), this); connect(actionInsert, &QAction::triggered, &m_model, &PluginKateXMLToolsCompletionModel::slotInsertElement); actionCollection()->addAction(QStringLiteral("xml_tool_insert_element"), actionInsert); actionCollection()->setDefaultShortcut(actionInsert, Qt::CTRL + Qt::Key_Return); QAction *actionClose = new QAction(i18n("&Close Element"), this); connect(actionClose, &QAction::triggered, &m_model, &PluginKateXMLToolsCompletionModel::slotCloseElement); actionCollection()->addAction(QStringLiteral("xml_tool_close_element"), actionClose); actionCollection()->setDefaultShortcut(actionClose, Qt::CTRL + Qt::Key_Less); QAction *actionAssignDTD = new QAction(i18n("Assign Meta &DTD..."), this); connect(actionAssignDTD, &QAction::triggered, &m_model, &PluginKateXMLToolsCompletionModel::getDTD); actionCollection()->addAction(QStringLiteral("xml_tool_assign"), actionAssignDTD); mainWin->guiFactory()->addClient(this); connect(KTextEditor::Editor::instance()->application(), &KTextEditor::Application::documentDeleted, &m_model, &PluginKateXMLToolsCompletionModel::slotDocumentDeleted); } PluginKateXMLToolsView::~PluginKateXMLToolsView() { m_mainWindow->guiFactory()->removeClient(this); // qDebug() << "xml tools destructor 1..."; // TODO: unregister the model } PluginKateXMLToolsCompletionModel::PluginKateXMLToolsCompletionModel(QObject *const parent) : CodeCompletionModel(parent) , m_viewToAssignTo(nullptr) , m_mode(none) , m_correctPos(0) { } PluginKateXMLToolsCompletionModel::~PluginKateXMLToolsCompletionModel() { qDeleteAll(m_dtds); m_dtds.clear(); } void PluginKateXMLToolsCompletionModel::slotDocumentDeleted(KTextEditor::Document *doc) { // Remove the document from m_DTDs, and also delete the PseudoDTD // if it becomes unused. if (m_docDtds.contains(doc)) { qDebug() << "XMLTools:slotDocumentDeleted: documents: " << m_docDtds.count() << ", DTDs: " << m_dtds.count(); PseudoDTD *dtd = m_docDtds.take(doc); if (m_docDtds.key(dtd)) { return; } QHash::iterator it; for (it = m_dtds.begin(); it != m_dtds.end(); ++it) { if (it.value() == dtd) { m_dtds.erase(it); delete dtd; return; } } } } void PluginKateXMLToolsCompletionModel::completionInvoked(KTextEditor::View *kv, const KTextEditor::Range &range, const InvocationType invocationType) { Q_UNUSED(range) Q_UNUSED(invocationType) qDebug() << "xml tools completionInvoked"; KTextEditor::Document *doc = kv->document(); if (!m_docDtds[doc]) // no meta DTD assigned yet { return; } // debug to test speed: // QTime t; t.start(); beginResetModel(); m_allowed.clear(); // get char on the left of the cursor: KTextEditor::Cursor curpos = kv->cursorPosition(); uint line = curpos.line(), col = curpos.column(); QString lineStr = kv->document()->line(line); QString leftCh = lineStr.mid(col - 1, 1); QString secondLeftCh = lineStr.mid(col - 2, 1); if (leftCh == QLatin1String("&")) { qDebug() << "Getting entities"; m_allowed = m_docDtds[doc]->entities(QString()); m_mode = entities; } else if (leftCh == QLatin1String("<")) { qDebug() << "*outside tag -> get elements"; QString parentElement = getParentElement(*kv, 1); qDebug() << "parent: " << parentElement; m_allowed = m_docDtds[doc]->allowedElements(parentElement); m_mode = elements; } else if (leftCh == QLatin1String("/") && secondLeftCh == QLatin1String("<")) { qDebug() << "*close parent element"; QString parentElement = getParentElement(*kv, 2); if (!parentElement.isEmpty()) { m_mode = closingtag; m_allowed = QStringList(parentElement); } } else if (leftCh == QLatin1Char(' ') || (isQuote(leftCh) && secondLeftCh == QLatin1String("="))) { // TODO: check secondLeftChar, too?! then you don't need to trigger // with space and we yet save CPU power QString currentElement = insideTag(*kv); QString currentAttribute; if (!currentElement.isEmpty()) { currentAttribute = insideAttribute(*kv); } qDebug() << "Tag: " << currentElement; qDebug() << "Attr: " << currentAttribute; if (!currentElement.isEmpty() && !currentAttribute.isEmpty()) { qDebug() << "*inside attribute -> get attribute values"; m_allowed = m_docDtds[doc]->attributeValues(currentElement, currentAttribute); if (m_allowed.count() == 1 && (m_allowed[0] == QLatin1String("CDATA") || m_allowed[0] == QLatin1String("ID") || m_allowed[0] == QLatin1String("IDREF") || m_allowed[0] == QLatin1String("IDREFS") || m_allowed[0] == QLatin1String("ENTITY") || m_allowed[0] == QLatin1String("ENTITIES") || m_allowed[0] == QLatin1String("NMTOKEN") || m_allowed[0] == QLatin1String("NMTOKENS") || m_allowed[0] == QLatin1String("NAME"))) { // these must not be taken literally, e.g. don't insert the string "CDATA" m_allowed.clear(); } else { m_mode = attributevalues; } } else if (!currentElement.isEmpty()) { qDebug() << "*inside tag -> get attributes"; m_allowed = m_docDtds[doc]->allowedAttributes(currentElement); m_mode = attributes; } } // qDebug() << "time elapsed (ms): " << t.elapsed(); qDebug() << "Allowed strings: " << m_allowed.count(); if (m_allowed.count() >= 1 && m_allowed[0] != QLatin1String("__EMPTY")) { m_allowed = sortQStringList(m_allowed); } setRowCount(m_allowed.count()); endResetModel(); } int PluginKateXMLToolsCompletionModel::columnCount(const QModelIndex &) const { return 1; } int PluginKateXMLToolsCompletionModel::rowCount(const QModelIndex &parent) const { if (!m_allowed.isEmpty()) { // Is there smth to complete? if (!parent.isValid()) { // Return the only one group node for root return 1; } if (parent.internalId() == groupNode) { // Return available rows count for group level node return m_allowed.size(); } } return 0; } QModelIndex PluginKateXMLToolsCompletionModel::parent(const QModelIndex &index) const { if (!index.isValid()) { // Is root/invalid index? return QModelIndex(); // Nothing to return... } if (index.internalId() == groupNode) { // Return a root node for group return QModelIndex(); } // Otherwise, this is a leaf level, so return the only group as a parent return createIndex(0, 0, groupNode); } QModelIndex PluginKateXMLToolsCompletionModel::index(const int row, const int column, const QModelIndex &parent) const { if (!parent.isValid()) { // At 'top' level only 'header' present, so nothing else than row 0 can be here... return row == 0 ? createIndex(row, column, groupNode) : QModelIndex(); } if (parent.internalId() == groupNode) { // Is this a group node? if (0 <= row && row < m_allowed.size()) { // Make sure to return only valid indices return createIndex(row, column, nullptr); // Just return a leaf-level index } } // Leaf node has no children... nothing to return return QModelIndex(); } QVariant PluginKateXMLToolsCompletionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { // Nothing to do w/ invalid index return QVariant(); } if (index.internalId() == groupNode) { // Return group level node data switch (role) { case KTextEditor::CodeCompletionModel::GroupRole: return QVariant(Qt::DisplayRole); case Qt::DisplayRole: return currentModeToString(); default: break; } return QVariant(); // Nothing to return for other roles } switch (role) { case Qt::DisplayRole: switch (index.column()) { case KTextEditor::CodeCompletionModel::Name: return m_allowed.at(index.row()); default: break; } default: break; } return QVariant(); } bool PluginKateXMLToolsCompletionModel::shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) { Q_UNUSED(view) Q_UNUSED(userInsertion) Q_UNUSED(position) const QString triggerChars = QStringLiteral("&application()->activeMainWindow()) { return; } KTextEditor::View *kv = KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView(); if (!kv) { qDebug() << "Warning: no KTextEditor::View"; return; } // ### replace this with something more sane // Start where the supplied XML-DTDs are fed by default unless // user changed directory last time: QString defaultDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("katexmltools")) + "/katexmltools/"; if (m_urlString.isNull()) { m_urlString = defaultDir; } // Guess the meta DTD by looking at the doctype's public identifier. // XML allows comments etc. before the doctype, so look further than // just the first line. // Example syntax: // uint checkMaxLines = 200; QString documentStart = kv->document()->text(KTextEditor::Range(0, 0, checkMaxLines + 1, 0)); const QRegularExpression re(QStringLiteral(" */ filename = QStringLiteral("xslt-1.0.dtd.xml"); doctype = QStringLiteral("XSLT 1.0"); } else { qDebug() << "No doctype found"; } QUrl url; if (filename.isEmpty()) { // no meta dtd found for this file url = QFileDialog::getOpenFileUrl(KTextEditor::Editor::instance()->application()->activeMainWindow()->window(), i18n("Assign Meta DTD in XML Format"), QUrl::fromLocalFile(m_urlString), QStringLiteral("*.xml")); } else { url.setUrl(defaultDir + filename); KMessageBox::information(nullptr, i18n("The current file has been identified " "as a document of type \"%1\". The meta DTD for this document type " "will now be loaded.", doctype), i18n("Loading XML Meta DTD"), QStringLiteral("DTDAssigned")); } if (url.isEmpty()) { return; } m_urlString = url.url(); // remember directory for next time if (m_dtds[m_urlString]) { assignDTD(m_dtds[m_urlString], kv); } else { m_dtdString.clear(); m_viewToAssignTo = kv; QGuiApplication::setOverrideCursor(Qt::WaitCursor); KIO::TransferJob *job = KIO::get(url); connect(job, &KIO::TransferJob::result, this, &PluginKateXMLToolsCompletionModel::slotFinished); connect(job, &KIO::TransferJob::data, this, &PluginKateXMLToolsCompletionModel::slotData); } qDebug() << "XMLTools::getDTD: Documents: " << m_docDtds.count() << ", DTDs: " << m_dtds.count(); } void PluginKateXMLToolsCompletionModel::slotFinished(KJob *job) { if (job->error()) { // qDebug() << "XML Plugin error: DTD in XML format (" << filename << " ) could not be loaded"; static_cast(job)->uiDelegate()->showErrorMessage(); } else if (static_cast(job)->isErrorPage()) { // catch failed loading loading via http: KMessageBox::error(nullptr, i18n("The file '%1' could not be opened. " "The server returned an error.", m_urlString), i18n("XML Plugin Error")); } else { PseudoDTD *dtd = new PseudoDTD(); dtd->analyzeDTD(m_urlString, m_dtdString); m_dtds.insert(m_urlString, dtd); assignDTD(dtd, m_viewToAssignTo); // clean up a bit m_viewToAssignTo = nullptr; m_dtdString.clear(); } QGuiApplication::restoreOverrideCursor(); } void PluginKateXMLToolsCompletionModel::slotData(KIO::Job *, const QByteArray &data) { m_dtdString += QString(data); } void PluginKateXMLToolsCompletionModel::assignDTD(PseudoDTD *dtd, KTextEditor::View *view) { m_docDtds.insert(view->document(), dtd); // TODO:perhaps for all views()? KTextEditor::CodeCompletionInterface *cci = qobject_cast(view); if (cci) { cci->registerCompletionModel(this); cci->setAutomaticInvocationEnabled(true); qDebug() << "PluginKateXMLToolsView: completion model registered"; } else { qWarning() << "PluginKateXMLToolsView: completion interface unavailable"; } } /** * Offer a line edit with completion for possible elements at cursor position and insert the * tag one chosen/entered by the user, plus its closing tag. If there's a text selection, * add the markup around it. */ void PluginKateXMLToolsCompletionModel::slotInsertElement() { if (!KTextEditor::Editor::instance()->application()->activeMainWindow()) { return; } KTextEditor::View *kv = KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView(); if (!kv) { qDebug() << "Warning: no KTextEditor::View"; return; } KTextEditor::Document *doc = kv->document(); PseudoDTD *dtd = m_docDtds[doc]; QString parentElement = getParentElement(*kv, 0); QStringList allowed; if (dtd) { allowed = dtd->allowedElements(parentElement); } QString text; InsertElement dialog(allowed, kv); if (dialog.exec() == QDialog::Accepted) { text = dialog.text(); } if (!text.isEmpty()) { QStringList list = text.split(QChar(' ')); QString pre; QString post; // anders: use if the tag is required to be empty. // In that case maybe we should not remove the selection? or overwrite it? int adjust = 0; // how much to move cursor. // if we know that we have attributes, it goes // just after the tag name, otherwise between tags. if (dtd && dtd->allowedAttributes(list[0]).count()) { adjust++; // the ">" } if (dtd && dtd->allowedElements(list[0]).contains(QLatin1String("__EMPTY"))) { pre = '<' + text + "/>"; if (adjust) { adjust++; // for the "/" } } else { pre = '<' + text + '>'; post = "'; } QString marked; if (!post.isEmpty()) { marked = kv->selectionText(); } KTextEditor::Document::EditingTransaction transaction(doc); if (!marked.isEmpty()) { kv->removeSelectionText(); } // with the old selection now removed, curPos points to the start of pre KTextEditor::Cursor curPos = kv->cursorPosition(); curPos.setColumn(curPos.column() + pre.length() - adjust); kv->insertText(pre + marked + post); kv->setCursorPosition(curPos); } } /** * Insert a closing tag for the nearest not-closed parent element. */ void PluginKateXMLToolsCompletionModel::slotCloseElement() { if (!KTextEditor::Editor::instance()->application()->activeMainWindow()) { return; } KTextEditor::View *kv = KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView(); if (!kv) { qDebug() << "Warning: no KTextEditor::View"; return; } QString parentElement = getParentElement(*kv, 0); // qDebug() << "parentElement: '" << parentElement << "'"; QString closeTag = "'; if (!parentElement.isEmpty()) { kv->insertText(closeTag); } } // modify the completion string before it gets inserted void PluginKateXMLToolsCompletionModel::executeCompletionItem(KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const { KTextEditor::Range toReplace = word; KTextEditor::Document *document = view->document(); QString text = data(index.sibling(index.row(), Name), Qt::DisplayRole).toString(); qDebug() << "executeCompletionItem text: " << text; int line, col; view->cursorPosition().position(line, col); QString lineStr = document->line(line); QString leftCh = lineStr.mid(col - 1, 1); QString rightCh = lineStr.mid(col, 1); int posCorrection = 0; // where to move the cursor after completion ( >0 = move right ) if (m_mode == entities) { text = text + ';'; } else if (m_mode == attributes) { text = text + "=\"\""; posCorrection = -1; if (!rightCh.isEmpty() && rightCh != QLatin1String(">") && rightCh != QLatin1String("/") && rightCh != QLatin1String(" ")) { // TODO: other whitespaces // add space in front of the next attribute text = text + ' '; posCorrection--; } } else if (m_mode == attributevalues) { // TODO: support more than one line uint startAttValue = 0; uint endAttValue = 0; // find left quote: for (startAttValue = col; startAttValue > 0; startAttValue--) { QString ch = lineStr.mid(startAttValue - 1, 1); if (isQuote(ch)) { break; } } // find right quote: for (endAttValue = col; endAttValue <= static_cast(lineStr.length()); endAttValue++) { QString ch = lineStr.mid(endAttValue - 1, 1); if (isQuote(ch)) { break; } } // replace the current contents of the attribute if (startAttValue < endAttValue) { toReplace = KTextEditor::Range(line, startAttValue, line, endAttValue - 1); } } else if (m_mode == elements) { // anders: if the tag is marked EMPTY, insert in form QString str; bool isEmptyTag = m_docDtds[document]->allowedElements(text).contains(QLatin1String("__EMPTY")); if (isEmptyTag) { str = text + "/>"; } else { str = text + ">'; } // Place the cursor where it is most likely wanted: // always inside the tag if the tag is empty AND the DTD indicates that there are attribs) // outside for open tags, UNLESS there are mandatory attributes if (m_docDtds[document]->requiredAttributes(text).count() || (isEmptyTag && m_docDtds[document]->allowedAttributes(text).count())) { posCorrection = text.length() - str.length(); } else if (!isEmptyTag) { posCorrection = text.length() - str.length() + 1; } text = str; } else if (m_mode == closingtag) { text += '>'; } document->replaceText(toReplace, text); // move the cursor to desired position KTextEditor::Cursor curPos = view->cursorPosition(); curPos.setColumn(curPos.column() + posCorrection); view->setCursorPosition(curPos); } // ======================================================================== // Pseudo-XML stuff: /** * Check if cursor is inside a tag, that is * if "<" occurs before ">" occurs ( on the left side of the cursor ). * Return the tag name, return "" if we cursor is outside a tag. */ QString PluginKateXMLToolsCompletionModel::insideTag(KTextEditor::View &kv) { int line, col; kv.cursorPosition().position(line, col); int y = line; // another variable because uint <-> int do { QString lineStr = kv.document()->line(y); for (uint x = col; x > 0; x--) { QString ch = lineStr.mid(x - 1, 1); if (ch == QLatin1String(">")) { // cursor is outside tag return QString(); } if (ch == QLatin1String("<")) { QString tag; // look for white space on the right to get the tag name for (int z = x; z <= lineStr.length(); ++z) { ch = lineStr.mid(z - 1, 1); if (ch.at(0).isSpace() || ch == QLatin1String("/") || ch == QLatin1String(">")) { return tag.right(tag.length() - 1); } if (z == lineStr.length()) { tag += ch; return tag.right(tag.length() - 1); } tag += ch; } } } y--; col = kv.document()->line(y).length(); } while (y >= 0); return QString(); } /** * Check if cursor is inside an attribute value, that is * if '="' is on the left, and if it's nearer than "<" or ">". * * @Return the attribute name or "" if we're outside an attribute * value. * * Note: only call when insideTag() == true. * TODO: allow whitespace around "=" */ QString PluginKateXMLToolsCompletionModel::insideAttribute(KTextEditor::View &kv) { int line, col; kv.cursorPosition().position(line, col); int y = line; // another variable because uint <-> int uint x = 0; QString lineStr; QString ch; do { lineStr = kv.document()->line(y); for (x = col; x > 0; x--) { ch = lineStr.mid(x - 1, 1); QString chLeft = lineStr.mid(x - 2, 1); // TODO: allow whitespace if (isQuote(ch) && chLeft == QLatin1String("=")) { break; } else if (isQuote(ch) && chLeft != QLatin1String("=")) { return QString(); } else if (ch == QLatin1String("<") || ch == QLatin1String(">")) { return QString(); } } y--; col = kv.document()->line(y).length(); } while (!isQuote(ch)); // look for next white space on the left to get the tag name QString attr; for (int z = x; z >= 0; z--) { ch = lineStr.mid(z - 1, 1); if (ch.at(0).isSpace()) { break; } if (z == 0) { // start of line == whitespace attr += ch; break; } attr = ch + attr; } return attr.left(attr.length() - 2); } /** * Find the parent element for the current cursor position. That is, * go left and find the first opening element that's not closed yet, * ignoring empty elements. * Examples: If cursor is at "X", the correct parent element is "p": *

foo test bar X *

foo bar X *

foo bar X *

foo bar X */ QString PluginKateXMLToolsCompletionModel::getParentElement(KTextEditor::View &kv, int skipCharacters) { enum { parsingText, parsingElement, parsingElementBoundary, parsingNonElement, parsingAttributeDquote, parsingAttributeSquote, parsingIgnore } parseState; parseState = (skipCharacters > 0) ? parsingIgnore : parsingText; int nestingLevel = 0; int line, col; kv.cursorPosition().position(line, col); QString str = kv.document()->line(line); while (true) { // move left a character if (!col--) { do { if (!line--) { return QString(); // reached start of document } str = kv.document()->line(line); col = str.length(); } while (!col); --col; } ushort ch = str.at(col).unicode(); switch (parseState) { case parsingIgnore: // ignore the specified number of characters parseState = (--skipCharacters > 0) ? parsingIgnore : parsingText; break; case parsingText: switch (ch) { case '<': // hmm... we were actually inside an element return QString(); case '>': // we just hit an element boundary parseState = parsingElementBoundary; break; } break; case parsingElement: switch (ch) { case '"': // attribute ( double quoted ) parseState = parsingAttributeDquote; break; case '\'': // attribute ( single quoted ) parseState = parsingAttributeSquote; break; case '/': // close tag parseState = parsingNonElement; ++nestingLevel; break; case '<': // we just hit the start of the element... if (nestingLevel--) { break; } QString tag = str.mid(col + 1); for (uint pos = 0, len = tag.length(); pos < len; ++pos) { ch = tag.at(pos).unicode(); if (ch == ' ' || ch == '\t' || ch == '>') { tag.truncate(pos); break; } } return tag; } break; case parsingElementBoundary: switch (ch) { case '?': // processing instruction case '-': // comment case '/': // empty element parseState = parsingNonElement; break; case '"': parseState = parsingAttributeDquote; break; case '\'': parseState = parsingAttributeSquote; break; case '<': // empty tag ( bad XML ) parseState = parsingText; break; default: parseState = parsingElement; } break; case parsingAttributeDquote: if (ch == '"') { parseState = parsingElement; } break; case parsingAttributeSquote: if (ch == '\'') { parseState = parsingElement; } break; case parsingNonElement: if (ch == '<') { parseState = parsingText; } break; } } } /** * Return true if the tag is neither a closing tag * nor an empty tag, nor a comment, nor processing instruction. */ bool PluginKateXMLToolsCompletionModel::isOpeningTag(const QString &tag) { return (!isClosingTag(tag) && !isEmptyTag(tag) && !tag.startsWith(QLatin1String("")); } /** * Return true if ch is a single or double quote. Expects ch to be of length 1. */ bool PluginKateXMLToolsCompletionModel::isQuote(const QString &ch) { return (ch == QLatin1String("\"") || ch == QLatin1String("'")); } // ======================================================================== // Tools: /// Get string describing current mode QString PluginKateXMLToolsCompletionModel::currentModeToString() const { switch (m_mode) { case entities: return i18n("XML entities"); case attributevalues: return i18n("XML attribute values"); case attributes: return i18n("XML attributes"); case elements: case closingtag: return i18n("XML elements"); default: break; } return QString(); } /** Sort a QStringList case-insensitively. Static. TODO: make it more simple. */ QStringList PluginKateXMLToolsCompletionModel::sortQStringList(QStringList list) { // Sort list case-insensitive. This looks complicated but using a QMap // is even suggested by the Qt documentation. QMap mapList; for (const auto &str : qAsConst(list)) { if (mapList.contains(str.toLower())) { // do not override a previous value, e.g. "Auml" and "auml" are two different // entities, but they should be sorted next to each other. // TODO: currently it's undefined if e.g. "A" or "a" comes first, it depends on // the meta DTD ( really? it seems to work okay?!? ) mapList[str.toLower() + '_'] = str; } else { mapList[str.toLower()] = str; } } list.clear(); QMap::Iterator it; // Qt doc: "the items are alphabetically sorted [by key] when iterating over the map": for (it = mapList.begin(); it != mapList.end(); ++it) { list.append(it.value()); } return list; } // BEGIN InsertElement dialog InsertElement::InsertElement(const QStringList &completions, QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Insert XML Element")); QVBoxLayout *topLayout = new QVBoxLayout(this); // label QString text = i18n("Enter XML tag name and attributes (\"<\", \">\" and closing tag will be supplied):"); QLabel *label = new QLabel(text, this); label->setWordWrap(true); // combo box m_cmbElements = new KHistoryComboBox(this); static_cast(m_cmbElements)->setHistoryItems(completions, true); connect(m_cmbElements->lineEdit(), &QLineEdit::textChanged, this, &InsertElement::slotHistoryTextChanged); // button box QDialogButtonBox *box = new QDialogButtonBox(this); box->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = box->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); connect(box, &QDialogButtonBox::accepted, this, &InsertElement::accept); connect(box, &QDialogButtonBox::rejected, this, &InsertElement::reject); // fill layout topLayout->addWidget(label); topLayout->addWidget(m_cmbElements); topLayout->addWidget(box); m_cmbElements->setFocus(); // make sure the ok button is enabled/disabled correctly slotHistoryTextChanged(m_cmbElements->lineEdit()->text()); } InsertElement::~InsertElement() { } void InsertElement::slotHistoryTextChanged(const QString &text) { m_okButton->setEnabled(!text.isEmpty()); } QString InsertElement::text() const { return m_cmbElements->currentText(); } // END InsertElement dialog #include "plugin_katexmltools.moc" // kate: space-indent on; indent-width 4; replace-tabs on; mixed-indent off; diff --git a/addons/xmltools/plugin_katexmltools.h b/addons/xmltools/plugin_katexmltools.h index 8201fa5ad..c61d9f313 100644 --- a/addons/xmltools/plugin_katexmltools.h +++ b/addons/xmltools/plugin_katexmltools.h @@ -1,173 +1,173 @@ /*************************************************************************** pluginKatexmltools.cpp copyright : (C) 2001-2002 by Daniel Naber email : daniel.naber@t-online.de ***************************************************************************/ /*************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or ( at your option ) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #ifndef PLUGIN_KATEXMLTOOLS_H #define PLUGIN_KATEXMLTOOLS_H #include "pseudo_dtd.h" #include #include #include #include #include #include #include #include #include #include class QComboBox; class QPushButton; class PluginKateXMLTools : public KTextEditor::Plugin { Q_OBJECT public: explicit PluginKateXMLTools(QObject *parent = nullptr, const QVariantList & = QVariantList()); ~PluginKateXMLTools() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; }; class PluginKateXMLToolsCompletionModel : public KTextEditor::CodeCompletionModel, public KTextEditor::CodeCompletionModelControllerInterface { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) public: PluginKateXMLToolsCompletionModel(QObject *parent); ~PluginKateXMLToolsCompletionModel() override; // // KTextEditor::CodeCompletionModel // public: int columnCount(const QModelIndex &) const override; int rowCount(const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &index) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QVariant data(const QModelIndex &idx, int role) const override; void executeCompletionItem(KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const override; // // KTextEditor::CodeCompletionModelControllerInterface // public: bool shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) override; public Q_SLOTS: void getDTD(); void slotInsertElement(); void slotCloseElement(); void slotFinished(KJob *job); void slotData(KIO::Job *, const QByteArray &data); void completionInvoked(KTextEditor::View *kv, const KTextEditor::Range &range, InvocationType invocationType) override; /// Connected to the document manager, to manage the dtd collection. void slotDocumentDeleted(KTextEditor::Document *doc); protected: QString currentModeToString() const; static QStringList sortQStringList(QStringList list); // bool eventFilter( QObject *object, QEvent *event ); QString insideTag(KTextEditor::View &kv); QString insideAttribute(KTextEditor::View &kv); static bool isOpeningTag(const QString &tag); static bool isClosingTag(const QString &tag); static bool isEmptyTag(const QString &tag); static bool isQuote(const QString &ch); QString getParentElement(KTextEditor::View &view, int skipCharacters); enum Mode { none, entities, attributevalues, attributes, elements, closingtag }; enum PopupMode { noPopup, tagname, attributename, attributevalue, entityname }; enum Level { groupNode = 1 }; /// Assign the PseudoDTD @p dtd to the Kate::View @p view void assignDTD(PseudoDTD *dtd, KTextEditor::View *view); /// temporary placeholder for the metaDTD file QString m_dtdString; /// temporary placeholder for the view to assign a DTD to while the file is loaded KTextEditor::View *m_viewToAssignTo; /// URL of the last loaded meta DTD QString m_urlString; QStringList m_allowed; Mode m_mode; int m_correctPos; // code completion stuff: - KTextEditor::CodeCompletionInterface *m_codeInterface; + KTextEditor::CodeCompletionInterface *m_codeInterface = nullptr; /// maps KTE::Document -> DTD QHash m_docDtds; /// maps DTD filename -> DTD QHash m_dtds; }; class PluginKateXMLToolsView : public QObject, public KXMLGUIClient { Q_OBJECT public: explicit PluginKateXMLToolsView(KTextEditor::MainWindow *mainWin); ~PluginKateXMLToolsView() override; protected: KTextEditor::MainWindow *m_mainWindow; PluginKateXMLToolsCompletionModel m_model; }; class InsertElement : public QDialog { Q_OBJECT public: InsertElement(const QStringList &completions, QWidget *parent); ~InsertElement() override; QString text() const; private Q_SLOTS: void slotHistoryTextChanged(const QString &); private: QComboBox *m_cmbElements; QPushButton *m_okButton; }; #endif // PLUGIN_KATEXMLTOOLS_H // kate: space-indent on; indent-width 4; replace-tabs on; mixed-indent off; diff --git a/kate/kateconfigplugindialogpage.cpp b/kate/kateconfigplugindialogpage.cpp index 0a66e1996..68daaa23e 100644 --- a/kate/kateconfigplugindialogpage.cpp +++ b/kate/kateconfigplugindialogpage.cpp @@ -1,125 +1,124 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger Copyright (C) 2007 Mirko Stocker This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateconfigplugindialogpage.h" #include "kateapp.h" #include "kateconfigdialog.h" #include "katepluginmanager.h" #include #include class KatePluginListItem : public QTreeWidgetItem { public: KatePluginListItem(bool checked, KatePluginInfo *info); KatePluginInfo *info() const { return mInfo; } protected: void stateChange(bool); private: KatePluginInfo *mInfo; }; KatePluginListItem::KatePluginListItem(bool checked, KatePluginInfo *info) - : QTreeWidgetItem() - , mInfo(info) + : mInfo(info) { setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); } KatePluginListView::KatePluginListView(QWidget *parent) : QTreeWidget(parent) { setRootIsDecorated(false); connect(this, &KatePluginListView::itemChanged, this, &KatePluginListView::stateChanged); } void KatePluginListView::stateChanged(QTreeWidgetItem *item) { emit stateChange(static_cast(item), item->checkState(0) == Qt::Checked); } KateConfigPluginPage::KateConfigPluginPage(QWidget *parent, KateConfigDialog *dialog) : QFrame(parent) , myDialog(dialog) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); KatePluginListView *listView = new KatePluginListView(this); layout->addWidget(listView); QStringList headers; headers << i18n("Name") << i18n("Description"); listView->setHeaderLabels(headers); listView->setWhatsThis(i18n("Here you can see all available Kate plugins. Those with a check mark are loaded, and will be loaded again the next time Kate is started.")); KatePluginList &pluginList(KateApp::self()->pluginManager()->pluginList()); for (auto &pluginInfo : pluginList) { QTreeWidgetItem *item = new KatePluginListItem(pluginInfo.load, &pluginInfo); item->setText(0, pluginInfo.metaData.name()); item->setText(1, pluginInfo.metaData.description()); listView->addTopLevelItem(item); } listView->resizeColumnToContents(0); listView->sortByColumn(0, Qt::AscendingOrder); connect(listView, &KatePluginListView::stateChange, this, &KateConfigPluginPage::stateChange); } void KateConfigPluginPage::stateChange(KatePluginListItem *item, bool b) { if (b) { loadPlugin(item); } else { unloadPlugin(item); } emit changed(); } void KateConfigPluginPage::loadPlugin(KatePluginListItem *item) { const bool ok = KateApp::self()->pluginManager()->loadPlugin(item->info()); if (!ok) { return; } KateApp::self()->pluginManager()->enablePluginGUI(item->info()); myDialog->addPluginPage(item->info()->plugin); item->setCheckState(0, Qt::Checked); } void KateConfigPluginPage::unloadPlugin(KatePluginListItem *item) { myDialog->removePluginPage(item->info()->plugin); KateApp::self()->pluginManager()->unloadPlugin(item->info()); item->setCheckState(0, Qt::Unchecked); } diff --git a/kate/katemainwindow.h b/kate/katemainwindow.h index 9e121baa6..2ae64c3c0 100644 --- a/kate/katemainwindow.h +++ b/kate/katemainwindow.h @@ -1,643 +1,643 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_MAINWINDOW_H__ #define __KATE_MAINWINDOW_H__ #include "katemdi.h" #include "kateviewmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class QMenu; namespace KIO { class UDSEntry; typedef class QList UDSEntryList; } class KFileItem; class KRecentFilesAction; class KateViewManager; class KateMwModOnHdDialog; class KateQuickOpen; enum KateQuickOpenModelList : int; // Helper layout class to always provide minimum size class KateContainerStackedLayout : public QStackedLayout { Q_OBJECT public: KateContainerStackedLayout(QWidget *parent); QSize sizeHint() const override; QSize minimumSize() const override; }; class KateMainWindow : public KateMDI::MainWindow, virtual public KParts::PartBase { Q_OBJECT public: /** * Construct the window and restore its state from given config if any * @param sconfig session config for this window, 0 if none * @param sgroup session config group to use */ KateMainWindow(KConfig *sconfig, const QString &sgroup); /** * Destruct the nice window */ ~KateMainWindow() override; /** * Accessor methodes for interface and child objects */ public: KateViewManager *viewManager() { return m_viewManager; } /** * KTextEditor::MainWindow wrapper * @return KTextEditor::MainWindow wrapper. */ KTextEditor::MainWindow *wrapper() { return m_wrapper; } public: /** Returns the URL of the current document. * anders: I add this for use from the file selector. */ QUrl activeDocumentUrl(); /** Enumeration to specify if files modified on disk should show up * in the reload dialog even if not edited in this instance. */ enum ModOnDiskMode { PromptEdited, ///< Do not list files that have not been edited PromptAll, ///< Include all files modified on disk }; /** * Prompts the user for what to do with files that are modified on disk if any. * This is optionally run when the window receives focus, and when the last * window is closed. * @return true if no documents are modified on disk, or all documents were * handled by the dialog; otherwise (the dialog was canceled) false. */ bool showModOnDiskPrompt(ModOnDiskMode mode); public: /*reimp*/ void readProperties(const KConfigGroup &config) override; /*reimp*/ void saveProperties(KConfigGroup &config) override; /*reimp*/ void saveGlobalProperties(KConfig *sessionConfig) override; void saveOpenRecent(KConfig *config); void loadOpenRecent(const KConfig *config); public: bool queryClose_internal(KTextEditor::Document *doc = nullptr); /** * save the settings, size and state of this window in * the provided config group */ void saveWindowConfig(const KConfigGroup &); /** * restore the settings, size and state of this window from * the provided config group. */ void restoreWindowConfig(const KConfigGroup &); /** * save some global options to katerc */ void saveOptions(); private: /** * Setup actions which pointers are needed already in setupMainWindow */ void setupImportantActions(); void setupMainWindow(); void setupActions(); bool queryClose() override; void addMenuBarActionToContextMenu(); void removeMenuBarActionFromContextMenu(); /** * read some global options from katerc */ void readOptions(); void dragEnterEvent(QDragEnterEvent *) override; void dropEvent(QDropEvent *) override; public Q_SLOTS: void slotFileClose(); void slotFileQuit(); void queueModifiedOnDisc(KTextEditor::Document *doc); void slotFocusPrevTab(); void slotFocusNextTab(); /** * Show quick open */ void slotQuickOpen(); /** * Overwrite size hint for better default window sizes * @return size hint */ QSize sizeHint() const override; /** * slots used for actions in the menus/toolbars * or internal signal connections */ private Q_SLOTS: void newWindow(); void slotConfigure(); void slotOpenWithMenuAction(QAction *a); void slotEditToolbars(); void slotNewToolbarConfig(); void slotUpdateOpenWith(); void slotUpdateActionsNeedingUrl(); void slotOpenDocument(const QUrl &); void slotDropEvent(QDropEvent *); void editKeys(); void mSlotFixOpenWithMenu(); void reloadXmlGui(); /* to update the caption */ void slotDocumentCreated(KTextEditor::Document *doc); void updateCaption(KTextEditor::Document *doc); // calls updateCaption(doc) with the current document void updateCaption(); void pluginHelp(); void aboutEditor(); void slotFullScreen(bool); void slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list); private Q_SLOTS: void toggleShowMenuBar(bool showMessage = true); void toggleShowStatusBar(); void toggleShowTabBar(); public: bool showStatusBar(); bool showTabBar(); Q_SIGNALS: void statusBarToggled(); void tabBarToggled(); void unhandledShortcutOverride(QEvent *e); public: void openUrl(const QString &name = QString()); QHash &pluginViews() { return m_pluginViews; } QWidget *bottomViewBarContainer() { return m_bottomViewBarContainer; } void addToBottomViewBarContainer(KTextEditor::View *view, QWidget *bar) { m_bottomContainerStack->addWidget(bar); m_bottomViewBarMapping[view] = BarState(bar); } void hideBottomViewBarForView(KTextEditor::View *view) { BarState &state = m_bottomViewBarMapping[view]; if (state.bar()) { m_bottomContainerStack->setCurrentWidget(state.bar()); state.bar()->hide(); state.setState(false); } m_bottomViewBarContainer->hide(); } void showBottomViewBarForView(KTextEditor::View *view) { BarState &state = m_bottomViewBarMapping[view]; if (state.bar()) { m_bottomContainerStack->setCurrentWidget(state.bar()); state.bar()->show(); state.setState(true); m_bottomViewBarContainer->show(); } } void deleteBottomViewBarForView(KTextEditor::View *view) { BarState state = m_bottomViewBarMapping.take(view); if (state.bar()) { if (m_bottomContainerStack->currentWidget() == state.bar()) { m_bottomViewBarContainer->hide(); } delete state.bar(); } } bool modNotificationEnabled() const { return m_modNotification; } void setModNotificationEnabled(bool e) { m_modNotification = e; } bool modCloseAfterLast() const { return m_modCloseAfterLast; } void setModCloseAfterLast(bool e) { m_modCloseAfterLast = e; } void setQuickOpenMatchMode(int mode); int quickOpenMatchMode(); void setQuickOpenListMode(KateQuickOpenModelList mode); KateQuickOpenModelList quickOpenListMode() const; KRecentFilesAction *fileOpenRecent() const { return m_fileOpenRecent; } // // KTextEditor::MainWindow interface, get called by invokeMethod from our wrapper object! // public Q_SLOTS: /** * get the toplevel widget. * \return the real main window widget. */ QWidget *window() { return this; } /** * Accessor to the XMLGUIFactory. * \return the mainwindow's KXMLGUIFactory. */ KXMLGUIFactory *guiFactory() override { return KateMDI::MainWindow::guiFactory(); } /** * Get a list of all views for this main window. * @return all views */ QList views() { return viewManager()->views(); } /** * Access the active view. * \return active view */ KTextEditor::View *activeView() { return viewManager()->activeView(); } /** * Activate the view with the corresponding \p document. * If none exist for this document, create one * \param document the document * \return activated view of this document */ KTextEditor::View *activateView(KTextEditor::Document *document) { return viewManager()->activateView(document); } /** * Open the document \p url with the given \p encoding. * \param url the document's url * \param encoding the preferred encoding. If encoding is QString() the * encoding will be guessed or the default encoding will be used. * \return a pointer to the created view for the new document, if a document * with this url is already existing, its view will be activated */ KTextEditor::View *openUrl(const QUrl &url, const QString &encoding = QString()) { return viewManager()->openUrlWithView(url, encoding); } /** * Close selected view * \param view the view * \return true if view was closed */ bool closeView(KTextEditor::View *view) { m_viewManager->closeView(view); return true; } /** * Close the split view where the given view is contained. * \param view the view. * \return true if the split view was closed. */ bool closeSplitView(KTextEditor::View *view) { m_viewManager->closeViewSpace(view); return true; } /** * @returns true if the two given views share the same split view, * false otherwise. */ bool viewsInSameSplitView(KTextEditor::View *view1, KTextEditor::View *view2) { return m_viewManager->viewsInSameViewSpace(view1, view2); } /** * Split current view space according to \p orientation * \param orientation in which line split the view */ void splitView(Qt::Orientation orientation) { m_viewManager->splitViewSpace(nullptr, orientation); } /** * Try to create a view bar for the given view. * Its parameter is the view for which we want a view bar * @return suitable widget that can host view bars widgets or nullptr */ QWidget *createViewBar(KTextEditor::View *) { return bottomViewBarContainer(); } /** * Delete the view bar for the given view. * @param view view for which we want an view bar */ void deleteViewBar(KTextEditor::View *view) { deleteBottomViewBarForView(view); } /** * Add a widget to the view bar. * @param view view for which the view bar is used * @param bar bar widget, shall have the viewBarParent() as parent widget */ void addWidgetToViewBar(KTextEditor::View *view, QWidget *bar) { addToBottomViewBarContainer(view, bar); } /** * Show the view bar for the given view * @param view view for which the view bar is used */ void showViewBar(KTextEditor::View *view) { showBottomViewBarForView(view); } /** * Hide the view bar for the given view * @param view view for which the view bar is used */ void hideViewBar(KTextEditor::View *view) { hideBottomViewBarForView(view); } /** * Create a new toolview with unique \p identifier at side \p pos * with \p icon and caption \p text. Use the returned widget to embed * your widgets. * \param plugin which owns this tool view * \param identifier unique identifier for this toolview * \param pos position for the toolview, if we are in session restore, * this is only a preference * \param icon icon to use in the sidebar for the toolview * \param text translated text (i18n()) to use in addition to icon * \return created toolview on success, otherwise NULL */ QWidget *createToolView(KTextEditor::Plugin *plugin, const QString &identifier, KTextEditor::MainWindow::ToolViewPosition pos, const QIcon &icon, const QString &text); /** * Move the toolview \p widget to position \p pos. * \param widget the toolview to move, where the widget was constructed * by createToolView(). * \param pos new position to move widget to * \return \e true on success, otherwise \e false */ bool moveToolView(QWidget *widget, KTextEditor::MainWindow::ToolViewPosition pos); /** * Show the toolview \p widget. * \param widget the toolview to show, where the widget was constructed * by createToolView(). * \return \e true on success, otherwise \e false * \todo add focus parameter: bool showToolView (QWidget *widget, bool giveFocus ); */ bool showToolView(QWidget *widget); /** * Hide the toolview \p widget. * \param widget the toolview to hide, where the widget was constructed * by createToolView(). * \return \e true on success, otherwise \e false */ bool hideToolView(QWidget *widget); /** * Shows the @p plugin's config page. The @p page specifies which * config page will be shown, see KTextEditor::Plugin::configPages(). * * \return \e true on success, otherwise \e false * \since 5.63 */ bool showPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id); /** * Get a plugin view for the plugin with with identifier \p name. * \param name the plugin's name * \return pointer to the plugin view if a plugin with \p name is loaded and has a view for this mainwindow, * otherwise NULL */ QObject *pluginView(const QString &name); private Q_SLOTS: void slotUpdateBottomViewBar(); private Q_SLOTS: void slotDocumentCloseAll(); void slotDocumentCloseOther(); void slotDocumentCloseOther(KTextEditor::Document *document); void slotDocumentCloseSelected(const QList &); private: /** * Notify about file modifications from other processes? */ - bool m_modNotification; + bool m_modNotification = false; /** * Shutdown Kate after last file is closed */ - bool m_modCloseAfterLast; + bool m_modCloseAfterLast = false; /** * stacked widget containing the central area, aka view manager, quickopen, ... */ - QStackedWidget *m_mainStackedWidget; + QStackedWidget *m_mainStackedWidget = nullptr; /** * quick open to fast switch documents */ - KateQuickOpen *m_quickOpen; + KateQuickOpen *m_quickOpen = nullptr; /** * keeps track of views */ - KateViewManager *m_viewManager; + KateViewManager *m_viewManager = nullptr; - KRecentFilesAction *m_fileOpenRecent; + KRecentFilesAction *m_fileOpenRecent = nullptr; - KActionMenu *documentOpenWith; + KActionMenu *documentOpenWith = nullptr; - KToggleAction *settingsShowFileselector; + KToggleAction *settingsShowFileselector = nullptr; - KToggleAction *m_showFullScreenAction; + KToggleAction *m_showFullScreenAction = nullptr; bool m_modignore; // all plugin views for this mainwindow, used by the pluginmanager QHash m_pluginViews; // options: show statusbar + show path - KToggleAction *m_paShowPath; - KToggleAction *m_paShowMenuBar; - KToggleAction *m_paShowStatusBar; - KToggleAction *m_paShowTabBar; + KToggleAction *m_paShowPath = nullptr; + KToggleAction *m_paShowMenuBar = nullptr; + KToggleAction *m_paShowStatusBar = nullptr; + KToggleAction *m_paShowTabBar = nullptr; - QWidget *m_bottomViewBarContainer; - KateContainerStackedLayout *m_bottomContainerStack; + QWidget *m_bottomViewBarContainer = nullptr; + KateContainerStackedLayout *m_bottomContainerStack = nullptr; class BarState { public: BarState() = default; BarState(QWidget *bar) : m_bar(bar) , m_state(false) { } ~BarState() { } QWidget *bar() { return m_bar; } bool state() { return m_state; } void setState(bool state) { m_state = state; } private: QWidget *m_bar = nullptr; bool m_state = false; }; QHash m_bottomViewBarMapping; public: static void unsetModifiedOnDiscDialogIfIf(KateMwModOnHdDialog *diag) { if (s_modOnHdDialog == diag) { s_modOnHdDialog = nullptr; } } private: static KateMwModOnHdDialog *s_modOnHdDialog; /** * Wrapper of main window for KTextEditor */ KTextEditor::MainWindow *m_wrapper; public Q_SLOTS: void slotWindowActivated(); protected: bool event(QEvent *e) override; void mousePressEvent(QMouseEvent *e) override; }; #endif diff --git a/kate/katemdi.h b/kate/katemdi.h index ff2ca8aef..22e3effeb 100644 --- a/kate/katemdi.h +++ b/kate/katemdi.h @@ -1,463 +1,463 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2005 Christoph Cullmann Copyright (C) 2002, 2003 Joseph Wenninger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_MDI_H__ #define __KATE_MDI_H__ #include #include #include #include #include #include #include #include #include #include #include #include class KActionMenu; class QAction; class QPixmap; class KConfigBase; namespace KTextEditor { class ConfigPageInterface; } namespace KateMDI { class ToolView; class ToggleToolViewAction : public KToggleAction { Q_OBJECT public: ToggleToolViewAction(const QString &text, ToolView *tv, QObject *parent); ~ToggleToolViewAction() override; protected Q_SLOTS: void slotToggled(bool) override; void toolVisibleChanged(bool); private: ToolView *m_tv; }; class GUIClient : public QObject, public KXMLGUIClient { Q_OBJECT public: GUIClient(class MainWindow *mw); ~GUIClient() override; void registerToolView(ToolView *tv); void unregisterToolView(ToolView *tv); void updateSidebarsVisibleAction(); private Q_SLOTS: void clientAdded(KXMLGUIClient *client); void updateActions(); private: MainWindow *m_mw; KToggleAction *m_showSidebarsAction; QList m_toolViewActions; QMap m_toolToAction; KActionMenu *m_toolMenu; }; class ToolView : public QFrame { Q_OBJECT friend class Sidebar; friend class MainWindow; friend class GUIClient; friend class ToggleToolViewAction; protected: /** * ToolView * Objects of this clas represent a toolview in the mainwindow * you should only add one widget as child to this toolview, it will * be automatically set to be the focus proxy of the toolview * @param mainwin main window for this toolview * @param sidebar sidebar of this toolview * @param parent parent widget, e.g. the splitter of one of the sidebars */ ToolView(class MainWindow *mainwin, class Sidebar *sidebar, QWidget *parent); public: /** * destruct me, this is allowed for all, will care itself that the toolview is removed * from the mainwindow and sidebar */ ~ToolView() override; Q_SIGNALS: /** * toolview hidden or shown * @param visible is this toolview made visible? */ void toolVisibleChanged(bool visible); /** * some internal methodes needed by the main window and the sidebars */ protected: MainWindow *mainWindow() { return m_mainWin; } Sidebar *sidebar() { return m_sidebar; } void setToolVisible(bool vis); public: bool toolVisible() const; QSize sizeHint() const override; QSize minimumSizeHint() const override; protected: void childEvent(QChildEvent *ev) override; void actionEvent(QActionEvent *event) override; private: MainWindow *m_mainWin; Sidebar *m_sidebar; KToolBar *m_toolbar; /// plugin this view belongs to, may be 0 QPointer plugin; /** * unique id */ QString id; /** * is visible in sidebar */ bool m_toolVisible; /** * is this view persistent? */ bool persistent; QIcon icon; QString text; }; class Sidebar : public KMultiTabBar { Q_OBJECT public: Sidebar(KMultiTabBar::KMultiTabBarPosition pos, class MainWindow *mainwin, QWidget *parent); ~Sidebar() override; void setSplitter(QSplitter *sp); public: ToolView *addWidget(const QIcon &icon, const QString &text, ToolView *widget); bool removeWidget(ToolView *widget); bool showWidget(ToolView *widget); bool hideWidget(ToolView *widget); void setLastSize(int s) { m_lastSize = s; } int lastSize() const { return m_lastSize; } void updateLastSize(); bool splitterVisible() const { return m_ownSplit->isVisible(); } void restoreSession(); /** * restore the current session config from given object, use current group * @param config config object to use */ void restoreSession(KConfigGroup &config); /** * save the current session config to given object, use current group * @param config config object to use */ void saveSession(KConfigGroup &config); public Q_SLOTS: // reimplemented, to block a show() call if all sidebars are forced hidden void setVisible(bool visible) override; private Q_SLOTS: void tabClicked(int); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private Q_SLOTS: void buttonPopupActivate(QAction *); private: MainWindow *m_mainWin; - KMultiTabBar::KMultiTabBarPosition m_pos; + KMultiTabBar::KMultiTabBarPosition m_pos{}; QSplitter *m_splitter; - KMultiTabBar *m_tabBar; + KMultiTabBar *m_tabBar = nullptr; QSplitter *m_ownSplit; QMap m_idToWidget; QMap m_widgetToId; QMap m_widgetToSize; /** * list of all toolviews around in this sidebar */ QList m_toolviews; int m_lastSize; QSize m_preHideSize; - int m_popupButton; + int m_popupButton = 0; Q_SIGNALS: void sigShowPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id); }; class MainWindow : public KParts::MainWindow { Q_OBJECT friend class ToolView; // // Constructor area // public: /** * Constructor */ MainWindow(QWidget *parentWidget = nullptr); /** * Destructor */ ~MainWindow() override; // // public interfaces // /** * add a given widget to the given sidebar if possible, name is very important * @param plugin pointer to the plugin * @param identifier unique identifier for this toolview * @param pos position for the toolview, if we are in session restore, this is only a preference * @param icon icon to use for the toolview * @param text text to use in addition to icon * @return created toolview on success or 0 */ ToolView *createToolView(KTextEditor::Plugin *plugin, const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QIcon &icon, const QString &text); /** * give you handle to toolview for the given name, 0 if no toolview around * @param identifier toolview name * @return toolview if existing, else 0 */ ToolView *toolView(const QString &identifier) const; /** * set the toolview's tabbar style. * @param style the tabbar style. */ void setToolViewStyle(KMultiTabBar::KMultiTabBarStyle style); /** * get the toolview's tabbar style. Call this before @p startRestore(), * otherwise you overwrite the usersettings. * @return toolview's tabbar style */ KMultiTabBar::KMultiTabBarStyle toolViewStyle() const; /** * get the sidebars' visibility. * @return false, if the sidebars' visibility is forced hidden, otherwise true */ bool sidebarsVisible() const; public Q_SLOTS: /** * set the sidebars' visibility to @p visible. If false, the sidebars * are @e always hidden. Usually you do not have to call this because * the user can set this in the menu. * @param visible sidebars visibility */ void setSidebarsVisible(bool visible); protected: /** * called by toolview destructor * @param widget toolview which is destroyed */ void toolViewDeleted(ToolView *widget); /** * central widget ;) * use this as parent for your content * this widget will get focus if a toolview is hidden * @return central widget */ QWidget *centralWidget() const; /** * modifiers for existing toolviews */ public: /** * move a toolview around * @param widget toolview to move * @param pos position to move too, during session restore, only preference * @return success */ bool moveToolView(ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos); /** * show given toolview, discarded while session restore * @param widget toolview to show * @return success */ bool showToolView(ToolView *widget); /** * hide given toolview, discarded while session restore * @param widget toolview to hide * @return success */ bool hideToolView(ToolView *widget); /** * session saving and restore stuff */ public: /** * start the restore * @param config config object to use * @param group config group to use */ void startRestore(KConfigBase *config, const QString &group); /** * finish the restore */ void finishRestore(); /** * save the current session config to given object and group * @param group config group to use */ void saveSession(KConfigGroup &group); /** * internal data ;) */ private: /** * map identifiers to widgets */ QMap m_idToWidget; /** * list of all toolviews around */ QList m_toolviews; /** * widget, which is the central part of the * main window ;) */ QWidget *m_centralWidget; /** * horizontal splitter */ QSplitter *m_hSplitter; /** * vertical splitter */ QSplitter *m_vSplitter; /** * sidebars for the four sides */ - Sidebar *m_sidebars[4]; + Sidebar *m_sidebars[4]{}; /** * sidebars state. */ bool m_sidebarsVisible = true; /** * config object for session restore, only valid between * start and finish restore calls */ KConfigBase *m_restoreConfig = nullptr; /** * restore group */ QString m_restoreGroup; /** * out guiclient */ GUIClient *m_guiClient; Q_SIGNALS: void sigShowPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id); }; } #endif diff --git a/kate/katequickopenmodel.h b/kate/katequickopenmodel.h index e7a3b25e0..df26e9fe9 100644 --- a/kate/katequickopenmodel.h +++ b/kate/katequickopenmodel.h @@ -1,75 +1,75 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2018 Tomaz Canabrava This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KATEQUICKOPENMODEL_H #define KATEQUICKOPENMODEL_H #include #include #include #include #include "katemainwindow.h" struct ModelEntry { QUrl url; // used for actually opening a selected file (local or remote) QString fileName; // display string for left column QString filePath; // display string for right column bool bold; // format line in bold text or not size_t sort_id; }; // needs to be defined outside of class to support forward declaration elsewhere enum KateQuickOpenModelList : int { CurrentProject, AllProjects }; class KateQuickOpenModel : public QAbstractTableModel { Q_OBJECT public: enum Columns : int { FileName, FilePath, Bold }; explicit KateQuickOpenModel(KateMainWindow *mainWindow, QObject *parent = nullptr); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &idx, int role) const override; void refresh(); // add a convenient in-class alias using List = KateQuickOpenModelList; List listMode() const { return m_listMode; } void setListMode(List mode) { m_listMode = mode; } private: QVector m_modelEntries; /* TODO: don't rely in a pointer to the main window. * this is bad engineering, but current code is too tight * on this and it's hard to untangle without breaking existing * code. */ KateMainWindow *m_mainWindow; - List m_listMode; + List m_listMode{}; }; #endif diff --git a/kate/katesavemodifieddialog.cpp b/kate/katesavemodifieddialog.cpp index 33de0e749..42eb72d6c 100644 --- a/kate/katesavemodifieddialog.cpp +++ b/kate/katesavemodifieddialog.cpp @@ -1,254 +1,253 @@ /* This file is part of the KDE project Copyright (C) 2004 Joseph Wenninger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katesavemodifieddialog.h" #include "katedebug.h" #include #include #include #include #include #include #include #include #include class AbstractKateSaveModifiedDialogCheckListItem : public QTreeWidgetItem { public: AbstractKateSaveModifiedDialogCheckListItem(const QString &title, const QString &url) - : QTreeWidgetItem() { setFlags(flags() | Qt::ItemIsUserCheckable); setText(0, title); setText(1, url); setCheckState(0, Qt::Checked); setState(InitialState); } ~AbstractKateSaveModifiedDialogCheckListItem() override { } virtual bool synchronousSave(QWidget *dialogParent) = 0; enum STATE { InitialState, SaveOKState, SaveFailedState }; STATE state() const { return m_state; } void setState(enum STATE state) { m_state = state; switch (state) { case InitialState: setIcon(0, QIcon()); break; case SaveOKState: setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-ok"))); // QStringLiteral("ok") icon should probably be QStringLiteral("dialog-success"), but we don't have that icon in KDE 4.0 break; case SaveFailedState: setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-error"))); break; } } private: - STATE m_state; + STATE m_state = InitialState; }; class KateSaveModifiedDocumentCheckListItem : public AbstractKateSaveModifiedDialogCheckListItem { public: KateSaveModifiedDocumentCheckListItem(KTextEditor::Document *document) : AbstractKateSaveModifiedDialogCheckListItem(document->documentName(), document->url().toString()) { m_document = document; } ~KateSaveModifiedDocumentCheckListItem() override { } bool synchronousSave(QWidget *dialogParent) override { if (m_document->url().isEmpty()) { const QUrl url = QFileDialog::getSaveFileUrl(dialogParent, i18n("Save As (%1)", m_document->documentName())); if (!url.isEmpty()) { if (!m_document->saveAs(url)) { setState(SaveFailedState); setText(1, m_document->url().toString()); return false; } else { bool sc = m_document->waitSaveComplete(); setText(1, m_document->url().toString()); if (!sc) { setState(SaveFailedState); return false; } else { setState(SaveOKState); return true; } } } else { // setState(SaveFailedState); return false; } } else { // document has an existing location if (!m_document->save()) { setState(SaveFailedState); setText(1, m_document->url().toString()); return false; } else { bool sc = m_document->waitSaveComplete(); setText(1, m_document->url().toString()); if (!sc) { setState(SaveFailedState); return false; } else { setState(SaveOKState); return true; } } } return false; } private: KTextEditor::Document *m_document; }; KateSaveModifiedDialog::KateSaveModifiedDialog(QWidget *parent, const QList &documents) : QDialog(parent) { setWindowTitle(i18n("Save Documents")); setObjectName(QStringLiteral("KateSaveModifiedDialog")); setModal(true); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); // label QLabel *lbl = new QLabel(i18n("The following documents have been modified. Do you want to save them before closing?"), this); mainLayout->addWidget(lbl); // main view m_list = new QTreeWidget(this); mainLayout->addWidget(m_list); m_list->setColumnCount(2); m_list->setHeaderLabels(QStringList() << i18n("Documents") << i18n("Location")); m_list->setRootIsDecorated(true); for (KTextEditor::Document *doc : documents) { m_list->addTopLevelItem(new KateSaveModifiedDocumentCheckListItem(doc)); } m_list->resizeColumnToContents(0); connect(m_list, &QTreeWidget::itemChanged, this, &KateSaveModifiedDialog::slotItemActivated); QPushButton *selectAllButton = new QPushButton(i18n("Se&lect All"), this); mainLayout->addWidget(selectAllButton); connect(selectAllButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotSelectAll); // dialog buttons QDialogButtonBox *buttons = new QDialogButtonBox(this); mainLayout->addWidget(buttons); m_saveButton = new QPushButton; KGuiItem::assign(m_saveButton, KStandardGuiItem::save()); buttons->addButton(m_saveButton, QDialogButtonBox::YesRole); connect(m_saveButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotSaveSelected); QPushButton *discardButton = new QPushButton; KGuiItem::assign(discardButton, KStandardGuiItem::discard()); buttons->addButton(discardButton, QDialogButtonBox::NoRole); connect(discardButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotDoNotSave); QPushButton *cancelButton = new QPushButton; KGuiItem::assign(cancelButton, KStandardGuiItem::cancel()); cancelButton->setDefault(true); cancelButton->setFocus(); buttons->addButton(cancelButton, QDialogButtonBox::RejectRole); connect(cancelButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::reject); } KateSaveModifiedDialog::~KateSaveModifiedDialog() { } void KateSaveModifiedDialog::slotItemActivated(QTreeWidgetItem *, int) { bool enableSaveButton = false; for (int i = 0; i < m_list->topLevelItemCount(); ++i) { if (m_list->topLevelItem(i)->checkState(0) == Qt::Checked) { enableSaveButton = true; break; } } m_saveButton->setEnabled(enableSaveButton); } void KateSaveModifiedDialog::slotSelectAll() { for (int i = 0; i < m_list->topLevelItemCount(); ++i) { m_list->topLevelItem(i)->setCheckState(0, Qt::Checked); } m_saveButton->setEnabled(true); } void KateSaveModifiedDialog::slotSaveSelected() { if (doSave()) { done(QDialog::Accepted); } } void KateSaveModifiedDialog::slotDoNotSave() { done(QDialog::Accepted); } bool KateSaveModifiedDialog::doSave() { for (int i = 0; i < m_list->topLevelItemCount(); ++i) { AbstractKateSaveModifiedDialogCheckListItem *cit = static_cast(m_list->topLevelItem(i)); if (cit->checkState(0) == Qt::Checked && (cit->state() != AbstractKateSaveModifiedDialogCheckListItem::SaveOKState)) { if (!cit->synchronousSave(this /*perhaps that should be the kate mainwindow*/)) { if (cit->state() == AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState) { KMessageBox::sorry(this, i18n("Data you requested to be saved could not be written. Please choose how you want to proceed.")); } return false; } } else if ((cit->checkState(0) != Qt::Checked) && (cit->state() == AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState)) { cit->setState(AbstractKateSaveModifiedDialogCheckListItem::InitialState); } } return true; } bool KateSaveModifiedDialog::queryClose(QWidget *parent, const QList &documents) { KateSaveModifiedDialog d(parent, documents); return (d.exec() != QDialog::Rejected); } diff --git a/kate/katesavemodifieddialog.h b/kate/katesavemodifieddialog.h index 37d2ab4c2..9cfb7b53a 100644 --- a/kate/katesavemodifieddialog.h +++ b/kate/katesavemodifieddialog.h @@ -1,54 +1,54 @@ /* This file is part of the KDE project Copyright (C) 2004 Joseph Wenninger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KATE_SAVE_MODIFIED_DIALOG_ #define _KATE_SAVE_MODIFIED_DIALOG_ #include #include #include class QTreeWidget; class QTreeWidgetItem; class QPushButton; class KateSaveModifiedDialog : public QDialog { Q_OBJECT public: KateSaveModifiedDialog(QWidget *parent, const QList &documents); ~KateSaveModifiedDialog() override; static bool queryClose(QWidget *parent, const QList &documents); protected: bool doSave(); protected Q_SLOTS: void slotSelectAll(); void slotItemActivated(QTreeWidgetItem *, int); void slotSaveSelected(); void slotDoNotSave(); private: - QTreeWidgetItem *m_documentRoot; + QTreeWidgetItem *m_documentRoot = nullptr; QTreeWidget *m_list; QPushButton *m_saveButton; }; #endif diff --git a/kate/katetabbar.cpp b/kate/katetabbar.cpp index bf49b6d3e..d530ec0e5 100644 --- a/kate/katetabbar.cpp +++ b/kate/katetabbar.cpp @@ -1,594 +1,594 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2014 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katetabbar.h" #include "katetabbutton.h" #include // ceil // #include #include #include #include #include #include class KateTabBarPrivate { public: // pointer to tabbar - KateTabBar *q; + KateTabBar *q = nullptr; // minimum and maximum tab width - int minimumTabWidth; - int maximumTabWidth; + int minimumTabWidth = 0; + int maximumTabWidth = 0; // current tab width: when closing tabs with the mouse, we keep // the tab width fixed until the mouse leaves the tab bar. This // way the user can keep clicking the close button without moving // the ouse. - qreal currentTabWidth; - bool keepTabWidth; + qreal currentTabWidth = 0; + bool keepTabWidth = false; - bool isActive; + bool isActive = false; QVector tabButtons; QHash idToTab; - KateTabButton *activeButton; + KateTabButton *activeButton = nullptr; - int nextID; + int nextID = 0; public: // functions /** * Set tab geometry. The tabs are animated only if @p animate is @e true. */ void updateButtonPositions(bool animate = false) { // if there are no tabs there is nothing to do if (tabButtons.count() == 0) { return; } // check whether an animation is still running, in that case, // continue animations to avoid jumping tabs const int maxi = tabButtons.size(); if (!animate) { for (int i = 0; i < maxi; ++i) { if (tabButtons.value(i)->geometryAnimationRunning()) { animate = true; break; } } } const int barWidth = q->width(); const int maxCount = q->maxTabCount(); // how many tabs do we show? const int visibleTabCount = qMin(q->count(), maxCount); // new tab width of each tab qreal tabWidth; const bool keepWidth = keepTabWidth && ceil(currentTabWidth) * visibleTabCount < barWidth; if (keepWidth) { // only keep tab width if the tabs still fit tabWidth = currentTabWidth; } else { tabWidth = qMin(static_cast(barWidth) / visibleTabCount, static_cast(maximumTabWidth)); } // if the last tab was closed through the close button, make sure the // close button of the new tab is again under the mouse if (keepWidth) { const int xPos = q->mapFromGlobal(QCursor::pos()).x(); if (tabWidth * visibleTabCount < xPos) { tabWidth = qMin(static_cast(tabWidth * (visibleTabCount + 1.0)) / visibleTabCount, static_cast(maximumTabWidth)); } } // now save the current tab width for future adaptation currentTabWidth = tabWidth; // now set the sizes const int w = ceil(tabWidth); const int h = q->height(); for (int i = 0; i < maxi; ++i) { KateTabButton *tabButton = tabButtons.value(i); if (i >= maxCount) { tabButton->hide(); } else { QRect endGeometry(i * tabWidth, 0, w, h); if (i > 0) { // make sure the tab button starts exactly next to the previous tab (avoid rounding errors) endGeometry.setLeft((i - 1) * tabWidth + w); } if (animate) { const QRect startGeometry = tabButton->isVisible() ? tabButton->geometry() : QRect(i * tabWidth, 0, 0, h); tabButton->setAnimatedGeometry(startGeometry, endGeometry); } else { // two times endGeometry. Takes care of stopping a running animation tabButton->setAnimatedGeometry(endGeometry, endGeometry); } tabButton->show(); } } } }; /** * Creates a new tab bar with the given \a parent. */ KateTabBar::KateTabBar(QWidget *parent) : QWidget(parent) , d(new KateTabBarPrivate()) { d->q = this; d->minimumTabWidth = 150; d->maximumTabWidth = 350; d->currentTabWidth = 350; d->keepTabWidth = false; d->isActive = false; d->activeButton = nullptr; d->nextID = 0; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); setAcceptDrops(true); } /** * Destroys the tab bar. */ KateTabBar::~KateTabBar() { delete d; } void KateTabBar::setActive(bool active) { if (active != d->isActive) { d->isActive = active; update(); } } bool KateTabBar::isActive() const { return d->isActive; } int KateTabBar::addTab(const QString &text) { return insertTab(d->tabButtons.size(), text); } int KateTabBar::insertTab(int position, const QString &text) { Q_ASSERT(position <= d->tabButtons.size()); // -1 is append if (position < 0) { position = d->tabButtons.size(); } KateTabButton *tabButton = new KateTabButton(text, this); d->tabButtons.insert(position, tabButton); d->idToTab[d->nextID] = tabButton; connect(tabButton, &KateTabButton::activated, this, &KateTabBar::tabButtonActivated); connect(tabButton, &KateTabButton::closeRequest, this, &KateTabBar::tabButtonCloseRequest); // abort potential keeping of width d->keepTabWidth = false; d->updateButtonPositions(true); return d->nextID++; } int KateTabBar::currentTab() const { return d->idToTab.key(d->activeButton, -1); } void KateTabBar::setCurrentTab(int id) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (d->activeButton == tabButton) { return; } if (d->activeButton) { d->activeButton->setChecked(false); } d->activeButton = tabButton; if (d->activeButton) { d->activeButton->setChecked(true); } } int KateTabBar::prevTab() const { const int curId = currentTab(); if (curId >= 0) { KateTabButton *tabButton = d->idToTab.value(curId, nullptr); const int index = d->tabButtons.indexOf(tabButton); Q_ASSERT(index >= 0); if (index > 0) { return d->idToTab.key(d->tabButtons[index - 1], -1); } else if (count() > 1) { // cycle through tabbar return d->idToTab.key(d->tabButtons.last(), -1); } } return -1; } int KateTabBar::nextTab() const { const int curId = currentTab(); if (curId >= 0) { KateTabButton *tabButton = d->idToTab.value(curId, nullptr); const int index = d->tabButtons.indexOf(tabButton); Q_ASSERT(index >= 0); if (index < d->tabButtons.size() - 1) { return d->idToTab.key(d->tabButtons[index + 1], -1); } else if (count() > 1) { // cycle through tabbar return d->idToTab.key(d->tabButtons.first(), -1); } } return -1; } int KateTabBar::removeTab(int id) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton == d->activeButton) { d->activeButton = Q_NULLPTR; } const int position = d->tabButtons.indexOf(tabButton); if (position != -1) { d->idToTab.remove(id); d->tabButtons.removeAt(position); } if (tabButton) { // delete the button with deleteLater() because the button itself might // have send a close-request. So the app-execution is still in the // button, a delete tabButton; would lead to a crash. tabButton->hide(); tabButton->deleteLater(); } d->updateButtonPositions(true); return position; } bool KateTabBar::containsTab(int id) const { return d->idToTab.contains(id); } void KateTabBar::setTabText(int id, const QString &text) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { tabButton->setText(text); } } QString KateTabBar::tabText(int id) const { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { return tabButton->text(); } return QString(); } void KateTabBar::setTabToolTip(int id, const QString &tip) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { tabButton->setToolTip(tip); } } QString KateTabBar::tabToolTip(int id) const { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { return tabButton->toolTip(); } return QString(); } void KateTabBar::setTabUrl(int id, const QUrl &url) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { tabButton->setUrl(url); } } QUrl KateTabBar::tabUrl(int id) const { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { return tabButton->url(); } return QUrl(); } void KateTabBar::setTabIcon(int id, const QIcon &icon) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { tabButton->setIcon(icon); } } QIcon KateTabBar::tabIcon(int id) const { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { return tabButton->icon(); } return QIcon(); } int KateTabBar::count() const { return d->tabButtons.count(); } void KateTabBar::tabButtonActivated(KateTabButton *tabButton) { if (!tabButton) { return; } if (tabButton == d->activeButton) { // make sure we are the currently active view space if (!isActive()) { emit activateViewSpaceRequested(); } return; } if (d->activeButton) { d->activeButton->setChecked(false); } d->activeButton = tabButton; d->activeButton->setChecked(true); const int id = d->idToTab.key(d->activeButton, -1); Q_ASSERT(id >= 0); if (id >= 0) { emit currentChanged(id); } } void KateTabBar::tabButtonCloseRequest(KateTabButton *tabButton) { // keep width if (underMouse()) { d->keepTabWidth = true; } const int id = d->idToTab.key(tabButton, -1); Q_ASSERT(id >= 0); if (id >= 0) { emit closeTabRequested(id); } } void KateTabBar::resizeEvent(QResizeEvent *event) { Q_UNUSED(event) // fix button positions if (!d->keepTabWidth || event->size().width() < event->oldSize().width()) { d->updateButtonPositions(); } const int tabDiff = maxTabCount() - d->tabButtons.size(); if (tabDiff > 0) { emit moreTabsRequested(tabDiff); } else if (tabDiff < 0) { emit lessTabsRequested(-tabDiff); } } int KateTabBar::maxTabCount() const { return qMax(1, width() / d->minimumTabWidth); } void KateTabBar::mouseDoubleClickEvent(QMouseEvent *event) { event->accept(); emit newTabRequested(); } void KateTabBar::mousePressEvent(QMouseEvent *event) { if (!isActive()) { emit activateViewSpaceRequested(); } QWidget::mousePressEvent(event); } void KateTabBar::leaveEvent(QEvent *event) { if (d->keepTabWidth) { d->keepTabWidth = false; d->updateButtonPositions(true); } QWidget::leaveEvent(event); } void KateTabBar::paintEvent(QPaintEvent *event) { Q_UNUSED(event) const int buttonCount = d->tabButtons.size(); if (buttonCount < 1) { return; } // draw separators QStyleOption option; option.initFrom(d->tabButtons[0]); option.state |= QStyle::State_Horizontal; const int w = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, nullptr, this); const int offset = w / 2; option.rect.setWidth(w); option.rect.moveTop(0); QPainter painter(this); // first separator option.rect.moveLeft(d->tabButtons[0]->geometry().left() - offset); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &option, &painter); // all other separators for (int i = 0; i < buttonCount; ++i) { option.rect.moveLeft(d->tabButtons[i]->geometry().right() - offset); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &option, &painter); } } void KateTabBar::contextMenuEvent(QContextMenuEvent *ev) { int id = -1; for (KateTabButton *button : qAsConst(d->tabButtons)) { if (button->rect().contains(button->mapFromGlobal(ev->globalPos()))) { id = d->idToTab.key(button, -1); Q_ASSERT(id >= 0); break; } } if (id >= 0) { emit contextMenuRequest(id, ev->globalPos()); } } void KateTabBar::wheelEvent(QWheelEvent *event) { event->accept(); // cycle through the tabs const int delta = event->angleDelta().x() + event->angleDelta().y(); const int id = (delta > 0) ? prevTab() : nextTab(); if (id >= 0) { Q_ASSERT(d->idToTab.contains(id)); KateTabButton *tabButton = d->idToTab.value(id, nullptr); if (tabButton) { tabButtonActivated(tabButton); } } } void KateTabBar::dragEnterEvent(QDragEnterEvent *event) { const bool sameApplication = event->source() != nullptr; if (sameApplication && event->mimeData()->hasFormat(QStringLiteral("application/x-dndkatetabbutton"))) { if (event->source()->parent() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { // possibly dragged from another tabbar (not yet implemented) event->ignore(); } } else { event->ignore(); } } void KateTabBar::dragMoveEvent(QDragMoveEvent *event) { // first of all, make sure the dragged button is from this tabbar auto button = qobject_cast(event->source()); if (!button) { event->ignore(); return; } // save old tab position const int oldButtonIndex = d->tabButtons.indexOf(button); d->tabButtons.removeAt(oldButtonIndex); // find new position const qreal currentPos = event->pos().x(); int index = d->tabButtons.size(); for (int i = 0; i < d->tabButtons.size(); ++i) { auto tab = d->tabButtons[i]; if (tab->geometry().center().x() > currentPos) { index = i; break; } } d->tabButtons.insert(index, button); // trigger rearrange if required if (oldButtonIndex != index) { d->updateButtonPositions(true); } event->accept(); } void KateTabBar::dropEvent(QDropEvent *event) { // no action required event->acceptProposedAction(); } diff --git a/kate/kateviewmanager.h b/kate/kateviewmanager.h index 31c604f9b..268b18503 100644 --- a/kate/kateviewmanager.h +++ b/kate/kateviewmanager.h @@ -1,346 +1,346 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_VIEWMANAGER_H__ #define __KATE_VIEWMANAGER_H__ #include "katedocmanager.h" #include #include #include #include namespace KActivities { class ResourceInstance; } namespace KTextEditor { class View; class Document; } class KateDocumentInfo; class KConfigGroup; class KConfigBase; class KateMainWindow; class KateViewSpace; class KateViewManager : public QSplitter { Q_OBJECT public: KateViewManager(QWidget *parentW, KateMainWindow *parent); ~KateViewManager() override; private: /** * create all actions needed for the view manager */ void setupActions(); void updateViewSpaceActions(); public: /* This will save the splitter configuration */ void saveViewConfiguration(KConfigGroup &group); /* restore it */ void restoreViewConfiguration(const KConfigGroup &group); KTextEditor::Document *openUrl(const QUrl &url, const QString &encoding, bool activate = true, bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); KTextEditor::Document *openUrls(const QList &url, const QString &encoding, bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); KTextEditor::View *openUrlWithView(const QUrl &url, const QString &encoding); public Q_SLOTS: void openUrl(const QUrl &url); public: void closeView(KTextEditor::View *view); KateMainWindow *mainWindow(); private Q_SLOTS: void activateView(KTextEditor::View *view); void activateSpace(KTextEditor::View *v); public Q_SLOTS: void slotDocumentNew(); void slotDocumentOpen(); void slotDocumentClose(); void slotDocumentClose(KTextEditor::Document *document); void setActiveSpace(KateViewSpace *vs); void setActiveView(KTextEditor::View *view); void activateNextView(); void activatePrevView(); Q_SIGNALS: void viewChanged(KTextEditor::View *); void viewCreated(KTextEditor::View *); public: /** * create and activate a new view for doc, if doc == 0, then * create a new document. * Can return NULL. */ KTextEditor::View *createView(KTextEditor::Document *doc = nullptr, KateViewSpace *vs = nullptr); private: bool deleteView(KTextEditor::View *view); void moveViewtoSplit(KTextEditor::View *view); void moveViewtoStack(KTextEditor::View *view); /* Save the configuration of a single splitter. * If child splitters are found, it calls it self with those as the argument. * If a viewspace child is found, it is asked to save its filelist. */ QString saveSplitterConfig(QSplitter *s, KConfigBase *config, const QString &viewConfGrp); /** Restore a single splitter. * This is all the work is done for @see saveSplitterConfig() */ void restoreSplitter(const KConfigBase *config, const QString &group, QSplitter *parent, const QString &viewConfGrp); void removeViewSpace(KateViewSpace *viewspace); public: KTextEditor::View *activeView(); KateViewSpace *activeViewSpace(); private Q_SLOTS: void slotViewChanged(); void documentCreated(KTextEditor::Document *doc); void documentWillBeDeleted(KTextEditor::Document *doc); void documentSavedOrUploaded(KTextEditor::Document *document, bool saveAs); /** * This signal is emitted before the documents batch is going to be deleted * * note that the batch can be interrupted in the middle and only some * of the documents may be actually deleted. See documentsDeleted() signal. * * @param documents documents we want to delete, may not be deleted */ void aboutToDeleteDocuments(const QList &documents); /** * This signal is emitted after the documents batch was deleted * * This is the batch closing signal for aboutToDeleteDocuments * @param documents the documents that weren't deleted after all */ void documentsDeleted(const QList &documents); public Q_SLOTS: /** * Splits a KateViewSpace into two in the following steps: * 1. create a QSplitter in the parent of the KateViewSpace to be split * 2. move the to-be-split KateViewSpace to the new splitter * 3. create new KateViewSpace and added to the new splitter * 4. create KateView to populate the new viewspace. * 5. The new KateView is made the active one, because createView() does that. * If no viewspace is provided, the result of activeViewSpace() is used. * The orientation of the new splitter is determined by the value of o. * Note: horizontal splitter means vertically aligned views. */ void splitViewSpace(KateViewSpace *vs = nullptr, Qt::Orientation o = Qt::Horizontal); /** * Close the view space that contains the given view. If no view was * given, then the active view space will be closed instead. */ void closeViewSpace(KTextEditor::View *view = nullptr); /** * @returns true of the two given views share the same view space. */ bool viewsInSameViewSpace(KTextEditor::View *view1, KTextEditor::View *view2); /** * activate view for given document * @param doc document to activate view for */ KTextEditor::View *activateView(KTextEditor::Document *doc); /** Splits the active viewspace horizontally */ void slotSplitViewSpaceHoriz() { splitViewSpace(nullptr, Qt::Vertical); } /** Splits the active viewspace vertically */ void slotSplitViewSpaceVert() { splitViewSpace(); } /** moves the splitter according to the key that has been pressed */ void moveSplitter(Qt::Key key, int repeats = 1); /** moves the splitter to the right */ void moveSplitterRight() { moveSplitter(Qt::Key_Right); } /** moves the splitter to the left */ void moveSplitterLeft() { moveSplitter(Qt::Key_Left); } /** moves the splitter up */ void moveSplitterUp() { moveSplitter(Qt::Key_Up); } /** moves the splitter down */ void moveSplitterDown() { moveSplitter(Qt::Key_Down); } /** closes the current view space. */ void slotCloseCurrentViewSpace() { closeViewSpace(); } /** closes every view but the active one */ void slotCloseOtherViews(); /** hide every view but the active one */ void slotHideOtherViews(bool hideOthers); void reactivateActiveView(); /** * Toogle the orientation of current split view */ void toggleSplitterOrientation(); /** * Get a list of all views. * @return all views */ QList views() const { return m_views.keys(); } /** * get views in lru order * @return views in lru order */ QList sortedViews() const { QMap sortedViews; QHashIterator i(m_views); while (i.hasNext()) { i.next(); sortedViews[i.value().lruAge] = i.key(); } return sortedViews.values(); } private: KateMainWindow *m_mainWindow; bool m_init; - QAction *m_splitViewVert; - QAction *m_splitViewHoriz; - QAction *m_closeView; - QAction *m_closeOtherViews; - QAction *m_toggleSplitterOrientation; - QAction *m_hideOtherViews; - QAction *goNext; - QAction *goPrev; + QAction *m_splitViewVert = nullptr; + QAction *m_splitViewHoriz = nullptr; + QAction *m_closeView = nullptr; + QAction *m_closeOtherViews = nullptr; + QAction *m_toggleSplitterOrientation = nullptr; + QAction *m_hideOtherViews = nullptr; + QAction *goNext = nullptr; + QAction *goPrev = nullptr; QList m_viewSpaceList; bool m_blockViewCreationAndActivation; bool m_activeViewRunning; - int m_splitterIndex; // used during saving splitter config. + int m_splitterIndex = 0; // used during saving splitter config. /** * View meta data */ class ViewData { public: /** * Default constructor */ ViewData() = default; /** * view active? */ bool active = false; /** * lru age of the view * important: smallest age ==> latest used view */ qint64 lruAge = 0; /** * activity resource for the view */ KActivities::ResourceInstance *activityResource = nullptr; }; /** * central storage of all views known in the view manager * maps the view to meta data */ QHash m_views; /** * current minimal age */ qint64 m_minAge; /** * the view that is ATM merged to the xml gui factory */ QPointer m_guiMergedView; /** * last url of open file dialog, used if current document has no valid url */ QUrl m_lastOpenDialogUrl; }; #endif diff --git a/kate/session/katesession.cpp b/kate/session/katesession.cpp index 82a74bcf0..a051a185c 100644 --- a/kate/session/katesession.cpp +++ b/kate/session/katesession.cpp @@ -1,129 +1,128 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2005 Christoph Cullmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katesession.h" #include "katedebug.h" #include "katesessionmanager.h" #include #include #include #include #include static const QLatin1String opGroupName("Open Documents"); static const QLatin1String keyCount("Count"); KateSession::KateSession(const QString &file, const QString &name, const bool anonymous, const KConfig *_config) : m_name(name) , m_file(file) , m_anonymous(anonymous) , m_documents(0) , m_config(nullptr) - , m_timestamp() { Q_ASSERT(!m_file.isEmpty()); if (_config) { // copy data from config instead m_config = _config->copyTo(m_file); } else if (!QFile::exists(m_file)) { // given file exists, use it to load some stuff qCDebug(LOG_KATE) << "Warning, session file not found: " << m_file; return; } m_timestamp = QFileInfo(m_file).lastModified(); // get the document count m_documents = config()->group(opGroupName).readEntry(keyCount, 0); } KateSession::~KateSession() { delete m_config; } const QString &KateSession::file() const { return m_file; } void KateSession::setDocuments(const unsigned int number) { config()->group(opGroupName).writeEntry(keyCount, number); m_documents = number; } void KateSession::setFile(const QString &filename) { if (m_config) { KConfig *cfg = m_config->copyTo(filename); delete m_config; m_config = cfg; } m_file = filename; } void KateSession::setName(const QString &name) { m_name = name; } KConfig *KateSession::config() { if (m_config) { return m_config; } // reread documents number? return m_config = new KConfig(m_file, KConfig::SimpleConfig); } KateSession::Ptr KateSession::create(const QString &file, const QString &name) { return Ptr(new KateSession(file, name, false)); } KateSession::Ptr KateSession::createFrom(const KateSession::Ptr &session, const QString &file, const QString &name) { return Ptr(new KateSession(file, name, false, session->config())); } KateSession::Ptr KateSession::createAnonymous(const QString &file) { return Ptr(new KateSession(file, QString(), true)); } KateSession::Ptr KateSession::createAnonymousFrom(const KateSession::Ptr &session, const QString &file) { return Ptr(new KateSession(file, QString(), true, session->config())); } bool KateSession::compareByName(const KateSession::Ptr &s1, const KateSession::Ptr &s2) { return QCollator().compare(s1->name(), s2->name()) == -1; } bool KateSession::compareByTimeDesc(const KateSession::Ptr &s1, const KateSession::Ptr &s2) { return s1->timestamp() > s2->timestamp(); } diff --git a/kwrite/kwrite.h b/kwrite/kwrite.h index 40df46e33..a8867e6a4 100644 --- a/kwrite/kwrite.h +++ b/kwrite/kwrite.h @@ -1,154 +1,154 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KWRITE_MAIN_H #define KWRITE_MAIN_H #include #include #include #include #include #include class QLabel; namespace KActivities { class ResourceInstance; } class KToggleAction; class KRecentFilesAction; class KSqueezedTextLabel; class KWriteApplication; class KWrite : public KParts::MainWindow { Q_OBJECT public: KWrite(KTextEditor::Document * = nullptr, KWriteApplication *app = nullptr); ~KWrite() override; void loadURL(const QUrl &url); private: void setupActions(); void addMenuBarActionToContextMenu(); void removeMenuBarActionFromContextMenu(); bool queryClose() override; void dragEnterEvent(QDragEnterEvent *) override; void dropEvent(QDropEvent *) override; public Q_SLOTS: void slotNew(); void slotFlush(); void slotOpen(); void slotOpen(const QUrl &url); void newView(); void toggleStatusBar(); void toggleMenuBar(bool showMessage = true); void editKeys(); void editToolbars(); void aboutEditor(); void modifiedChanged(); private Q_SLOTS: void slotNewToolbarConfig(); public Q_SLOTS: void slotDropEvent(QDropEvent *); void slotEnableActions(bool enable); /** * adds a changed URL to the recent files */ void urlChanged(); /** * Overwrite size hint for better default window sizes * @return size hint */ QSize sizeHint() const override; // config file functions public: void readConfig(KSharedConfigPtr); void writeConfig(KSharedConfigPtr); void readConfig(); void writeConfig(); // session management public: void restore(KConfig *, int); public: KTextEditor::MainWindow *mainWindow() { return &m_mainWindow; } public Q_SLOTS: QWidget *window() { return this; } QList views(); KTextEditor::View *activeView() { return m_view; } KTextEditor::View *activateView(KTextEditor::Document *document); private: void readProperties(const KConfigGroup &) override; void saveProperties(KConfigGroup &) override; void saveGlobalProperties(KConfig *) override; private: KTextEditor::View *m_view = nullptr; KRecentFilesAction *m_recentFiles = nullptr; KToggleAction *m_paShowPath = nullptr; KToggleAction *m_paShowMenuBar = nullptr; KToggleAction *m_paShowStatusBar = nullptr; - QAction *m_closeAction; + QAction *m_closeAction = nullptr; KActivities::ResourceInstance *m_activityResource = nullptr; KWriteApplication *m_app; KTextEditor::MainWindow m_mainWindow; public Q_SLOTS: void documentNameChanged(); protected: /** * Event filter for QApplication to handle mac os like file open */ bool eventFilter(QObject *obj, QEvent *event) override; }; #endif