File indexing completed on 2024-04-28 05:49:09
0001 /* This file is part of the Kate project. 0002 * 0003 * SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kateprojectviewtree.h" 0009 #include "kateproject.h" 0010 #include "kateprojectfiltermodel.h" 0011 #include "kateprojectitem.h" 0012 #include "kateprojectpluginview.h" 0013 #include "kateprojecttreeviewcontextmenu.h" 0014 #include "ktexteditor_utils.h" 0015 0016 #include <KTextEditor/Document> 0017 #include <KTextEditor/MainWindow> 0018 #include <KTextEditor/View> 0019 0020 #include <QContextMenuEvent> 0021 #include <QDir> 0022 0023 #include <KLocalizedString> 0024 0025 KateProjectViewTree::KateProjectViewTree(KateProjectPluginView *pluginView, KateProject *project) 0026 : m_pluginView(pluginView) 0027 , m_project(project) 0028 { 0029 /** 0030 * default style 0031 */ 0032 setHeaderHidden(true); 0033 setEditTriggers(QAbstractItemView::NoEditTriggers); 0034 setAllColumnsShowFocus(true); 0035 setIndentation(12); 0036 0037 setDragDropMode(QAbstractItemView::DropOnly); 0038 setDragDropOverwriteMode(false); 0039 0040 /** 0041 * attach view => project 0042 * do this once, model is stable for whole project life time 0043 * kill selection model 0044 * create sort proxy model 0045 */ 0046 QItemSelectionModel *m = selectionModel(); 0047 0048 KateProjectFilterProxyModel *sortModel = new KateProjectFilterProxyModel(this); 0049 0050 // sortModel->setFilterRole(SortFilterRole); 0051 // sortModel->setSortRole(SortFilterRole); 0052 sortModel->setRecursiveFilteringEnabled(true); 0053 sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); 0054 sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0055 sortModel->setSourceModel(m_project->model()); 0056 setModel(sortModel); 0057 delete m; 0058 0059 /** 0060 * connect needed signals 0061 * we use activated + clicked as we want "always" single click activation + keyboard focus / enter working 0062 */ 0063 connect(this, &KateProjectViewTree::activated, this, &KateProjectViewTree::slotClicked); 0064 connect(this, &KateProjectViewTree::clicked, this, &KateProjectViewTree::slotClicked); 0065 connect(m_project, &KateProject::modelChanged, this, &KateProjectViewTree::slotModelChanged); 0066 0067 /** 0068 * trigger once some slots 0069 */ 0070 slotModelChanged(); 0071 } 0072 0073 KateProjectViewTree::~KateProjectViewTree() 0074 { 0075 } 0076 0077 void KateProjectViewTree::selectFile(const QString &file) 0078 { 0079 /** 0080 * get item if any 0081 */ 0082 QStandardItem *item = m_project->itemForFile(file); 0083 if (!item) { 0084 return; 0085 } 0086 0087 /** 0088 * select it 0089 */ 0090 QModelIndex index = static_cast<QSortFilterProxyModel *>(model())->mapFromSource(m_project->model()->indexFromItem(item)); 0091 scrollTo(index, QAbstractItemView::EnsureVisible); 0092 selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear | QItemSelectionModel::Select); 0093 } 0094 0095 void KateProjectViewTree::openSelectedDocument() 0096 { 0097 /** 0098 * anything selected? 0099 */ 0100 QModelIndexList selecteStuff = selectedIndexes(); 0101 if (selecteStuff.isEmpty()) { 0102 return; 0103 } 0104 0105 /** 0106 * we only handle files here! 0107 */ 0108 if (selecteStuff[0].data(KateProjectItem::TypeRole).toInt() != KateProjectItem::File) { 0109 return; 0110 } 0111 0112 /** 0113 * open document for first element, if possible 0114 */ 0115 QString filePath = selecteStuff[0].data(Qt::UserRole).toString(); 0116 if (!filePath.isEmpty()) { 0117 m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath)); 0118 } 0119 } 0120 0121 void KateProjectViewTree::addFile(const QModelIndex &idx, const QString &fileName) 0122 { 0123 auto proxyModel = static_cast<QSortFilterProxyModel *>(model()); 0124 auto index = proxyModel->mapToSource(idx); 0125 auto item = m_project->model()->itemFromIndex(index); 0126 0127 const QString fullFileName = index.data(Qt::UserRole).toString() + QLatin1Char('/') + fileName; 0128 0129 /** 0130 * Create an actual file on disk 0131 */ 0132 QFile f(fullFileName); 0133 bool created = f.open(QIODevice::WriteOnly); 0134 if (!created) { 0135 const auto icon = QIcon::fromTheme(QStringLiteral("document-new")); 0136 Utils::showMessage(i18n("Failed to create file: %1, Error: %2", fileName, f.errorString()), icon, i18n("Project"), MessageType::Error); 0137 return; 0138 } 0139 0140 KateProjectItem *i = new KateProjectItem(KateProjectItem::File, fileName); 0141 i->setData(fullFileName, Qt::UserRole); 0142 item->appendRow(i); 0143 m_project->addFile(fullFileName, i); 0144 item->sortChildren(0); 0145 } 0146 0147 void KateProjectViewTree::addDirectory(const QModelIndex &idx, const QString &name) 0148 { 0149 auto proxyModel = static_cast<QSortFilterProxyModel *>(model()); 0150 auto index = proxyModel->mapToSource(idx); 0151 auto item = m_project->model()->itemFromIndex(index); 0152 const QString fullDirName = index.data(Qt::UserRole).toString() + QLatin1Char('/') + name; 0153 0154 QDir dir(index.data(Qt::UserRole).toString()); 0155 if (!dir.mkdir(name)) { 0156 const auto icon = QIcon::fromTheme(QStringLiteral("folder-new")); 0157 Utils::showMessage(i18n("Failed to create dir: %1", name), icon, i18n("Project"), MessageType::Error); 0158 return; 0159 } 0160 0161 KateProjectItem *i = new KateProjectItem(KateProjectItem::Directory, name); 0162 i->setData(fullDirName, Qt::UserRole); 0163 item->appendRow(i); 0164 item->sortChildren(0); 0165 } 0166 0167 void KateProjectViewTree::removeFile(const QModelIndex &idx, const QString &fullFilePath) 0168 { 0169 auto proxyModel = static_cast<QSortFilterProxyModel *>(model()); 0170 auto index = proxyModel->mapToSource(idx); 0171 auto item = m_project->model()->itemFromIndex(index); 0172 if (!item) { 0173 return; 0174 } 0175 QStandardItem *parent = item->parent(); 0176 0177 /** 0178 * Delete file 0179 */ 0180 QFile file(fullFilePath); 0181 if (file.remove()) //.moveToTrash() 0182 { 0183 if (parent != nullptr) { 0184 parent->removeRow(item->row()); 0185 parent->sortChildren(0); 0186 } else { 0187 m_project->model()->removeRow(item->row()); 0188 m_project->model()->sort(0); 0189 } 0190 m_project->removeFile(fullFilePath); 0191 } 0192 } 0193 0194 void KateProjectViewTree::openTerminal(const QString &dirPath) 0195 { 0196 m_pluginView->openTerminal(dirPath, m_project); 0197 } 0198 0199 void KateProjectViewTree::slotClicked(const QModelIndex &index) 0200 { 0201 /** 0202 * open document, if any usable user data 0203 */ 0204 const QString filePath = index.data(Qt::UserRole).toString(); 0205 if (!filePath.isEmpty()) { 0206 /** 0207 * normal file? => just trigger open of it 0208 */ 0209 if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::File) { 0210 m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath)); 0211 selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear | QItemSelectionModel::Select); 0212 return; 0213 } 0214 0215 /** 0216 * linked project? => switch the current active project 0217 */ 0218 if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::LinkedProject) { 0219 m_pluginView->switchToProject(QDir(filePath)); 0220 return; 0221 } 0222 } 0223 } 0224 0225 void KateProjectViewTree::slotModelChanged() 0226 { 0227 /** 0228 * model was updated 0229 * perhaps we need to highlight again new file 0230 */ 0231 KTextEditor::View *activeView = m_pluginView->mainWindow()->activeView(); 0232 if (activeView && activeView->document()->url().isLocalFile()) { 0233 selectFile(activeView->document()->url().toLocalFile()); 0234 } 0235 } 0236 0237 void KateProjectViewTree::contextMenuEvent(QContextMenuEvent *event) 0238 { 0239 /** 0240 * get path file path or don't do anything 0241 */ 0242 QModelIndex index = selectionModel()->currentIndex(); 0243 QString filePath = index.data(Qt::UserRole).toString(); 0244 if (filePath.isEmpty()) { 0245 QTreeView::contextMenuEvent(event); 0246 return; 0247 } 0248 0249 KateProjectTreeViewContextMenu menu; 0250 menu.exec(filePath, index, viewport()->mapToGlobal(event->pos()), this); 0251 0252 event->accept(); 0253 } 0254 0255 #include "moc_kateprojectviewtree.cpp"