File indexing completed on 2024-05-05 05:51:26
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2010 Thomas Fjellstrom <thomas@fjellstrom.ca> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 // BEGIN Includes 0008 0009 #include "katefiletreeplugin.h" 0010 #include "katefiletree.h" 0011 #include "katefiletreeconfigpage.h" 0012 #include "katefiletreemodel.h" 0013 #include "katefiletreeproxymodel.h" 0014 0015 #include <ktexteditor/application.h> 0016 #include <ktexteditor/view.h> 0017 0018 #include <KActionCollection> 0019 #include <KConfigGroup> 0020 #include <KLocalizedString> 0021 #include <KPluginFactory> 0022 #include <KXMLGUIFactory> 0023 #include <KXmlGuiWindow> 0024 0025 #include <QAction> 0026 #include <QLayout> 0027 #include <QLineEdit> 0028 #include <QStyle> 0029 #include <QToolBar> 0030 0031 #include "katefiletreedebug.h" 0032 0033 // END Includes 0034 0035 K_PLUGIN_FACTORY_WITH_JSON(KateFileTreeFactory, "katefiletreeplugin.json", registerPlugin<KateFileTreePlugin>();) 0036 0037 Q_LOGGING_CATEGORY(FILETREE, "kate-filetree", QtWarningMsg) 0038 0039 // BEGIN KateFileTreePlugin 0040 KateFileTreePlugin::KateFileTreePlugin(QObject *parent, const QVariantList &) 0041 : KTextEditor::Plugin(parent) 0042 { 0043 } 0044 0045 KateFileTreePlugin::~KateFileTreePlugin() 0046 { 0047 m_settings.save(); 0048 } 0049 0050 QObject *KateFileTreePlugin::createView(KTextEditor::MainWindow *mainWindow) 0051 { 0052 auto view = new KateFileTreePluginView(mainWindow, this); 0053 m_views.append(view); 0054 return view; 0055 } 0056 0057 void KateFileTreePlugin::viewDestroyed(QObject *view) 0058 { 0059 // do not access the view pointer, since it is partially destroyed already 0060 m_views.removeAll(static_cast<KateFileTreePluginView *>(view)); 0061 } 0062 0063 int KateFileTreePlugin::configPages() const 0064 { 0065 return 1; 0066 } 0067 0068 KTextEditor::ConfigPage *KateFileTreePlugin::configPage(int number, QWidget *parent) 0069 { 0070 if (number != 0) { 0071 return nullptr; 0072 } 0073 0074 KateFileTreeConfigPage *page = new KateFileTreeConfigPage(parent, this); 0075 return page; 0076 } 0077 0078 const KateFileTreePluginSettings &KateFileTreePlugin::settings() 0079 { 0080 return m_settings; 0081 } 0082 0083 void KateFileTreePlugin::applyConfig(bool shadingEnabled, 0084 const QColor &viewShade, 0085 const QColor &editShade, 0086 bool listMode, 0087 int sortRole, 0088 bool showFullPath, 0089 bool showToolbar, 0090 bool showCloseButton, 0091 bool middleClickToClose) 0092 { 0093 // save to settings 0094 m_settings.setShadingEnabled(shadingEnabled); 0095 m_settings.setViewShade(viewShade); 0096 m_settings.setEditShade(editShade); 0097 0098 m_settings.setListMode(listMode); 0099 m_settings.setSortRole(sortRole); 0100 m_settings.setShowFullPathOnRoots(showFullPath); 0101 m_settings.setShowToolbar(showToolbar); 0102 m_settings.setShowCloseButton(showCloseButton); 0103 m_settings.middleClickToClose = middleClickToClose; 0104 m_settings.save(); 0105 0106 // update views 0107 for (KateFileTreePluginView *view : qAsConst(m_views)) { 0108 view->setHasLocalPrefs(false); 0109 view->model()->setShadingEnabled(shadingEnabled); 0110 view->model()->setViewShade(viewShade); 0111 view->model()->setEditShade(editShade); 0112 view->setListMode(listMode); 0113 view->proxy()->setSortRole(sortRole); 0114 view->tree()->setDragDropMode(sortRole == CustomSorting ? QAbstractItemView::InternalMove : QAbstractItemView::DragOnly); 0115 view->model()->setShowFullPathOnRoots(showFullPath); 0116 view->setToolbarVisible(showToolbar); 0117 view->tree()->setShowCloseButton(showCloseButton); 0118 view->tree()->setMiddleClickToClose(middleClickToClose); 0119 } 0120 } 0121 0122 // END KateFileTreePlugin 0123 0124 // BEGIN KateFileTreePluginView 0125 KateFileTreePluginView::KateFileTreePluginView(KTextEditor::MainWindow *mainWindow, KateFileTreePlugin *plug) 0126 : QObject(mainWindow) 0127 , m_plug(plug) 0128 , m_mainWindow(mainWindow) 0129 { 0130 KXMLGUIClient::setComponentName(QStringLiteral("katefiletree"), i18n("Documents")); 0131 setXMLFile(QStringLiteral("ui.rc")); 0132 0133 m_toolView = mainWindow->createToolView(plug, 0134 QStringLiteral("kate_private_plugin_katefiletreeplugin"), 0135 KTextEditor::MainWindow::Left, 0136 QIcon::fromTheme(QStringLiteral("folder-documents-symbolic")), 0137 i18n("Documents")); 0138 0139 // create toolbar 0140 m_toolbar = new QToolBar(m_toolView); 0141 m_toolbar->setMovable(false); 0142 m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); 0143 m_toolbar->setContextMenuPolicy(Qt::NoContextMenu); 0144 m_toolbar->layout()->setContentsMargins(0, 0, 0, 0); 0145 0146 // ensure reasonable icons sizes, like e.g. the quick-open and co. icons 0147 // the normal toolbar sizes are TOO large, e.g. for scaled stuff even more! 0148 const int iconSize = m_toolView->style()->pixelMetric(QStyle::PM_ButtonIconSize, nullptr, m_toolView); 0149 m_toolbar->setIconSize(QSize(iconSize, iconSize)); 0150 0151 // create filetree 0152 m_fileTree = new KateFileTree(m_mainWindow, m_toolView); 0153 m_fileTree->setSortingEnabled(true); 0154 m_fileTree->setShowCloseButton(m_plug->settings().showCloseButton()); 0155 m_fileTree->setMiddleClickToClose(m_plug->settings().middleClickToClose); 0156 m_fileTree->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::TopEdge})); 0157 0158 connect(m_fileTree, &KateFileTree::activateDocument, this, &KateFileTreePluginView::activateDocument); 0159 connect(m_fileTree, &KateFileTree::viewModeChanged, this, &KateFileTreePluginView::viewModeChanged); 0160 connect(m_fileTree, &KateFileTree::sortRoleChanged, this, &KateFileTreePluginView::sortRoleChanged); 0161 0162 m_documentModel = new KateFileTreeModel(m_mainWindow, this); 0163 m_proxyModel = new KateFileTreeProxyModel(this); 0164 m_proxyModel->setSourceModel(m_documentModel); 0165 m_proxyModel->setDynamicSortFilter(true); 0166 m_proxyModel->setRecursiveFilteringEnabled(true); 0167 0168 m_documentModel->setShowFullPathOnRoots(m_plug->settings().showFullPathOnRoots()); 0169 m_documentModel->setShadingEnabled(m_plug->settings().shadingEnabled()); 0170 m_documentModel->setViewShade(m_plug->settings().viewShade()); 0171 m_documentModel->setEditShade(m_plug->settings().editShade()); 0172 0173 m_filter = new QLineEdit(m_toolView); 0174 m_filter->setPlaceholderText(QStringLiteral("Filter...")); 0175 m_filter->setClearButtonEnabled(true); 0176 m_filter->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::TopEdge})); 0177 connect(m_filter, &QLineEdit::textChanged, this, [this](const QString &text) { 0178 m_proxyModel->setFilterRegularExpression(QRegularExpression(text, QRegularExpression::CaseInsensitiveOption)); 0179 if (!text.isEmpty()) { 0180 QTimer::singleShot(100, m_fileTree, &QTreeView::expandAll); 0181 } 0182 }); 0183 0184 connect(KTextEditor::Editor::instance()->application(), 0185 &KTextEditor::Application::documentWillBeDeleted, 0186 m_documentModel, 0187 &KateFileTreeModel::documentClosed); 0188 connect(KTextEditor::Editor::instance()->application(), &KTextEditor::Application::documentCreated, this, &KateFileTreePluginView::documentOpened); 0189 connect(KTextEditor::Editor::instance()->application(), &KTextEditor::Application::documentWillBeDeleted, this, &KateFileTreePluginView::documentClosed); 0190 0191 // delayed update for new documents to be more efficient if multiple ones are created at once 0192 m_documentsCreatedTimer.setSingleShot(true); 0193 m_documentsCreatedTimer.setInterval(0); 0194 connect(&m_documentsCreatedTimer, &QTimer::timeout, this, &KateFileTreePluginView::slotDocumentsCreated); 0195 0196 connect(m_documentModel, &KateFileTreeModel::triggerViewChangeAfterNameChange, this, [this] { 0197 viewChanged(); 0198 }); 0199 0200 m_fileTree->setModel(m_proxyModel); 0201 m_fileTree->setSelectionMode(QAbstractItemView::SingleSelection); 0202 0203 connect(m_fileTree->selectionModel(), &QItemSelectionModel::currentChanged, m_fileTree, &KateFileTree::slotCurrentChanged); 0204 0205 connect(mainWindow, &KTextEditor::MainWindow::viewChanged, this, &KateFileTreePluginView::viewChanged); 0206 0207 auto mw = mainWindow->window(); 0208 connect(mw, SIGNAL(widgetAdded(QWidget *)), this, SLOT(slotWidgetCreated(QWidget *))); 0209 connect(mw, SIGNAL(widgetRemoved(QWidget *)), this, SLOT(slotWidgetRemoved(QWidget *))); 0210 0211 connect(m_fileTree, &KateFileTree::closeWidget, this, [this](QWidget *w) { 0212 auto mw = m_mainWindow->window(); 0213 QMetaObject::invokeMethod(mw, "removeWidget", Q_ARG(QWidget *, w)); 0214 }); 0215 connect(m_fileTree, &KateFileTree::activateWidget, this, [this](QWidget *w) { 0216 auto mw = m_mainWindow->window(); 0217 QMetaObject::invokeMethod(mw, "activateWidget", Q_ARG(QWidget *, w)); 0218 }); 0219 0220 // 0221 // actions 0222 // 0223 setupActions(); 0224 0225 mainWindow->guiFactory()->addClient(this); 0226 0227 setToolbarVisible(m_plug->settings().showToolbar()); 0228 0229 m_proxyModel->setSortRole(Qt::DisplayRole); 0230 m_fileTree->setDragDropMode(QAbstractItemView::DragOnly); 0231 0232 m_proxyModel->sort(0, Qt::AscendingOrder); 0233 m_proxyModel->invalidate(); 0234 } 0235 0236 KateFileTreePluginView::~KateFileTreePluginView() 0237 { 0238 m_plug->viewDestroyed(this); 0239 0240 m_mainWindow->guiFactory()->removeClient(this); 0241 0242 // clean up tree and toolview 0243 delete m_fileTree->parent(); 0244 // delete m_toolView; 0245 // and TreeModel 0246 delete m_documentModel; 0247 } 0248 0249 void KateFileTreePluginView::setupActions() 0250 { 0251 auto aPrev = actionCollection()->addAction(QStringLiteral("filetree_prev_document")); 0252 aPrev->setText(i18n("Previous Document")); 0253 aPrev->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); 0254 actionCollection()->setDefaultShortcut(aPrev, Qt::ALT | Qt::Key_Up); 0255 connect(aPrev, &QAction::triggered, m_fileTree, &KateFileTree::slotDocumentPrev); 0256 0257 auto aNext = actionCollection()->addAction(QStringLiteral("filetree_next_document")); 0258 aNext->setText(i18n("Next Document")); 0259 aNext->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); 0260 actionCollection()->setDefaultShortcut(aNext, Qt::ALT | Qt::Key_Down); 0261 connect(aNext, &QAction::triggered, m_fileTree, &KateFileTree::slotDocumentNext); 0262 0263 auto aShowActive = actionCollection()->addAction(QStringLiteral("filetree_show_active_document")); 0264 aShowActive->setText(i18n("&Show Active Document")); 0265 aShowActive->setIcon(QIcon::fromTheme(QStringLiteral("folder-sync"))); 0266 connect(aShowActive, &QAction::triggered, this, &KateFileTreePluginView::showActiveDocument); 0267 0268 auto aSave = actionCollection()->addAction(QStringLiteral("filetree_save"), this, SLOT(slotDocumentSave())); 0269 aSave->setText(i18n("Save")); 0270 aSave->setToolTip(i18n("Save the current document")); 0271 aSave->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); 0272 0273 auto aSaveAs = actionCollection()->addAction(QStringLiteral("filetree_save_as"), this, SLOT(slotDocumentSaveAs())); 0274 aSaveAs->setText(i18n("Save As")); 0275 aSaveAs->setToolTip(i18n("Save the current document under a new name")); 0276 aSaveAs->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as"))); 0277 0278 /** 0279 * add new & open, if hosting application has it 0280 */ 0281 if (KXmlGuiWindow *parentClient = qobject_cast<KXmlGuiWindow *>(m_mainWindow->window())) { 0282 bool newOrOpen = false; 0283 if (auto a = parentClient->action(QStringLiteral("file_new"))) { 0284 m_toolbar->addAction(a); 0285 newOrOpen = true; 0286 } 0287 if (auto a = parentClient->action(QStringLiteral("file_open"))) { 0288 m_toolbar->addAction(a); 0289 newOrOpen = true; 0290 } 0291 if (newOrOpen) { 0292 m_toolbar->addSeparator(); 0293 } 0294 } 0295 0296 /** 0297 * add own actions 0298 */ 0299 m_toolbar->addAction(aSave); 0300 m_toolbar->addAction(aSaveAs); 0301 m_toolbar->addSeparator(); 0302 m_toolbar->addAction(aPrev); 0303 m_toolbar->addAction(aNext); 0304 m_toolbar->addAction(aShowActive); 0305 } 0306 0307 KateFileTreeModel *KateFileTreePluginView::model() const 0308 { 0309 return m_documentModel; 0310 } 0311 0312 KateFileTreeProxyModel *KateFileTreePluginView::proxy() const 0313 { 0314 return m_proxyModel; 0315 } 0316 0317 KateFileTree *KateFileTreePluginView::tree() const 0318 { 0319 return m_fileTree; 0320 } 0321 0322 void KateFileTreePluginView::documentOpened(KTextEditor::Document *doc) 0323 { 0324 // enqueue and start update timer to collapse updates 0325 m_documentsCreatedTimer.start(); 0326 m_documentsCreated.append(doc); 0327 } 0328 0329 void KateFileTreePluginView::documentClosed(KTextEditor::Document *doc) 0330 { 0331 m_documentsCreated.removeAll(doc); 0332 m_proxyModel->invalidate(); 0333 } 0334 0335 void KateFileTreePluginView::setToolbarVisible(bool visible) 0336 { 0337 m_toolbar->setVisible(visible); 0338 } 0339 0340 void KateFileTreePluginView::viewChanged(KTextEditor::View *) 0341 { 0342 auto mw = m_mainWindow->window(); 0343 QWidget *activeWidget = nullptr; 0344 QMetaObject::invokeMethod(mw, "activeWidget", Q_RETURN_ARG(QWidget *, activeWidget)); 0345 if (!activeWidget) { 0346 return; 0347 } 0348 0349 QModelIndex index; 0350 if (auto view = qobject_cast<KTextEditor::View *>(activeWidget)) { 0351 KTextEditor::Document *doc = view->document(); 0352 index = m_proxyModel->docIndex(doc); 0353 // update the model on which doc is active 0354 m_documentModel->documentActivated(doc); 0355 } else { 0356 index = m_proxyModel->widgetIndex(activeWidget); 0357 } 0358 0359 m_fileTree->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0360 0361 m_fileTree->scrollTo(index); 0362 0363 while (index != QModelIndex()) { 0364 m_fileTree->expand(index); 0365 index = index.parent(); 0366 } 0367 } 0368 0369 void KateFileTreePluginView::setListMode(bool listMode) 0370 { 0371 if (listMode) { 0372 m_documentModel->setListMode(true); 0373 m_fileTree->setRootIsDecorated(false); 0374 } else { 0375 m_documentModel->setListMode(false); 0376 m_fileTree->setRootIsDecorated(true); 0377 } 0378 0379 m_proxyModel->sort(0, Qt::AscendingOrder); 0380 m_proxyModel->invalidate(); 0381 } 0382 0383 void KateFileTreePluginView::viewModeChanged(bool listMode) 0384 { 0385 setHasLocalPrefs(true); 0386 setListMode(listMode); 0387 } 0388 0389 void KateFileTreePluginView::sortRoleChanged(int role) 0390 { 0391 setHasLocalPrefs(true); 0392 m_proxyModel->setSortRole(role); 0393 m_proxyModel->invalidate(); 0394 m_fileTree->setDragDropMode(role == CustomSorting ? QAbstractItemView::InternalMove : QAbstractItemView::DragOnly); 0395 } 0396 0397 void KateFileTreePluginView::activateDocument(KTextEditor::Document *doc) 0398 { 0399 m_mainWindow->activateView(doc); 0400 } 0401 0402 void KateFileTreePluginView::showToolView() 0403 { 0404 m_mainWindow->showToolView(m_toolView); 0405 m_toolView->setFocus(); 0406 } 0407 0408 void KateFileTreePluginView::hideToolView() 0409 { 0410 m_mainWindow->hideToolView(m_toolView); 0411 } 0412 0413 void KateFileTreePluginView::showActiveDocument() 0414 { 0415 // hack? 0416 viewChanged(); 0417 // make the tool view show if it was hidden 0418 showToolView(); 0419 } 0420 0421 bool KateFileTreePluginView::hasLocalPrefs() const 0422 { 0423 return m_hasLocalPrefs; 0424 } 0425 0426 void KateFileTreePluginView::setHasLocalPrefs(bool h) 0427 { 0428 m_hasLocalPrefs = h; 0429 } 0430 0431 void KateFileTreePluginView::readSessionConfig(const KConfigGroup &g) 0432 { 0433 if (g.exists()) { 0434 m_hasLocalPrefs = true; 0435 } else { 0436 m_hasLocalPrefs = false; 0437 } 0438 0439 // we chain to the global settings by using them as the defaults 0440 // here in the session view config loading. 0441 const KateFileTreePluginSettings &defaults = m_plug->settings(); 0442 0443 bool listMode = g.readEntry("listMode", defaults.listMode()); 0444 0445 setListMode(listMode); 0446 0447 int sortRole = g.readEntry("sortRole", defaults.sortRole()); 0448 m_proxyModel->setSortRole(sortRole); 0449 m_fileTree->setDragDropMode(sortRole == CustomSorting ? QAbstractItemView::InternalMove : QAbstractItemView::DragOnly); 0450 } 0451 0452 void KateFileTreePluginView::writeSessionConfig(KConfigGroup &g) 0453 { 0454 if (m_hasLocalPrefs) { 0455 g.writeEntry("listMode", QVariant(m_documentModel->listMode())); 0456 g.writeEntry("sortRole", int(m_proxyModel->sortRole())); 0457 } else { 0458 g.deleteEntry("listMode"); 0459 g.deleteEntry("sortRole"); 0460 } 0461 0462 g.sync(); 0463 } 0464 0465 void KateFileTreePluginView::slotDocumentsCreated() 0466 { 0467 // handle potential multiple new documents 0468 m_documentModel->documentsOpened(m_documentsCreated); 0469 m_documentsCreated.clear(); 0470 viewChanged(); 0471 } 0472 0473 void KateFileTreePluginView::slotDocumentSave() const 0474 { 0475 if (auto view = m_mainWindow->activeView()) { 0476 view->document()->documentSave(); 0477 } 0478 } 0479 0480 void KateFileTreePluginView::slotDocumentSaveAs() const 0481 { 0482 if (auto view = m_mainWindow->activeView()) { 0483 view->document()->documentSaveAs(); 0484 } 0485 } 0486 0487 void KateFileTreePluginView::slotWidgetCreated(QWidget *w) 0488 { 0489 m_documentModel->addWidget(w); 0490 } 0491 0492 void KateFileTreePluginView::slotWidgetRemoved(QWidget *w) 0493 { 0494 m_documentModel->removeWidget(w); 0495 } 0496 0497 // END KateFileTreePluginView 0498 0499 #include "katefiletreeplugin.moc" 0500 #include "moc_katefiletreeplugin.cpp"