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