Commit 9202a58b authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Show non-KTE::* widgets in documents plugin

parent a1429c68
......@@ -95,6 +95,8 @@ public:
Q_DECLARE_METATYPE(ResultNode)
static const ResultNode openWidgetsNode("Open Widgets", true);
namespace QTest
{
inline bool qCompare(const ResultNode &t1, const ResultNode &t2, const char *actual, const char *expected, const char *file, int line)
......@@ -136,13 +138,14 @@ void FileTreeModelTest::basic()
QScopedPointer<DummyDocument> d2(new DummyDocument());
KateFileTreeModel m(this);
QCOMPARE(m.rowCount(QModelIndex()), 0);
// 1 because, there is always a "open widgets" node
QCOMPARE(m.rowCount(QModelIndex()), 1);
m.documentOpened(d1.data());
QCOMPARE(m.rowCount(QModelIndex()), 1);
QCOMPARE(m.rowCount(QModelIndex()), 2);
m.documentOpened(d2.data());
QCOMPARE(m.rowCount(QModelIndex()), 2);
QCOMPARE(m.rowCount(QModelIndex()), 3);
}
void FileTreeModelTest::buildTree_data()
......@@ -333,6 +336,7 @@ void FileTreeModelTest::walkTree(KateFileTreeModel &model, const QModelIndex &ro
walkTree(model, idx, node);
rootNode << node;
}
rootNode.children.removeAll(openWidgetsNode);
}
void FileTreeModelTest::buildTreeFullPath_data()
......
......@@ -339,6 +339,12 @@ void KateFileTree::mouseClicked(const QModelIndex &index)
return;
}
Q_EMIT activateDocument(doc);
} else if (auto *w = index.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) {
if (closeButtonClicked) {
Q_EMIT closeWidget(w);
} else {
Q_EMIT activateWidget(w);
}
}
}
......@@ -359,7 +365,9 @@ void KateFileTree::contextMenuEvent(QContextMenuEvent *event)
m_customSorting->setChecked(sortRole == CustomSorting);
KTextEditor::Document *doc = docFromIndex(m_indexContextMenu);
QWidget *w = m_indexContextMenu.data(KateFileTreeModel::WidgetRole).value<QWidget *>();
const bool isFile = (nullptr != doc);
const bool isWidget = (w != nullptr);
QMenu menu;
if (isFile) {
......@@ -402,6 +410,11 @@ void KateFileTree::contextMenuEvent(QContextMenuEvent *event)
m_filelistCopyFilename->setEnabled(hasFileName);
m_filelistRenameFile->setEnabled(hasFileName);
m_filelistDeleteDocument->setEnabled(hasFileName);
} else if (isWidget) {
auto a = menu.addAction(i18n("Close Widget"), this, [this, w] {
Q_EMIT closeWidget(w);
});
a->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
} else {
menu.addAction(m_filelistReloadDocument);
......
......@@ -62,6 +62,9 @@ Q_SIGNALS:
void viewModeChanged(bool treeMode);
void sortRoleChanged(int);
void closeWidget(QWidget *);
void activateWidget(QWidget *);
private Q_SLOTS:
void mouseClicked(const QModelIndex &index);
......
......@@ -14,12 +14,14 @@
#include <QMimeData>
#include <QMimeDatabase>
#include <QStack>
#include <QWidget>
#include <KColorScheme>
#include <KColorUtils>
#include <KIconUtils>
#include <KLocalizedString>
#include <KTextEditor/MainWindow>
#include <ktexteditor/application.h>
#include <ktexteditor/document.h>
#include <ktexteditor/editor.h>
......@@ -27,6 +29,8 @@
#include "katefiletreedebug.h"
#include "ktexteditor_utils.h"
#include <variant>
static constexpr int MaxHistoryItems = 10;
class FileTreeMimeData : public QMimeData
......@@ -53,7 +57,17 @@ class ProxyItem
friend class KateFileTreeModel;
public:
enum Flag { None = 0, Dir = 1, Modified = 2, ModifiedExternally = 4, DeletedExternally = 8, Empty = 16, ShowFullPath = 32, Host = 64 };
enum Flag {
None = 0,
Dir = 1,
Modified = 2,
ModifiedExternally = 4,
DeletedExternally = 8,
Empty = 16,
ShowFullPath = 32,
Host = 64,
Widget = 128,
};
Q_DECLARE_FLAGS(Flags, Flag)
ProxyItem(const QString &n, ProxyItemDir *p = nullptr, Flags f = ProxyItem::None);
......@@ -87,6 +101,9 @@ public:
void setDoc(KTextEditor::Document *doc);
KTextEditor::Document *doc() const;
void setWidget(QWidget *);
QWidget *widget() const;
/**
* the view uses this to close all the documents under the folder
* @returns list of all the (nested) documents under this node
......@@ -108,7 +125,7 @@ private:
QString m_display;
QIcon m_icon;
KTextEditor::Document *m_doc;
std::variant<KTextEditor::Document *, QWidget *> m_object;
QString m_host;
protected:
......@@ -167,10 +184,13 @@ ProxyItem::ProxyItem(const QString &d, ProxyItemDir *p, ProxyItem::Flags f)
, m_parent(Q_NULLPTR)
, m_row(-1)
, m_flags(f)
, m_doc(nullptr)
{
updateDisplay();
if (f.testFlag(Widget) && f.testFlag(Dir)) {
m_documentName = display();
}
/**
* add to parent, if parent passed
* we assigned above nullptr to parent to not trigger
......@@ -300,21 +320,37 @@ QList<ProxyItem *> &ProxyItem::children()
void ProxyItem::setDoc(KTextEditor::Document *doc)
{
Q_ASSERT(doc);
m_doc = doc;
m_object = doc;
updateDocumentName();
}
void ProxyItem::setWidget(QWidget *w)
{
Q_ASSERT(w);
m_object = w;
updateDocumentName();
}
QWidget *ProxyItem::widget() const
{
if (!std::holds_alternative<QWidget *>(m_object))
return nullptr;
return std::get<QWidget *>(m_object);
}
KTextEditor::Document *ProxyItem::doc() const
{
return m_doc;
if (!std::holds_alternative<KTextEditor::Document *>(m_object))
return nullptr;
return std::get<KTextEditor::Document *>(m_object);
}
QList<KTextEditor::Document *> ProxyItem::docTree() const
{
QList<KTextEditor::Document *> result;
if (m_doc) {
result.append(m_doc);
if (doc()) {
result.append(doc());
return result;
}
......@@ -366,12 +402,17 @@ const QString &ProxyItem::host() const
void ProxyItem::updateDocumentName()
{
const QString docName = m_doc ? m_doc->documentName() : QString();
QString name;
if (doc()) {
name = doc()->documentName();
} else if (widget()) {
name = widget()->windowTitle();
}
if (flag(ProxyItem::Host)) {
m_documentName = QStringLiteral("[%1]%2").arg(m_host, docName);
m_documentName = QStringLiteral("[%1]%2").arg(m_host, name);
} else {
m_documentName = docName;
m_documentName = name;
}
}
......@@ -450,11 +491,26 @@ void KateFileTreeModel::setShowFullPathOnRoots(bool s)
void KateFileTreeModel::initModel()
{
beginInsertRows(QModelIndex(), 0, 0);
Q_ASSERT(!m_widgetsRoot);
m_widgetsRoot = new ProxyItem(i18n("Open Widgets"), nullptr, ProxyItem::Flags(ProxyItem::Dir | ProxyItem::Widget));
m_widgetsRoot->setFlags(ProxyItem::Flags(ProxyItem::Dir | ProxyItem::Widget));
m_widgetsRoot->setIcon(QIcon::fromTheme(QStringLiteral("folder-windows")));
m_root->addChild(m_widgetsRoot);
endInsertRows();
// add already existing documents
const auto documents = KTextEditor::Editor::instance()->application()->documents();
for (KTextEditor::Document *doc : documents) {
documentOpened(doc);
}
auto mw = KTextEditor::Editor::instance()->application()->activeMainWindow();
QWidgetList widgets;
QMetaObject::invokeMethod(mw->window(), "widgets", Q_RETURN_ARG(QWidgetList, widgets));
for (auto *w : std::as_const(widgets)) {
addWidget(w);
}
}
void KateFileTreeModel::clearModel()
......@@ -467,6 +523,8 @@ void KateFileTreeModel::clearModel()
delete m_root;
m_root = new ProxyItemDir(QStringLiteral("m_root"), nullptr);
m_widgetsRoot = nullptr;
m_docmap.clear();
m_viewHistory.clear();
m_editHistory.clear();
......@@ -498,6 +556,23 @@ QModelIndex KateFileTreeModel::docIndex(const KTextEditor::Document *doc) const
return createIndex(item->row(), 0, item);
}
QModelIndex KateFileTreeModel::widgetIndex(QWidget *widget) const
{
ProxyItem *item = nullptr;
const auto items = m_widgetsRoot->children();
for (auto *it : items) {
if (it->widget() == widget) {
item = it;
break;
}
}
if (!item) {
return {};
}
return createIndex(item->row(), 0, item);
}
Qt::ItemFlags KateFileTreeModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = Qt::ItemIsEnabled;
......@@ -508,14 +583,18 @@ Qt::ItemFlags KateFileTreeModel::flags(const QModelIndex &index) const
const ProxyItem *item = static_cast<ProxyItem *>(index.internalPointer());
if (item) {
if (!item->childCount()) {
if (!item->flag(ProxyItem::Dir)) {
flags |= Qt::ItemIsSelectable;
}
if (item->childCount() > 0) {
if (item->flag(ProxyItem::Dir) && !item->flag(ProxyItem::Widget)) {
flags |= Qt::ItemIsDropEnabled;
}
if (item->flag(ProxyItem::Dir) && item->flag(ProxyItem::Widget) && item->childCount() == 0) {
flags.setFlag(Qt::ItemIsEnabled, false);
}
if (item->doc() && item->doc()->url().isValid()) {
flags |= Qt::ItemIsDragEnabled;
}
......@@ -543,6 +622,9 @@ QVariant KateFileTreeModel::data(const QModelIndex &index, int role) const
case KateFileTreeModel::DocumentRole:
return QVariant::fromValue(item->doc());
case KateFileTreeModel::WidgetRole:
return QVariant::fromValue(item->widget());
case KateFileTreeModel::OpeningOrderRole:
return item->row();
......@@ -574,7 +656,7 @@ QVariant KateFileTreeModel::data(const QModelIndex &index, int role) const
}
case Qt::ForegroundRole: {
if (!item->flag(ProxyItem::Dir) && (!item->doc() || item->doc()->openingError())) {
if (!item->flag(ProxyItem::Widget) && !item->flag(ProxyItem::Dir) && (!item->doc() || item->doc()->openingError())) {
return m_inactiveDocColor;
}
} break;
......@@ -1387,4 +1469,40 @@ void KateFileTreeModel::resetHistory()
}
}
void KateFileTreeModel::addWidget(QWidget *w)
{
if (!w) {
return;
}
const QModelIndex parentIdx = createIndex(m_widgetsRoot->row(), 0, m_widgetsRoot);
beginInsertRows(parentIdx, m_widgetsRoot->childCount(), m_widgetsRoot->childCount());
auto item = new ProxyItem(w->windowTitle());
item->setFlag(ProxyItem::Widget);
item->setIcon(w->windowIcon());
item->setWidget(w);
m_widgetsRoot->addChild(item);
endInsertRows();
}
void KateFileTreeModel::removeWidget(QWidget *w)
{
ProxyItem *item = nullptr;
const auto items = m_widgetsRoot->children();
for (auto *it : items) {
if (it->widget() == w) {
item = it;
break;
}
}
if (!item) {
return;
}
const QModelIndex parentIdx = createIndex(m_widgetsRoot->row(), 0, m_widgetsRoot);
beginRemoveRows(parentIdx, item->row(), item->row());
m_widgetsRoot->removeChild(item);
endRemoveRows();
}
#include "katefiletreemodel.moc"
......@@ -30,7 +30,13 @@ class KateFileTreeModel : public QAbstractItemModel
Q_OBJECT
public:
enum { DocumentRole = Qt::UserRole + 1, PathRole, OpeningOrderRole, DocumentTreeRole };
enum {
DocumentRole = Qt::UserRole + 1,
PathRole,
OpeningOrderRole,
DocumentTreeRole,
WidgetRole,
};
KateFileTreeModel(QObject *p);
~KateFileTreeModel() override;
......@@ -53,6 +59,8 @@ public:
/* extra api for view */
QModelIndex docIndex(const KTextEditor::Document *) const;
QModelIndex widgetIndex(QWidget *) const;
static bool isDir(const QModelIndex &index);
bool listMode() const;
......@@ -83,6 +91,9 @@ public Q_SLOTS:
void documentModifiedChanged(KTextEditor::Document *);
void documentModifiedOnDisc(KTextEditor::Document *, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason);
void addWidget(QWidget *w);
void removeWidget(QWidget *w);
Q_SIGNALS:
void triggerViewChangeAfterNameChange();
......@@ -105,6 +116,7 @@ private:
private:
ProxyItemDir *m_root;
ProxyItem *m_widgetsRoot = nullptr;
QHash<const KTextEditor::Document *, ProxyItem *> m_docmap;
bool m_shadingEnabled;
......
......@@ -202,6 +202,20 @@ KateFileTreePluginView::KateFileTreePluginView(KTextEditor::MainWindow *mainWind
connect(mainWindow, &KTextEditor::MainWindow::viewChanged, this, &KateFileTreePluginView::viewChanged);
auto mw = mainWindow->window();
connect(mw, SIGNAL(widgetAdded(QWidget *)), this, SLOT(slotWidgetCreated(QWidget *)));
connect(mw, SIGNAL(widgetRemoved(QWidget *)), this, SLOT(slotWidgetRemoved(QWidget *)));
connect(mw, SIGNAL(widgetActivated(QWidget *)), this, SLOT(widgetActivated(QWidget *)));
connect(m_fileTree, &KateFileTree::closeWidget, this, [this](QWidget *w) {
auto mw = m_mainWindow->window();
QMetaObject::invokeMethod(mw, "removeWidget", Q_ARG(QWidget *, w));
});
connect(m_fileTree, &KateFileTree::activateWidget, this, [this](QWidget *w) {
auto mw = m_mainWindow->window();
QMetaObject::invokeMethod(mw, "activateWidget", Q_ARG(QWidget *, w));
});
//
// actions
//
......@@ -319,16 +333,22 @@ void KateFileTreePluginView::setToolbarVisible(bool visible)
void KateFileTreePluginView::viewChanged(KTextEditor::View *)
{
KTextEditor::View *view = m_mainWindow->activeView();
if (!view) {
auto mw = m_mainWindow->window();
QWidget *activeWidget = nullptr;
QMetaObject::invokeMethod(mw, "activeWidget", Q_RETURN_ARG(QWidget *, activeWidget));
if (!activeWidget) {
return;
}
KTextEditor::Document *doc = view->document();
QModelIndex index = m_proxyModel->docIndex(doc);
// update the model on which doc is active
m_documentModel->documentActivated(doc);
QModelIndex index;
if (auto view = qobject_cast<KTextEditor::View *>(activeWidget)) {
KTextEditor::Document *doc = view->document();
index = m_proxyModel->docIndex(doc);
// update the model on which doc is active
m_documentModel->documentActivated(doc);
} else {
index = m_proxyModel->widgetIndex(activeWidget);
}
m_fileTree->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
......@@ -458,6 +478,21 @@ void KateFileTreePluginView::slotDocumentSaveAs() const
}
}
void KateFileTreePluginView::widgetActivated(QWidget *)
{
viewChanged(nullptr);
}
void KateFileTreePluginView::slotWidgetCreated(QWidget *w)
{
m_documentModel->addWidget(w);
}
void KateFileTreePluginView::slotWidgetRemoved(QWidget *w)
{
m_documentModel->removeWidget(w);
}
// END KateFileTreePluginView
#include "katefiletreeplugin.moc"
......@@ -135,6 +135,10 @@ private Q_SLOTS:
void slotDocumentsCreated();
void slotDocumentSave() const;
void slotDocumentSaveAs() const;
void widgetActivated(QWidget *);
void slotWidgetCreated(QWidget *);
void slotWidgetRemoved(QWidget *);
};
#endif // KATE_FILETREE_PLUGIN_H
......@@ -81,3 +81,8 @@ bool KateFileTreeProxyModel::isDir(const QModelIndex &index) const
{
return static_cast<KateFileTreeModel *>(sourceModel())->isDir(mapToSource(index));
}
QModelIndex KateFileTreeProxyModel::widgetIndex(QWidget *w) const
{
return mapFromSource(static_cast<KateFileTreeModel *>(sourceModel())->widgetIndex(w));
}
......@@ -23,6 +23,7 @@ class KateFileTreeProxyModel : public QSortFilterProxyModel
public:
KateFileTreeProxyModel(QObject *p = nullptr);
QModelIndex docIndex(const KTextEditor::Document *) const;
QModelIndex widgetIndex(QWidget *) const;
bool isDir(const QModelIndex &i) const;
void setSourceModel(QAbstractItemModel *model) override;
KTextEditor::Document *docFromIndex(const QModelIndex &index);
......
......@@ -1318,9 +1318,36 @@ bool KateMainWindow::addWidget(QWidget *widget)
{
auto vs = m_viewManager->activeViewSpace();
vs->addWidgetAsTab(widget);
Q_EMIT widgetAdded(widget);
Q_EMIT widgetActivated(widget);
return true;
}
bool KateMainWindow::removeWidget(QWidget *widget)
{
auto success = m_viewManager->removeWidget(widget);
if (success) {
Q_EMIT widgetRemoved(widget);
}
return success;
}
QWidget *KateMainWindow::activeWidget()
{
auto vs = m_viewManager->activeViewSpace();
if (auto w = vs->currentWidget()) {
return w;
}
return activeView();
}
void KateMainWindow::activateWidget(QWidget *widget)
{
if (m_viewManager->activateWidget(widget)) {
Q_EMIT widgetActivated(widget);
}
}
void KateMainWindow::showDiff(const QByteArray &wordDiff, const DiffParams &params)
{
auto w = new DiffWidget(params, this);
......@@ -1346,6 +1373,11 @@ void KateMainWindow::showMessage(const QVariantMap &map)
m_outputView->slotMessage(map);
}
QWidgetList KateMainWindow::widgets() const
{
return m_viewManager->widgets();
}
void KateMainWindow::mousePressEvent(QMouseEvent *e)
{
switch (e->button()) {
......
......@@ -220,6 +220,10 @@ Q_SIGNALS:
void tabBarToggled();
void unhandledShortcutOverride(QEvent *e);
void widgetAdded(QWidget *);
void widgetRemoved(QWidget *);
void widgetActivated(QWidget *);
public:
void openUrl(const QString &name = QString());
......@@ -522,6 +526,31 @@ public Q_SLOTS:
*/
bool addWidget(QWidget *widget);
/**
* \brief returns the list of non-KTextEditor::View widgets in this main window.
* \see addWidget
*/
QWidgetList widgets() const;
/**
* \brief remove this \p widget from this mainwindow.
* \param widget the widget to be removed
* \return true on success
*/
bool removeWidget(QWidget *widget);
/**
* \brief returns the currently active widget. It can be a
* non-KTextEditor::View widget or a KTextEditor::View
*/
QWidget *activeWidget();
/**
* \brief activate @p widget
* \param widget the widget to activate
*/
void activateWidget(QWidget *widget);
void showDiff(const QByteArray &wordDiff, const DiffParams &params);
/**
......
......@@ -614,6 +614,35 @@ KateViewSpace *KateViewManager::activeViewSpace()
return nullptr;
}
QWidgetList KateViewManager::widgets() const
{
QWidgetList widgets;
for (auto *vs : m_viewSpaceList) {
widgets << vs->widgets();
}
return widgets;
}
bool KateViewManager::removeWidget(QWidget *w)
{
for (auto *vs : m_viewSpaceList) {
if (vs->closeTabWithWidget(w)) {