File indexing completed on 2024-05-12 05:47:41
0001 /* 0002 * SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz19@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "folderspanel.h" 0008 0009 #include "dolphin_folderspanelsettings.h" 0010 #include "dolphin_generalsettings.h" 0011 #include "foldersitemlistwidget.h" 0012 #include "global.h" 0013 #include "kitemviews/kfileitemlistview.h" 0014 #include "kitemviews/kfileitemmodel.h" 0015 #include "kitemviews/kitemlistcontainer.h" 0016 #include "kitemviews/kitemlistcontroller.h" 0017 #include "kitemviews/kitemlistselectionmanager.h" 0018 #include "kitemviews/private/kitemlistroleeditor.h" 0019 #include "treeviewcontextmenu.h" 0020 #include "views/draganddrophelper.h" 0021 0022 #include <KIO/CopyJob> 0023 #include <KIO/DropJob> 0024 #include <KIO/FileUndoManager> 0025 #include <KIO/RenameFileDialog> 0026 #include <KJobUiDelegate> 0027 #include <KJobWidgets> 0028 0029 #include <QApplication> 0030 #include <QBoxLayout> 0031 #include <QGraphicsSceneDragDropEvent> 0032 #include <QGraphicsView> 0033 #include <QPropertyAnimation> 0034 #include <QTimer> 0035 0036 FoldersPanel::FoldersPanel(QWidget *parent) 0037 : Panel(parent) 0038 , m_updateCurrentItem(false) 0039 , m_controller(nullptr) 0040 , m_model(nullptr) 0041 { 0042 setLayoutDirection(Qt::LeftToRight); 0043 } 0044 0045 FoldersPanel::~FoldersPanel() 0046 { 0047 FoldersPanelSettings::self()->save(); 0048 0049 if (m_controller) { 0050 KItemListView *view = m_controller->view(); 0051 m_controller->setView(nullptr); 0052 delete view; 0053 } 0054 } 0055 0056 void FoldersPanel::setShowHiddenFiles(bool show) 0057 { 0058 FoldersPanelSettings::setHiddenFilesShown(show); 0059 m_model->setShowHiddenFiles(show); 0060 } 0061 0062 bool FoldersPanel::showHiddenFiles() const 0063 { 0064 return FoldersPanelSettings::hiddenFilesShown(); 0065 } 0066 0067 void FoldersPanel::setLimitFoldersPanelToHome(bool enable) 0068 { 0069 FoldersPanelSettings::setLimitFoldersPanelToHome(enable); 0070 reloadTree(); 0071 } 0072 0073 bool FoldersPanel::limitFoldersPanelToHome() const 0074 { 0075 return FoldersPanelSettings::limitFoldersPanelToHome(); 0076 } 0077 0078 void FoldersPanel::setAutoScrolling(bool enable) 0079 { 0080 // TODO: Not supported yet in Dolphin 2.0 0081 FoldersPanelSettings::setAutoScrolling(enable); 0082 } 0083 0084 bool FoldersPanel::autoScrolling() const 0085 { 0086 return FoldersPanelSettings::autoScrolling(); 0087 } 0088 0089 void FoldersPanel::rename(const KFileItem &item) 0090 { 0091 if (GeneralSettings::renameInline()) { 0092 const int index = m_model->index(item); 0093 m_controller->view()->editRole(index, "text"); 0094 } else { 0095 KIO::RenameFileDialog *dialog = new KIO::RenameFileDialog(KFileItemList({item}), this); 0096 dialog->open(); 0097 } 0098 } 0099 0100 bool FoldersPanel::urlChanged() 0101 { 0102 if (!url().isValid() || url().scheme().contains(QLatin1String("search"))) { 0103 // Skip results shown by a search, as possible identical 0104 // directory names are useless without parent-path information. 0105 return false; 0106 } 0107 0108 if (m_controller) { 0109 loadTree(url()); 0110 } 0111 0112 return true; 0113 } 0114 0115 void FoldersPanel::reloadTree() 0116 { 0117 if (m_controller) { 0118 loadTree(url(), AllowJumpHome); 0119 } 0120 } 0121 0122 void FoldersPanel::showEvent(QShowEvent *event) 0123 { 0124 if (event->spontaneous()) { 0125 Panel::showEvent(event); 0126 return; 0127 } 0128 0129 if (!m_controller) { 0130 // Postpone the creating of the controller to the first show event. 0131 // This assures that no performance and memory overhead is given when the folders panel is not 0132 // used at all and stays invisible. 0133 KFileItemListView *view = new KFileItemListView(); 0134 view->setWidgetCreator(new KItemListWidgetCreator<FoldersItemListWidget>()); 0135 view->setSupportsItemExpanding(true); 0136 // Set the opacity to 0 initially. The opacity will be increased after the loading of the initial tree 0137 // has been finished in slotLoadingCompleted(). This prevents an unnecessary animation-mess when 0138 // opening the folders panel. 0139 view->setOpacity(0); 0140 0141 connect(view, &KFileItemListView::roleEditingFinished, this, &FoldersPanel::slotRoleEditingFinished); 0142 0143 m_model = new KFileItemModel(this); 0144 m_model->setShowDirectoriesOnly(true); 0145 m_model->setShowHiddenFiles(FoldersPanelSettings::hiddenFilesShown()); 0146 // Use a QueuedConnection to give the view the possibility to react first on the 0147 // finished loading. 0148 connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &FoldersPanel::slotLoadingCompleted, Qt::QueuedConnection); 0149 0150 m_controller = new KItemListController(m_model, view, this); 0151 m_controller->setSelectionBehavior(KItemListController::SingleSelection); 0152 m_controller->setAutoActivationBehavior(KItemListController::ExpansionOnly); 0153 m_controller->setMouseDoubleClickAction(KItemListController::ActivateAndExpandItem); 0154 m_controller->setAutoActivationDelay(750); 0155 m_controller->setSingleClickActivationEnforced(true); 0156 0157 connect(m_controller, &KItemListController::itemActivated, this, &FoldersPanel::slotItemActivated); 0158 connect(m_controller, &KItemListController::itemMiddleClicked, this, &FoldersPanel::slotItemMiddleClicked); 0159 connect(m_controller, &KItemListController::itemContextMenuRequested, this, &FoldersPanel::slotItemContextMenuRequested); 0160 connect(m_controller, &KItemListController::viewContextMenuRequested, this, &FoldersPanel::slotViewContextMenuRequested); 0161 connect(m_controller, &KItemListController::itemDropEvent, this, &FoldersPanel::slotItemDropEvent); 0162 0163 KItemListContainer *container = new KItemListContainer(m_controller, this); 0164 #ifndef QT_NO_ACCESSIBILITY 0165 view->setAccessibleParentsObject(container); 0166 #endif 0167 container->setEnabledFrame(false); 0168 0169 QVBoxLayout *layout = new QVBoxLayout(this); 0170 layout->setContentsMargins(0, 0, 0, 0); 0171 layout->addWidget(container); 0172 } 0173 0174 loadTree(url()); 0175 Panel::showEvent(event); 0176 } 0177 0178 void FoldersPanel::keyPressEvent(QKeyEvent *event) 0179 { 0180 const int key = event->key(); 0181 if ((key == Qt::Key_Enter) || (key == Qt::Key_Return)) { 0182 event->accept(); 0183 } else { 0184 Panel::keyPressEvent(event); 0185 } 0186 } 0187 0188 void FoldersPanel::slotItemActivated(int index) 0189 { 0190 const KFileItem item = m_model->fileItem(index); 0191 if (!item.isNull()) { 0192 const auto modifiers = QGuiApplication::keyboardModifiers(); 0193 // keep in sync with KUrlNavigator::slotNavigatorButtonClicked 0194 if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) { 0195 Q_EMIT folderInNewActiveTab(item.url()); 0196 } else if (modifiers & Qt::ControlModifier) { 0197 Q_EMIT folderInNewTab(item.url()); 0198 } else if (modifiers & Qt::ShiftModifier) { 0199 // The shift modifier is not considered because it is used to expand the tree view without actually 0200 // opening the folder 0201 return; 0202 } else { 0203 Q_EMIT folderActivated(item.url()); 0204 } 0205 } 0206 } 0207 0208 void FoldersPanel::slotItemMiddleClicked(int index) 0209 { 0210 const KFileItem item = m_model->fileItem(index); 0211 if (!item.isNull()) { 0212 const auto modifiers = QGuiApplication::keyboardModifiers(); 0213 // keep in sync with KUrlNavigator::slotNavigatorButtonClicked 0214 if (modifiers & Qt::ShiftModifier) { 0215 Q_EMIT folderInNewActiveTab(item.url()); 0216 } else { 0217 Q_EMIT folderInNewTab(item.url()); 0218 } 0219 } 0220 } 0221 0222 void FoldersPanel::slotItemContextMenuRequested(int index, const QPointF &pos) 0223 { 0224 const KFileItem fileItem = m_model->fileItem(index); 0225 0226 QPointer<TreeViewContextMenu> contextMenu = new TreeViewContextMenu(this, fileItem); 0227 contextMenu.data()->open(pos.toPoint()); 0228 if (contextMenu.data()) { 0229 delete contextMenu.data(); 0230 } 0231 } 0232 0233 void FoldersPanel::slotViewContextMenuRequested(const QPointF &pos) 0234 { 0235 QPointer<TreeViewContextMenu> contextMenu = new TreeViewContextMenu(this, KFileItem()); 0236 contextMenu.data()->open(pos.toPoint()); 0237 if (contextMenu.data()) { 0238 delete contextMenu.data(); 0239 } 0240 } 0241 0242 void FoldersPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent *event) 0243 { 0244 if (index >= 0) { 0245 KFileItem destItem = m_model->fileItem(index); 0246 if (destItem.isNull()) { 0247 return; 0248 } 0249 0250 QDropEvent dropEvent(event->pos().toPoint(), event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); 0251 0252 KIO::DropJob *job = DragAndDropHelper::dropUrls(destItem.mostLocalUrl(), &dropEvent, this); 0253 if (job) { 0254 connect(job, &KIO::DropJob::result, this, [this](KJob *job) { 0255 if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { 0256 Q_EMIT errorMessage(job->errorString()); 0257 } 0258 }); 0259 } 0260 } 0261 } 0262 0263 void FoldersPanel::slotRoleEditingFinished(int index, const QByteArray &role, const QVariant &value) 0264 { 0265 if (role == "text") { 0266 const KFileItem item = m_model->fileItem(index); 0267 const EditResult retVal = value.value<EditResult>(); 0268 const QString newName = retVal.newName; 0269 if (!newName.isEmpty() && newName != item.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { 0270 const QUrl oldUrl = item.url(); 0271 QUrl newUrl = oldUrl.adjusted(QUrl::RemoveFilename); 0272 newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName)); 0273 0274 KIO::Job *job = KIO::moveAs(oldUrl, newUrl); 0275 KJobWidgets::setWindow(job, this); 0276 KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job); 0277 job->uiDelegate()->setAutoErrorHandlingEnabled(true); 0278 } 0279 } 0280 } 0281 0282 void FoldersPanel::slotLoadingCompleted() 0283 { 0284 if (m_controller->view()->opacity() == 0) { 0285 // The loading of the initial tree after opening the Folders panel 0286 // has been finished. Trigger the increasing of the opacity after 0287 // a short delay to give the view the chance to finish its internal 0288 // animations. 0289 // TODO: Check whether it makes sense to allow accessing the 0290 // view-internal delay for usecases like this. 0291 QTimer::singleShot(250, this, &FoldersPanel::startFadeInAnimation); 0292 } 0293 0294 if (!m_updateCurrentItem) { 0295 return; 0296 } 0297 0298 const int index = m_model->index(url()); 0299 updateCurrentItem(index); 0300 m_updateCurrentItem = false; 0301 } 0302 0303 void FoldersPanel::startFadeInAnimation() 0304 { 0305 QPropertyAnimation *anim = new QPropertyAnimation(m_controller->view(), "opacity", this); 0306 anim->setStartValue(0); 0307 anim->setEndValue(1); 0308 anim->setEasingCurve(QEasingCurve::InOutQuad); 0309 anim->start(QAbstractAnimation::DeleteWhenStopped); 0310 anim->setDuration(200); 0311 } 0312 0313 void FoldersPanel::loadTree(const QUrl &url, FoldersPanel::NavigationBehaviour navigationBehaviour) 0314 { 0315 Q_ASSERT(m_controller); 0316 0317 m_updateCurrentItem = false; 0318 bool jumpHome = false; 0319 0320 QUrl baseUrl; 0321 if (!url.isLocalFile()) { 0322 // Clear the path for non-local URLs and use it as base 0323 baseUrl = url; 0324 baseUrl.setPath(QStringLiteral("/")); 0325 } else if (Dolphin::homeUrl().isParentOf(url) || (Dolphin::homeUrl() == url)) { 0326 if (FoldersPanelSettings::limitFoldersPanelToHome()) { 0327 baseUrl = Dolphin::homeUrl(); 0328 } else { 0329 // Use the root directory as base for local URLs (#150941) 0330 baseUrl = QUrl::fromLocalFile(QDir::rootPath()); 0331 } 0332 } else if (FoldersPanelSettings::limitFoldersPanelToHome() && navigationBehaviour == AllowJumpHome) { 0333 baseUrl = Dolphin::homeUrl(); 0334 jumpHome = true; 0335 } else { 0336 // Use the root directory as base for local URLs (#150941) 0337 baseUrl = QUrl::fromLocalFile(QDir::rootPath()); 0338 } 0339 0340 if (m_model->directory() != baseUrl && !jumpHome) { 0341 m_updateCurrentItem = true; 0342 m_model->refreshDirectory(baseUrl); 0343 } 0344 0345 const int index = m_model->index(url); 0346 if (jumpHome) { 0347 Q_EMIT folderActivated(baseUrl); 0348 } else if (index >= 0) { 0349 updateCurrentItem(index); 0350 } else if (url == baseUrl) { 0351 // clear the selection when visiting the base url 0352 updateCurrentItem(-1); 0353 } else { 0354 m_updateCurrentItem = true; 0355 m_model->expandParentDirectories(url); 0356 0357 // slotLoadingCompleted() will be invoked after the model has 0358 // expanded the url 0359 } 0360 } 0361 0362 void FoldersPanel::updateCurrentItem(int index) 0363 { 0364 KItemListSelectionManager *selectionManager = m_controller->selectionManager(); 0365 selectionManager->setCurrentItem(index); 0366 selectionManager->clearSelection(); 0367 selectionManager->setSelected(index); 0368 0369 m_controller->view()->scrollToItem(index); 0370 } 0371 0372 #include "moc_folderspanel.cpp"