File indexing completed on 2024-05-05 05:51:25
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2010 Thomas Fjellstrom <thomas@fjellstrom.ca> 0003 SPDX-FileCopyrightText: 2014 Joseph Wenninger <jowenn@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 // BEGIN Includes 0009 #include "katefiletree.h" 0010 0011 #include "filehistorywidget.h" 0012 #include "katefileactions.h" 0013 #include "katefiletreemodel.h" 0014 #include "katefiletreeproxymodel.h" 0015 0016 #include <ktexteditor/application.h> 0017 #include <ktexteditor/document.h> 0018 #include <ktexteditor/editor.h> 0019 0020 #include <KIO/CopyJob> 0021 #include <KIO/DeleteJob> 0022 #include <KIO/OpenFileManagerWindowJob> 0023 #include <KLocalizedString> 0024 #include <KMessageBox> 0025 #include <KStandardAction> 0026 #include <KTextEditor/MainWindow> 0027 0028 #include <QActionGroup> 0029 #include <QApplication> 0030 #include <QClipboard> 0031 #include <QContextMenuEvent> 0032 #include <QDir> 0033 #include <QHeaderView> 0034 #include <QInputDialog> 0035 #include <QLineEdit> 0036 #include <QMenu> 0037 #include <QMimeDatabase> 0038 #include <QStyledItemDelegate> 0039 // END Includes 0040 0041 namespace 0042 { 0043 KTextEditor::Document *docFromIndex(const QModelIndex &index) 0044 { 0045 return index.data(KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>(); 0046 } 0047 0048 QList<KTextEditor::Document *> docTreeFromIndex(const QModelIndex &index) 0049 { 0050 return index.data(KateFileTreeModel::DocumentTreeRole).value<QList<KTextEditor::Document *>>(); 0051 } 0052 0053 bool closeDocs(const QList<KTextEditor::Document *> &docs) 0054 { 0055 return KTextEditor::Editor::instance()->application()->closeDocuments(docs); 0056 } 0057 0058 class CloseIconStyleDelegate : public QStyledItemDelegate 0059 { 0060 public: 0061 explicit CloseIconStyleDelegate(QObject *parent = nullptr) 0062 : QStyledItemDelegate(parent) 0063 { 0064 } 0065 0066 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0067 { 0068 QStyledItemDelegate::paint(painter, option, index); 0069 0070 if (!m_showCloseBtn) { 0071 return; 0072 } 0073 0074 if (index.column() == 1 && option.state & QStyle::State_Enabled && option.state & QStyle::State_MouseOver) { 0075 const QIcon icon = QIcon::fromTheme(QStringLiteral("tab-close")); 0076 const int w = option.decorationSize.width(); 0077 QRect iconRect(option.rect.right() - w, option.rect.top(), w, option.rect.height()); 0078 icon.paint(painter, iconRect, Qt::AlignRight | Qt::AlignVCenter); 0079 } 0080 } 0081 0082 void setShowCloseButton(bool s) 0083 { 0084 m_showCloseBtn = s; 0085 } 0086 0087 private: 0088 bool m_showCloseBtn = false; 0089 }; 0090 } // namespace 0091 0092 // BEGIN KateFileTree 0093 0094 KateFileTree::KateFileTree(KTextEditor::MainWindow *mainWindow, QWidget *parent) 0095 : QTreeView(parent) 0096 , m_mainWindow(mainWindow) 0097 { 0098 setIndentation(12); 0099 setAllColumnsShowFocus(true); 0100 setFocusPolicy(Qt::NoFocus); 0101 setSelectionBehavior(QAbstractItemView::SelectRows); 0102 // for hover close button 0103 viewport()->setAttribute(Qt::WA_Hover); 0104 0105 // DND 0106 setDefaultDropAction(Qt::MoveAction); 0107 setDragDropMode(QAbstractItemView::InternalMove); 0108 setDragDropOverwriteMode(false); 0109 setAcceptDrops(true); 0110 setDropIndicatorShown(true); 0111 setDragEnabled(true); 0112 setUniformRowHeights(true); 0113 0114 setItemDelegate(new CloseIconStyleDelegate(this)); 0115 0116 // handle activated (e.g. for pressing enter) + clicked (to avoid to need to do double-click e.g. on Windows) 0117 connect(this, &KateFileTree::activated, this, &KateFileTree::mouseClicked); 0118 connect(this, &KateFileTree::clicked, this, &KateFileTree::mouseClicked); 0119 0120 m_filelistReloadDocument = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action:inmenu", "Reloa&d"), this); 0121 connect(m_filelistReloadDocument, &QAction::triggered, this, &KateFileTree::slotDocumentReload); 0122 m_filelistReloadDocument->setWhatsThis(i18n("Reload selected document(s) from disk.")); 0123 0124 m_filelistCloseDocument = new QAction(QIcon::fromTheme(QStringLiteral("document-close")), i18nc("@action:inmenu", "Close"), this); 0125 connect(m_filelistCloseDocument, &QAction::triggered, this, &KateFileTree::slotDocumentClose); 0126 m_filelistCloseDocument->setWhatsThis(i18n("Close the current document.")); 0127 0128 m_filelistExpandRecursive = new QAction(QIcon::fromTheme(QStringLiteral("view-list-tree")), i18nc("@action:inmenu", "Expand Recursively"), this); 0129 connect(m_filelistExpandRecursive, &QAction::triggered, this, &KateFileTree::slotExpandRecursive); 0130 m_filelistExpandRecursive->setWhatsThis(i18n("Expand the file list sub tree recursively.")); 0131 0132 m_filelistCollapseRecursive = new QAction(QIcon::fromTheme(QStringLiteral("view-list-tree")), i18nc("@action:inmenu", "Collapse Recursively"), this); 0133 connect(m_filelistCollapseRecursive, &QAction::triggered, this, &KateFileTree::slotCollapseRecursive); 0134 m_filelistCollapseRecursive->setWhatsThis(i18n("Collapse the file list sub tree recursively.")); 0135 0136 m_filelistCloseOtherDocument = new QAction(QIcon::fromTheme(QStringLiteral("document-close")), i18nc("@action:inmenu", "Close Other"), this); 0137 connect(m_filelistCloseOtherDocument, &QAction::triggered, this, &KateFileTree::slotDocumentCloseOther); 0138 m_filelistCloseOtherDocument->setWhatsThis(i18n("Close other documents in this folder.")); 0139 0140 m_filelistOpenContainingFolder = 0141 new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Containing Folder"), this); 0142 connect(m_filelistOpenContainingFolder, &QAction::triggered, this, &KateFileTree::slotOpenContainingFolder); 0143 m_filelistOpenContainingFolder->setWhatsThis(i18n("Open the folder this file is located in.")); 0144 0145 m_filelistCopyFilename = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy-path")), i18nc("@action:inmenu", "Copy Location"), this); 0146 connect(m_filelistCopyFilename, &QAction::triggered, this, &KateFileTree::slotCopyFilename); 0147 m_filelistCopyFilename->setWhatsThis(i18n("Copy path and filename to the clipboard.")); 0148 0149 m_filelistRenameFile = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "Rename..."), this); 0150 connect(m_filelistRenameFile, &QAction::triggered, this, &KateFileTree::slotRenameFile); 0151 m_filelistRenameFile->setWhatsThis(i18n("Rename the selected file.")); 0152 0153 m_filelistPrintDocument = KStandardAction::print(this, &KateFileTree::slotPrintDocument, this); 0154 m_filelistPrintDocument->setWhatsThis(i18n("Print selected document.")); 0155 0156 m_filelistPrintDocumentPreview = KStandardAction::printPreview(this, &KateFileTree::slotPrintDocumentPreview, this); 0157 m_filelistPrintDocumentPreview->setWhatsThis(i18n("Show print preview of current document")); 0158 0159 m_filelistDeleteDocument = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete"), this); 0160 connect(m_filelistDeleteDocument, &QAction::triggered, this, &KateFileTree::slotDocumentDelete); 0161 m_filelistDeleteDocument->setWhatsThis(i18n("Close and delete selected file from storage.")); 0162 0163 setupContextMenuActionGroups(); 0164 0165 m_resetHistory = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-history")), i18nc("@action:inmenu", "Clear History"), this); 0166 connect(m_resetHistory, &QAction::triggered, this, &KateFileTree::slotResetHistory); 0167 m_resetHistory->setWhatsThis(i18n("Clear edit/view history.")); 0168 0169 QPalette p = palette(); 0170 p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight)); 0171 p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText)); 0172 setPalette(p); 0173 } 0174 0175 KateFileTree::~KateFileTree() = default; 0176 0177 void KateFileTree::setModel(QAbstractItemModel *model) 0178 { 0179 m_proxyModel = static_cast<KateFileTreeProxyModel *>(model); 0180 Q_ASSERT(m_proxyModel); // we don't really work with anything else 0181 QTreeView::setModel(model); 0182 m_sourceModel = static_cast<KateFileTreeModel *>(m_proxyModel->sourceModel()); 0183 0184 header()->hide(); 0185 header()->setStretchLastSection(false); 0186 header()->setSectionResizeMode(0, QHeaderView::Stretch); 0187 0188 const int minSize = m_hasCloseButton ? 16 : 1; 0189 header()->setMinimumSectionSize(minSize); 0190 header()->setSectionResizeMode(1, QHeaderView::Fixed); 0191 header()->resizeSection(1, minSize); 0192 0193 // proxy never emits rowsMoved 0194 connect(m_proxyModel->sourceModel(), &QAbstractItemModel::rowsMoved, this, &KateFileTree::onRowsMoved); 0195 } 0196 0197 void KateFileTree::onRowsMoved(const QModelIndex &, int, int, const QModelIndex &destination, int row) 0198 { 0199 QModelIndex movedIndex = m_proxyModel->mapFromSource(m_sourceModel->index(row, 0, destination)); 0200 // We moved stuff, make sure if child was expanded, we expand all parents too. 0201 if (movedIndex.isValid() && isExpanded(movedIndex) && !isExpanded(movedIndex.parent())) { 0202 QModelIndex movedParent = movedIndex.parent(); 0203 while (movedParent.isValid() && !isExpanded(movedParent)) { 0204 expand(movedParent); 0205 movedParent = movedParent.parent(); 0206 } 0207 } 0208 } 0209 0210 void KateFileTree::setShowCloseButton(bool show) 0211 { 0212 m_hasCloseButton = show; 0213 static_cast<CloseIconStyleDelegate *>(itemDelegate())->setShowCloseButton(show); 0214 0215 if (!header()) 0216 return; 0217 0218 const int minSize = show ? 16 : 1; 0219 header()->setMinimumSectionSize(minSize); 0220 header()->resizeSection(1, minSize); 0221 header()->viewport()->update(); 0222 } 0223 0224 void KateFileTree::setMiddleClickToClose(bool value) 0225 { 0226 m_middleClickToClose = value; 0227 if (value) { 0228 viewport()->installEventFilter(this); 0229 } else { 0230 viewport()->removeEventFilter(this); 0231 } 0232 } 0233 0234 void KateFileTree::setupContextMenuActionGroups() 0235 { 0236 QActionGroup *modeGroup = new QActionGroup(this); 0237 0238 m_treeModeAction = setupOption(modeGroup, 0239 QIcon::fromTheme(QStringLiteral("view-list-tree")), 0240 i18nc("@action:inmenu", "Tree Mode"), 0241 i18n("Set view style to Tree Mode"), 0242 &KateFileTree::slotTreeMode, 0243 Qt::Checked); 0244 0245 m_listModeAction = setupOption(modeGroup, 0246 QIcon::fromTheme(QStringLiteral("view-list-text")), 0247 i18nc("@action:inmenu", "List Mode"), 0248 i18n("Set view style to List Mode"), 0249 &KateFileTree::slotListMode); 0250 0251 QActionGroup *sortGroup = new QActionGroup(this); 0252 0253 m_sortByFile = setupOption(sortGroup, 0254 QIcon(), 0255 i18nc("@action:inmenu sorting option", "Document Name"), 0256 i18n("Sort by Document Name"), 0257 &KateFileTree::slotSortName, 0258 Qt::Checked); 0259 0260 m_sortByPath = 0261 setupOption(sortGroup, QIcon(), i18nc("@action:inmenu sorting option", "Document Path"), i18n("Sort by Document Path"), &KateFileTree::slotSortPath); 0262 0263 m_sortByOpeningOrder = setupOption(sortGroup, 0264 QIcon(), 0265 i18nc("@action:inmenu sorting option", "Opening Order"), 0266 i18n("Sort by Opening Order"), 0267 &KateFileTree::slotSortOpeningOrder); 0268 0269 m_customSorting = new QAction(QIcon(), i18n("Custom Sorting"), this); 0270 m_customSorting->setCheckable(true); 0271 m_customSorting->setActionGroup(sortGroup); 0272 connect(m_customSorting, &QAction::triggered, this, [this] { 0273 Q_EMIT sortRoleChanged(CustomSorting); 0274 }); 0275 } 0276 0277 QAction *KateFileTree::setupOption(QActionGroup *group, 0278 const QIcon &icon, 0279 const QString &text, 0280 const QString &whatsThis, 0281 const Func &slot, 0282 Qt::CheckState checked /* = Qt::Unchecked */) 0283 { 0284 QAction *new_action = new QAction(icon, text, this); 0285 new_action->setWhatsThis(whatsThis); 0286 new_action->setActionGroup(group); 0287 new_action->setCheckable(true); 0288 new_action->setChecked(checked == Qt::Checked); 0289 connect(new_action, &QAction::triggered, this, slot); 0290 return new_action; 0291 } 0292 0293 void KateFileTree::slotListMode() 0294 { 0295 Q_EMIT viewModeChanged(true); 0296 } 0297 0298 void KateFileTree::slotTreeMode() 0299 { 0300 Q_EMIT viewModeChanged(false); 0301 } 0302 0303 void KateFileTree::slotSortName() 0304 { 0305 Q_EMIT sortRoleChanged(Qt::DisplayRole); 0306 } 0307 0308 void KateFileTree::slotSortPath() 0309 { 0310 Q_EMIT sortRoleChanged(KateFileTreeModel::PathRole); 0311 } 0312 0313 void KateFileTree::slotSortOpeningOrder() 0314 { 0315 Q_EMIT sortRoleChanged(KateFileTreeModel::OpeningOrderRole); 0316 } 0317 0318 void KateFileTree::slotCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) 0319 { 0320 Q_UNUSED(previous); 0321 if (!current.isValid()) { 0322 return; 0323 } 0324 0325 KTextEditor::Document *doc = m_proxyModel->docFromIndex(current); 0326 if (doc) { 0327 m_previouslySelected = current; 0328 } 0329 } 0330 0331 void KateFileTree::closeClicked(const QModelIndex &index) 0332 { 0333 if (m_proxyModel->isDir(index)) { 0334 const QList<KTextEditor::Document *> list = m_proxyModel->docTreeFromIndex(index); 0335 closeDocs(list); 0336 return; 0337 } else if (m_proxyModel->isWidgetDir(index)) { 0338 const auto idx = index.siblingAtColumn(0); 0339 const auto count = m_proxyModel->rowCount(idx); 0340 QWidgetList widgets; 0341 widgets.reserve(count); 0342 for (int i = 0; i < count; ++i) { 0343 widgets << m_proxyModel->index(i, 0, idx).data(KateFileTreeModel::WidgetRole).value<QWidget *>(); 0344 } 0345 0346 for (const auto &w : widgets) { 0347 closeWidget(w); 0348 } 0349 } 0350 0351 if (auto *doc = m_proxyModel->docFromIndex(index)) { 0352 closeDocs({doc}); 0353 } else if (auto *w = index.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) { 0354 Q_EMIT closeWidget(w); 0355 } 0356 } 0357 0358 void KateFileTree::mouseClicked(const QModelIndex &index) 0359 { 0360 if (m_hasCloseButton && index.column() == 1) { 0361 closeClicked(index); 0362 return; 0363 } 0364 0365 if (auto *doc = m_proxyModel->docFromIndex(index)) { 0366 Q_EMIT activateDocument(doc); 0367 } else if (auto *w = index.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) { 0368 Q_EMIT activateWidget(w); 0369 } 0370 } 0371 0372 void KateFileTree::contextMenuEvent(QContextMenuEvent *event) 0373 { 0374 m_indexContextMenu = indexAt(event->pos()); 0375 if (m_indexContextMenu.isValid()) { 0376 selectionModel()->setCurrentIndex(m_indexContextMenu, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0377 } 0378 0379 const bool listMode = m_sourceModel->listMode(); 0380 m_treeModeAction->setChecked(!listMode); 0381 m_listModeAction->setChecked(listMode); 0382 0383 const int sortRole = m_proxyModel->sortRole(); 0384 m_sortByFile->setChecked(sortRole == Qt::DisplayRole); 0385 m_sortByPath->setChecked(sortRole == KateFileTreeModel::PathRole); 0386 m_sortByOpeningOrder->setChecked(sortRole == KateFileTreeModel::OpeningOrderRole); 0387 m_customSorting->setChecked(sortRole == CustomSorting); 0388 0389 KTextEditor::Document *doc = docFromIndex(m_indexContextMenu); 0390 0391 bool isDir = m_proxyModel->isDir(m_indexContextMenu); 0392 bool isWidgetDir = m_proxyModel->isWidgetDir(m_indexContextMenu); 0393 bool isWidget = m_indexContextMenu.data(KateFileTreeModel::WidgetRole).value<QWidget *>() != nullptr; 0394 0395 QMenu menu(this); 0396 if (doc) { 0397 if (doc->url().isValid()) { 0398 QMenu *openWithMenu = menu.addMenu(i18nc("@action:inmenu", "Open With")); 0399 openWithMenu->setIcon(QIcon::fromTheme(QStringLiteral("system-run"))); 0400 connect(openWithMenu, &QMenu::aboutToShow, this, [this, openWithMenu]() { 0401 slotFixOpenWithMenu(openWithMenu); 0402 }); 0403 connect(openWithMenu, &QMenu::triggered, this, &KateFileTree::slotOpenWithMenuAction); 0404 0405 menu.addSeparator(); 0406 menu.addAction(m_filelistCopyFilename); 0407 menu.addAction(m_filelistRenameFile); 0408 menu.addAction(m_filelistDeleteDocument); 0409 menu.addAction(m_filelistReloadDocument); 0410 0411 if (doc->url().isLocalFile()) { 0412 auto a = menu.addAction(i18n("Show File Git History")); 0413 connect(a, &QAction::triggered, this, [doc] { 0414 auto url = doc->url(); 0415 if (url.isValid() && url.isLocalFile()) { 0416 FileHistory::showFileHistory(url.toLocalFile()); 0417 } 0418 }); 0419 } 0420 0421 menu.addSeparator(); 0422 menu.addAction(m_filelistOpenContainingFolder); 0423 0424 menu.addSeparator(); 0425 menu.addAction(m_filelistCloseDocument); 0426 menu.addAction(m_filelistCloseOtherDocument); 0427 0428 menu.addSeparator(); 0429 menu.addAction(m_filelistPrintDocument); 0430 menu.addAction(m_filelistPrintDocumentPreview); 0431 } else { 0432 // untitled documents 0433 menu.addAction(m_filelistCloseDocument); 0434 0435 menu.addSeparator(); 0436 } 0437 } else if (isDir || isWidgetDir || isWidget) { 0438 if (isDir) { 0439 menu.addAction(m_filelistReloadDocument); 0440 } 0441 0442 menu.addSeparator(); 0443 menu.addAction(m_filelistCloseDocument); 0444 0445 menu.addSeparator(); 0446 menu.addAction(m_filelistExpandRecursive); 0447 menu.addAction(m_filelistCollapseRecursive); 0448 } 0449 0450 menu.addSeparator(); 0451 QMenu *view_menu = menu.addMenu(i18nc("@action:inmenu", "View Mode")); 0452 view_menu->addAction(m_treeModeAction); 0453 view_menu->addAction(m_listModeAction); 0454 0455 QMenu *sort_menu = menu.addMenu(QIcon::fromTheme(QStringLiteral("view-sort")), i18nc("@action:inmenu", "Sort By")); 0456 sort_menu->addAction(m_sortByFile); 0457 sort_menu->addAction(m_sortByPath); 0458 sort_menu->addAction(m_sortByOpeningOrder); 0459 sort_menu->addAction(m_customSorting); 0460 0461 m_filelistCloseDocument->setEnabled(m_indexContextMenu.isValid()); 0462 0463 menu.addAction(m_resetHistory); 0464 0465 menu.exec(viewport()->mapToGlobal(event->pos())); 0466 0467 if (m_previouslySelected.isValid()) { 0468 selectionModel()->setCurrentIndex(m_previouslySelected, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0469 } 0470 0471 event->accept(); 0472 } 0473 0474 bool KateFileTree::eventFilter(QObject *o, QEvent *e) 0475 { 0476 if (m_middleClickToClose && o == viewport() && e->type() == QEvent::MouseButtonRelease) { 0477 auto me = static_cast<QMouseEvent *>(e); 0478 if (me->button() == Qt::MiddleButton && me->modifiers() == Qt::NoModifier) { 0479 closeClicked(indexAt(me->pos())); 0480 return true; 0481 } 0482 } 0483 0484 return QObject::eventFilter(o, e); 0485 } 0486 0487 void KateFileTree::slotFixOpenWithMenu(QMenu *menu) 0488 { 0489 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0490 if (!doc) { 0491 return; 0492 } 0493 0494 KateFileActions::prepareOpenWithMenu(doc->url(), menu); 0495 } 0496 0497 void KateFileTree::slotOpenWithMenuAction(QAction *a) 0498 { 0499 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0500 if (!doc) { 0501 return; 0502 } 0503 0504 KateFileActions::showOpenWithMenu(m_mainWindow->window(), doc->url(), a); 0505 } 0506 0507 void KateFileTree::slotDocumentClose() 0508 { 0509 m_previouslySelected = QModelIndex(); 0510 if (!m_indexContextMenu.isValid()) { 0511 return; 0512 } 0513 const auto closeColumnIndex = m_indexContextMenu.sibling(m_indexContextMenu.row(), 1); 0514 closeClicked(closeColumnIndex); 0515 } 0516 0517 void KateFileTree::addChildrenTolist(const QModelIndex &index, QList<QPersistentModelIndex> *worklist) 0518 { 0519 const int count = m_proxyModel->rowCount(index); 0520 worklist->reserve(worklist->size() + count); 0521 for (int i = 0; i < count; ++i) { 0522 worklist->append(m_proxyModel->index(i, 0, index)); 0523 } 0524 } 0525 0526 void KateFileTree::slotExpandRecursive() 0527 { 0528 if (!m_indexContextMenu.isValid()) { 0529 return; 0530 } 0531 0532 // Work list for DFS walk over sub tree 0533 QList<QPersistentModelIndex> worklist = {m_indexContextMenu}; 0534 0535 while (!worklist.isEmpty()) { 0536 QPersistentModelIndex index = worklist.takeLast(); 0537 0538 // Expand current item 0539 expand(index); 0540 0541 // Append all children of current item 0542 addChildrenTolist(index, &worklist); 0543 } 0544 } 0545 0546 void KateFileTree::slotCollapseRecursive() 0547 { 0548 if (!m_indexContextMenu.isValid()) { 0549 return; 0550 } 0551 0552 // Work list for DFS walk over sub tree 0553 QList<QPersistentModelIndex> worklist = {m_indexContextMenu}; 0554 0555 while (!worklist.isEmpty()) { 0556 QPersistentModelIndex index = worklist.takeLast(); 0557 0558 // Expand current item 0559 collapse(index); 0560 0561 // Prepend all children of current item 0562 addChildrenTolist(index, &worklist); 0563 } 0564 } 0565 0566 void KateFileTree::slotDocumentCloseOther() 0567 { 0568 QList<KTextEditor::Document *> closingDocuments = m_proxyModel->docTreeFromIndex(m_indexContextMenu.parent()); 0569 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0570 closingDocuments.removeOne(doc); 0571 closeDocs(closingDocuments); 0572 } 0573 0574 void KateFileTree::slotDocumentReload() 0575 { 0576 const QList<KTextEditor::Document *> docs = docTreeFromIndex(m_indexContextMenu); 0577 for (auto *doc : docs) { 0578 doc->documentReload(); 0579 } 0580 } 0581 0582 void KateFileTree::slotOpenContainingFolder() 0583 { 0584 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0585 0586 if (!doc) { 0587 return; 0588 } 0589 0590 KateFileActions::openContainingFolder(doc); 0591 } 0592 0593 void KateFileTree::slotCopyFilename() 0594 { 0595 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0596 0597 if (!doc) { 0598 return; 0599 } 0600 0601 KateFileActions::copyFilePathToClipboard(doc); 0602 } 0603 0604 void KateFileTree::slotRenameFile() 0605 { 0606 KateFileActions::renameDocumentFile(this, m_proxyModel->docFromIndex(m_indexContextMenu)); 0607 } 0608 0609 void KateFileTree::slotDocumentFirst() 0610 { 0611 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_proxyModel->index(0, 0)); 0612 if (doc) { 0613 Q_EMIT activateDocument(doc); 0614 } 0615 } 0616 0617 void KateFileTree::slotDocumentLast() 0618 { 0619 int count = m_proxyModel->rowCount(m_proxyModel->parent(currentIndex())); 0620 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_proxyModel->index(count - 1, 0)); 0621 if (doc) { 0622 Q_EMIT activateDocument(doc); 0623 } 0624 } 0625 0626 void KateFileTree::slotDocumentPrev() 0627 { 0628 QModelIndex current_index = currentIndex(); 0629 QModelIndex prev; 0630 0631 // scan up the tree skipping any dir nodes 0632 while (current_index.isValid()) { 0633 if (current_index.row() > 0) { 0634 current_index = m_proxyModel->sibling(current_index.row() - 1, current_index.column(), current_index); 0635 if (!current_index.isValid()) { 0636 break; 0637 } 0638 0639 if (m_proxyModel->isDir(current_index)) { 0640 // try and select the last child in this parent 0641 int children = m_proxyModel->rowCount(current_index); 0642 current_index = m_proxyModel->index(children - 1, 0, current_index); 0643 if (m_proxyModel->isDir(current_index)) { 0644 // since we're a dir, keep going 0645 while (m_proxyModel->isDir(current_index)) { 0646 children = m_proxyModel->rowCount(current_index); 0647 current_index = m_proxyModel->index(children - 1, 0, current_index); 0648 } 0649 0650 if (!m_proxyModel->isDir(current_index)) { 0651 prev = current_index; 0652 break; 0653 } 0654 0655 continue; 0656 } else { 0657 // we're the previous file, set prev 0658 prev = current_index; 0659 break; 0660 } 0661 } else { // found document item 0662 prev = current_index; 0663 break; 0664 } 0665 } else { 0666 // just select the parent, the logic above will handle the rest 0667 current_index = m_proxyModel->parent(current_index); 0668 if (!current_index.isValid()) { 0669 // paste the root node here, try and wrap around 0670 0671 int children = m_proxyModel->rowCount(current_index); 0672 QModelIndex last_index = m_proxyModel->index(children - 1, 0, current_index); 0673 if (!last_index.isValid()) { 0674 break; 0675 } 0676 0677 if (m_proxyModel->isDir(last_index)) { 0678 // last node is a dir, select last child row 0679 int last_children = m_proxyModel->rowCount(last_index); 0680 prev = m_proxyModel->index(last_children - 1, 0, last_index); 0681 // bug here? 0682 break; 0683 } else { 0684 // got last file node 0685 prev = last_index; 0686 break; 0687 } 0688 } 0689 } 0690 } 0691 0692 if (prev.isValid()) { 0693 if (auto *doc = m_proxyModel->docFromIndex(prev)) { 0694 Q_EMIT activateDocument(doc); 0695 } else if (auto *w = prev.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) { 0696 Q_EMIT activateWidget(w); 0697 } 0698 } 0699 } 0700 0701 void KateFileTree::slotDocumentNext() 0702 { 0703 QModelIndex current_index = currentIndex(); 0704 int parent_row_count = m_proxyModel->rowCount(m_proxyModel->parent(current_index)); 0705 QModelIndex next; 0706 0707 // scan down the tree skipping any dir nodes 0708 while (current_index.isValid()) { 0709 if (current_index.row() < parent_row_count - 1) { 0710 current_index = m_proxyModel->sibling(current_index.row() + 1, current_index.column(), current_index); 0711 if (!current_index.isValid()) { 0712 break; 0713 } 0714 0715 if (m_proxyModel->isDir(current_index)) { 0716 // we have a dir node 0717 while (m_proxyModel->isDir(current_index)) { 0718 current_index = m_proxyModel->index(0, 0, current_index); 0719 } 0720 0721 parent_row_count = m_proxyModel->rowCount(m_proxyModel->parent(current_index)); 0722 0723 if (!m_proxyModel->isDir(current_index)) { 0724 next = current_index; 0725 break; 0726 } 0727 } else { // found document item 0728 next = current_index; 0729 break; 0730 } 0731 } else { 0732 // select the parent's next sibling 0733 QModelIndex parent_index = m_proxyModel->parent(current_index); 0734 int grandparent_row_count = m_proxyModel->rowCount(m_proxyModel->parent(parent_index)); 0735 0736 current_index = parent_index; 0737 parent_row_count = grandparent_row_count; 0738 0739 // at least if we're not past the last node 0740 if (!current_index.isValid()) { 0741 // paste the root node here, try and wrap around 0742 QModelIndex last_index = m_proxyModel->index(0, 0, QModelIndex()); 0743 if (!last_index.isValid()) { 0744 break; 0745 } 0746 0747 if (m_proxyModel->isDir(last_index)) { 0748 // last node is a dir, select first child row 0749 while (m_proxyModel->isDir(last_index)) { 0750 if (m_proxyModel->rowCount(last_index)) { 0751 // has children, select first 0752 last_index = m_proxyModel->index(0, 0, last_index); 0753 } 0754 } 0755 0756 next = last_index; 0757 break; 0758 } else { 0759 // got first file node 0760 next = last_index; 0761 break; 0762 } 0763 } 0764 } 0765 } 0766 0767 if (next.isValid()) { 0768 if (auto *doc = m_proxyModel->docFromIndex(next)) { 0769 Q_EMIT activateDocument(doc); 0770 } else if (auto *w = next.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) { 0771 Q_EMIT activateWidget(w); 0772 } 0773 } 0774 } 0775 0776 void KateFileTree::slotPrintDocument() 0777 { 0778 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0779 0780 if (!doc) { 0781 return; 0782 } 0783 0784 doc->print(); 0785 } 0786 0787 void KateFileTree::slotPrintDocumentPreview() 0788 { 0789 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0790 0791 if (!doc) { 0792 return; 0793 } 0794 0795 doc->printPreview(); 0796 } 0797 0798 void KateFileTree::slotResetHistory() 0799 { 0800 m_sourceModel->resetHistory(); 0801 } 0802 0803 void KateFileTree::slotDocumentDelete() 0804 { 0805 KTextEditor::Document *doc = m_proxyModel->docFromIndex(m_indexContextMenu); 0806 KateFileActions::deleteDocumentFile(m_mainWindow->window(), doc); 0807 } 0808 0809 // END KateFileTree 0810 0811 #include "moc_katefiletree.cpp"