diff --git a/libfiletree/filetreebranch.cpp b/libfiletree/filetreebranch.cpp index 01e06b2..d7b34c1 100644 --- a/libfiletree/filetreebranch.cpp +++ b/libfiletree/filetreebranch.cpp @@ -1,692 +1,683 @@ /* This file is part of the KDEproject Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 2002 Klaas Freitag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filetreebranch.h" #include #include #include #include #include #include #include #include "filetreeview.h" #undef DEBUG_LISTING #undef DEBUG_MAPPING FileTreeBranch::FileTreeBranch(FileTreeView *parent, const QUrl &url, const QString &name, const QIcon &pix, bool showHidden, FileTreeViewItem *branchRoot) : KDirLister(parent), m_root(branchRoot), m_name(name), m_rootIcon(pix), m_openRootIcon(pix), m_lastFoundItem(nullptr), m_recurseChildren(true), m_showExtensions(true) { setObjectName("FileTreeBranch"); QUrl u(url); if (u.isLocalFile()) { // for local files, QDir d(u.path()); // ensure path is canonical u.setPath(d.canonicalPath()); } m_startURL = u; //qDebug() << "for" << u; // if no root is specified, create a new one if (m_root == nullptr) m_root = new FileTreeViewItem(parent, KFileItem(u, "inode/directory", S_IFDIR), this); //m_root->setExpandable(true); //m_root->setFirstColumnSpanned(true); bool sb = blockSignals(true); // don't want setText() to signal m_root->setIcon(0, pix); m_root->setText(0, name); m_root->setToolTip(0, QString("%1 - %2").arg(name, u.url(QUrl::PreferLocalFile))); blockSignals(sb); setShowingDotFiles(showHidden); - connect(this, &FileTreeBranch::itemsAdded, this, &FileTreeBranch::slotItemsAdded); - connect(this, &FileTreeBranch::itemsDeleted, this, &FileTreeBranch::slotItemsDeleted); - connect(this, &FileTreeBranch::refreshItems, this, &FileTreeBranch::slotRefreshItems); - connect(this, &FileTreeBranch::started, this, &FileTreeBranch::slotListerStarted); - - connect(this, static_cast(&FileTreeBranch::completed), - this, &FileTreeBranch::slotListerCompleted); - - connect(this, static_cast(&FileTreeBranch::canceled), - this, &FileTreeBranch::slotListerCanceled); - - connect(this, static_cast(&FileTreeBranch::clear), - this, &FileTreeBranch::slotListerClear); - - connect(this, static_cast(&FileTreeBranch::clear), - this, &FileTreeBranch::slotListerClearUrl); - - connect(this, static_cast(&FileTreeBranch::redirection), - this, &FileTreeBranch::slotRedirect); + connect(this, &KDirLister::itemsAdded, this, &FileTreeBranch::slotItemsAdded); + connect(this, &KDirLister::itemsDeleted, this, &FileTreeBranch::slotItemsDeleted); + connect(this, &KDirLister::refreshItems, this, &FileTreeBranch::slotRefreshItems); + connect(this, &KDirLister::started, this, &FileTreeBranch::slotListerStarted); + + connect(this, QOverload::of(&KDirLister::completed), this, &FileTreeBranch::slotListerCompleted); + connect(this, QOverload::of(&KDirLister::canceled), this, &FileTreeBranch::slotListerCanceled); + connect(this, QOverload<>::of(&KDirLister::clear), this, &FileTreeBranch::slotListerClear); + connect(this, QOverload::of(&KDirLister::clear), this, &FileTreeBranch::slotListerClearUrl); + connect(this, QOverload::of(&KDirLister::redirection), this, &FileTreeBranch::slotRedirect); m_openChildrenURLs.append(u); } QUrl FileTreeBranch::rootUrl() const { QUrl u = m_startURL.adjusted(QUrl::StripTrailingSlash); u.setPath(u.path()+'/'); return (u); } void FileTreeBranch::setRoot(FileTreeViewItem *r) { m_root = r; } FileTreeViewItem *FileTreeBranch::root() const { return (m_root); } QString FileTreeBranch::name() const { return (m_name); } void FileTreeBranch::setName(const QString &newName) { m_name = newName; } QIcon FileTreeBranch::pixmap() const { return (m_rootIcon); } QIcon FileTreeBranch::openPixmap() const { return (m_openRootIcon); } void FileTreeBranch::setOpen(bool open) { if (root() != nullptr) { root()->setExpanded(open); } } void FileTreeBranch::setOpenPixmap(const QIcon &pix) { m_openRootIcon = pix; if (root()->isExpanded()) { root()->setIcon(0, pix); } } void FileTreeBranch::slotListerStarted(const QUrl &url) { #ifdef DEBUG_LISTING qDebug() << "lister started for" << url; #endif // DEBUG_LISTING FileTreeViewItem *item = findItemByUrl(url); if (item != nullptr) { emit populateStarted(item); } } // Renames seem to be emitted from KDirLister as a delete of the old followed // by an add of the new. Therefore there is no need to check for renaming here. void FileTreeBranch::slotRefreshItems(const QList > &list) { #ifdef DEBUG_LISTING qDebug() << "Refreshing" << list.count() << "items"; #endif // DEBUG_LISTING FileTreeViewItemList treeViewItList; // tree view items updated for (int i = 0; i < list.count(); ++i) { const KFileItem fi2 = list[i].second; // not interested in the first #ifdef DEBUG_LISTING qDebug() << fi2.url(); #endif // DEBUG_LISTING FileTreeViewItem *item = findItemByUrl(fi2.url()); if (item != nullptr) { treeViewItList.append(item); item->setIcon(0, QIcon::fromTheme(fi2.iconName())); item->setText(0, fi2.text()); } } if (treeViewItList.count() > 0) { emit changedTreeViewItems(this, treeViewItList); } } // This would never work in KDE4. It relies on being able to set the // KFileItem's extra data to hold its corresponding tree item pointer, // but since now KFileItem values (not pointers/references) are passed // around it is not possible to modify the KDirLister's internal KFileItem. // // Use findItemByUrl(fi.url()) instead. // //FileTreeViewItem *FileTreeBranch::treeItemForFileItem(const KFileItem &fi) //{ // if (fi.isNull()) return (nullptr); // //qDebug() << "for" << fi.url(); // FileTreeViewItem *ftvi = static_cast(const_cast(fi.extraData(this))); // return (ftvi); //} FileTreeViewItem *FileTreeBranch::findItemByUrl(const QUrl &url) { FileTreeViewItem *resultItem = nullptr; if (url == m_lastFoundUrl) { // most likely and fastest first #ifdef DEBUG_MAPPING qDebug() << "Found as last" << url; #endif return (m_lastFoundItem); // no more to do } else if (url == m_startURL) { // see if is the root #ifdef DEBUG_MAPPING qDebug() << "Found as root" << url; #endif resultItem = m_root; } else if (m_itemMap.contains(url)) { // see if in our map #ifdef DEBUG_MAPPING qDebug() << "Found in map" << url; #endif resultItem = m_itemMap[url]; } else { // need to ask the lister // See comments on the removed treeItemForFileItem() above. // We should never get here, the TVImap should have the data for // every item that we create. // ////qDebug() << "searching dirlister for" << url; //const KFileItem it = findByUrl(url); //if (!it.isNull() ) //{ // //qDebug() << "found item url" << it.url(); // resultItem = treeItemForFileItem(it); //} #ifdef DEBUG_MAPPING qDebug() << "Not found" << url; #endif } if (resultItem != nullptr) { // found something m_lastFoundItem = resultItem; // cache for next time m_lastFoundUrl = url; // path this applies to } return (resultItem); } // Find an item by a relative path. If the branch is known, this // saves having to convert an input path into an URL and then doing // lots of comparisons on it as above. FileTreeViewItem *FileTreeBranch::findItemByPath(const QString &path) { #ifdef DEBUG_MAPPING qDebug() << path; #endif const QStringList pathSplit = path.split('/', QString::SkipEmptyParts); FileTreeViewItem *item = m_root; foreach (const QString &part, pathSplit) { FileTreeViewItem *foundItem = nullptr; for (int i = 0; ichildCount(); ++i) { FileTreeViewItem *child = static_cast(item->child(i)); if (child->text(0)==part) { foundItem = child; break; } } if (foundItem==nullptr) { #ifdef DEBUG_MAPPING qDebug() << "didn't find" << part << "under" << item->url(); #endif return (nullptr); // no child with that name } item = foundItem; } #ifdef DEBUG_MAPPING qDebug() << "found" << item->url(); #endif return (item); } void FileTreeBranch::itemRenamed(FileTreeViewItem *item) { QUrl u = m_itemMap.key(item); // find key for that item if (u.isEmpty()) return; // not in map, ignore m_itemMap.remove(u); // remove old from map m_itemMap[item->url()] = item; // save new item in map } // No longer needed, itemsAdded signal passes parent URL //FileTreeViewItem *FileTreeBranch::parentFTVItem(const KFileItem &fi) //{ // if (fi.isNull()) return (nullptr); // // QUrl url = fi.url(); // //qDebug() << "for" << url; // url.setFileName(QString()); // return (findItemByUrl(url)); //} FileTreeViewItem *FileTreeBranch::createTreeViewItem(FileTreeViewItem *parent, const KFileItem &fileItem) { FileTreeViewItem *tvi = nullptr; if (parent != nullptr && !fileItem.isNull()) { tvi = new FileTreeViewItem(parent, fileItem, this); const QString p = fileItem.url().url(QUrl::PreferLocalFile|QUrl::StripTrailingSlash); m_itemMap[fileItem.url()] = tvi; #ifdef DEBUG_MAPPING qDebug() << "stored in map" << fileItem.url(); #endif } else { #ifdef DEBUG_MAPPING qDebug() << "no parent/fileitem for new item!"; #endif } return (tvi); } void FileTreeBranch::slotItemsAdded(const QUrl &parent, const KFileItemList &items) { //qDebug() << "Adding" << items.count() << "items"; FileTreeViewItem *parentItem = findItemByUrl(parent); if (parentItem == nullptr) { //qDebug() << "parent item not found for" << parent; return; } FileTreeViewItem *newItem; FileTreeViewItemList treeViewItList; // tree view items created for (KFileItemList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { const KFileItem currItem = (*it); /* Only create a new FileTreeViewItem if it does not yet exist */ if (findItemByUrl(currItem.url()) != nullptr) { continue; } newItem = createTreeViewItem(parentItem, currItem); if (newItem == nullptr) { // should never happen now, // 'parent' checked above //qDebug() << "failed to create item for" << currItem.url(); continue; } // Cut off the file extension if requested, if it is not a directory if (!m_showExtensions && !currItem.isDir()) { QString name = currItem.text(); //int mPoint = name.lastIndexOf('.'); //if (mPoint>0) name = name.left(mPoint); QMimeDatabase db; QString ext = db.suffixForFileName(name); if (!ext.isEmpty()) { name.chop(ext.length()+1); newItem->setText(0, name); } } // TODO: is this useful (for local dirs) even in non-dirOnlyMode? // /* Now try to find out if there are children for dirs in the treeview */ /* This stats a directory on the local file system and checks the */ /* hardlink entry in the stat-buf. This works only for local directories. */ if (dirOnlyMode() && !m_recurseChildren && currItem.isLocalFile() && currItem.isDir()) { QUrl url = currItem.url(); QString filename = url.toLocalFile(); /* do the stat trick of Carsten. The problem is, that the hardlink * count only contains directory links. Thus, this method only seem * to work in dir-only mode */ #ifdef DEBUG_LISTING qDebug() << "Doing stat on" << filename; #endif // DEBUG_LISTING //qDebug() << "Doing stat on" << filename; struct stat statBuf; if (stat(QFile::encodeName(filename).constData(), &statBuf) == 0) { int hardLinks = statBuf.st_nlink; /* Count of dirs */ #ifdef DEBUG_LISTING qDebug() << "stat succeeded, hardlinks: " << hardLinks; #endif // DEBUG_LISTING // If the link count is > 2, the directory likely has subdirs. If it's < 2 // it's something weird like a mounted SMB share. In that case we don't know // if there are subdirs, thus show it as expandable. if (hardLinks != 2) { newItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } else { newItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); } if (hardLinks >= 2) { // "Normal" directory with subdirs hardLinks -= 2; #ifdef DEBUG_LISTING qDebug() << "Emitting directoryChildCount" << hardLinks << "for" << url; #endif // DEBUG_LISTING emit directoryChildCount(newItem, hardLinks); } } else { //qDebug() << "stat of" << filename << "failed!"; } } treeViewItList.append(newItem); } if (treeViewItList.count() > 0) { emit newTreeViewItems(this, treeViewItList); } } void FileTreeBranch::setChildRecurse(bool t) { m_recurseChildren = t; if (!t) { m_openChildrenURLs.clear(); } } bool FileTreeBranch::childRecurse() { return (m_recurseChildren); } void FileTreeBranch::setShowExtensions(bool visible) { m_showExtensions = visible; } bool FileTreeBranch::showExtensions() const { return (m_showExtensions); } /* * The signal that tells that a directory was deleted may arrive before the signal * for its children arrive. Thus, we must walk through the children of a dir and * remove them before removing the dir itself. */ void FileTreeBranch::slotItemsDeleted(const KFileItemList &items) { for (KFileItemList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { const KFileItem fi = (*it); itemDeleted(&fi); } } void FileTreeBranch::itemDeleted(const KFileItem *fi) { if (fi->isNull()) { return; } #ifdef DEBUG_LISTING qDebug() << "for" << fi->url(); #endif // DEBUG_LISTING FileTreeViewItem *ftvi = findItemByUrl(fi->url()); if (ftvi == nullptr) { #ifdef DEBUG_LISTING qDebug() << "no tree item!"; #endif // DEBUG_LISTING return; } int nChildren = ftvi->childCount(); if (nChildren > 0) { #ifdef DEBUG_LISTING qDebug() << "child count" << nChildren; #endif // DEBUG_LISTING for (int i = 0; i < nChildren; ++i) { FileTreeViewItem *ch = static_cast(ftvi->child(i)); if (ch != nullptr) { itemDeleted(ch->fileItem()); } } } QUrl u = fi->url(); if (u == m_lastFoundUrl) { m_lastFoundUrl = QUrl(); // invalidate last-found cache m_lastFoundItem = nullptr; } m_itemMap.remove(u); // remove from item map delete ftvi; // finally remove view item } void FileTreeBranch::slotListerCanceled(const QUrl &url) { #ifdef DEBUG_LISTING qDebug() << "lister cancelled for" << url; #endif // DEBUG_LISTING // remove the URL from the children-to-recurse list m_openChildrenURLs.removeAll(url); // stop animations, etc. FileTreeViewItem *item = findItemByUrl(url); if (item != nullptr) { emit populateFinished(item); } } void FileTreeBranch::slotListerClear() { #ifdef DEBUG_LISTING qDebug(); #endif // DEBUG_LISTING /* this slots needs to clear all listed items, but NOT the root item */ if (m_root != nullptr) { deleteChildrenOf(m_root); } } void FileTreeBranch::slotListerClearUrl(const QUrl &url) { #ifdef DEBUG_LISTING qDebug() << "for" << url; #endif // DEBUG_LISTING FileTreeViewItem *ftvi = findItemByUrl(url); if (ftvi != nullptr) { deleteChildrenOf(ftvi); } } void FileTreeBranch::deleteChildrenOf(QTreeWidgetItem *parent) { // for some strange reason, slotListerClearUrl() sometimes calls us // with a nullptr parent. if (parent == nullptr) { return; } QList childs = parent->takeChildren(); qDeleteAll(childs); } void FileTreeBranch::slotRedirect(const QUrl &oldUrl, const QUrl &newUrl) { if (oldUrl.adjusted(QUrl::StripTrailingSlash) == m_startURL.adjusted(QUrl::StripTrailingSlash)) { m_startURL = newUrl; } } void FileTreeBranch::slotListerCompleted(const QUrl &url) { #ifdef DEBUG_LISTING qDebug() << "lister completed for" << url; #endif // DEBUG_LISTING FileTreeViewItem *currParent = findItemByUrl(url); if (currParent == nullptr) { return; } #ifdef DEBUG_LISTING qDebug() << "current parent" << currParent << "already listed?" << currParent->alreadyListed(); #endif // DEBUG_LISTING emit populateFinished(currParent); emit directoryChildCount(currParent, currParent->childCount()); /* This is a walk through the children of the last populated directory. * Here we start the dirlister on every child of the dir and wait for its * finish. When it has finished, we go to the next child. * This must be done for non local file systems in dirOnly- and Full-Mode * and for local file systems only in full mode, because the stat trick * (see addItem-Method) does only work for dirs, not for files in the directory. */ /* Set bit that the parent dir was listed completely */ currParent->setListed(true); #ifdef DEBUG_LISTING qDebug() << "recurseChildren" << m_recurseChildren << "isLocalFile" << m_startURL.isLocalFile() << "dirOnlyMode" << dirOnlyMode(); #endif // DEBUG_LISTING if (m_recurseChildren && (!m_startURL.isLocalFile() || !dirOnlyMode())) { bool wantRecurseUrl = false; /* look if the url is in the list for url to recurse */ foreach (const QUrl &u, m_openChildrenURLs) { /* it is only interesting that the url _is_in_ the list. */ if (u.adjusted(QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) { wantRecurseUrl = true; break; } } #ifdef DEBUG_LISTING qDebug() << "Recurse for" << url << wantRecurseUrl; #endif // DEBUG_LISTING int nChildren = 0; if (wantRecurseUrl && currParent != nullptr) { /* now walk again through the tree and populate the children to get +-signs */ /* This is the starting point. The visible folder has finished, processing the children has not yet started */ nChildren = currParent->childCount(); if (nChildren == 0) { /* This happens if there is no child at all */ #ifdef DEBUG_LISTING qDebug() << "No children to recurse"; #endif // DEBUG_LISTING } /* Since we have listed the children to recurse, we can remove the entry * in the list of the URLs to see the children. */ m_openChildrenURLs.removeAll(url); } /* There are some children. We start a dirlister job on every child item * which is a directory to find out how much children are in the child * of the last opened dir. Skip non directory entries. */ for (int i = 0; i < nChildren; ++i) { const FileTreeViewItem *ch = static_cast(currParent->child(i)); if (ch->isDir() && !ch->alreadyListed()) { const KFileItem *fi = ch->fileItem(); if (!fi->isNull() && fi->isReadable()) { QUrl recurseUrl = fi->url(); #ifdef DEBUG_LISTING qDebug() << "Starting to list" << recurseUrl; #endif // DEBUG_LISTING openUrl(recurseUrl, KDirLister::Keep); } } } } #ifdef DEBUG_LISTING else { qDebug() << "no need to recurse"; } #endif // DEBUG_LISTING } /* This slot is called when a tree view item is expanded in the GUI */ bool FileTreeBranch::populate(const QUrl &url, FileTreeViewItem *currItem) { bool ret = false; if (currItem == nullptr) { return (ret); } #ifdef DEBUG_LISTING qDebug() << "populating" << url; #endif // DEBUG_LISTING /* Add this url to the list of urls to recurse for children */ if (m_recurseChildren) { m_openChildrenURLs.append(url); #ifdef DEBUG_LISTING qDebug() << "Adding as open child"; #endif // DEBUG_LISTING } if (!currItem->alreadyListed()) { #ifdef DEBUG_LISTING qDebug() << "Starting to list"; #endif // DEBUG_LISTING ret = openUrl(url, KDirLister::Keep); // start the lister } else { #ifdef DEBUG_LISTING qDebug() << "Children already exist"; #endif // DEBUG_LISTING slotListerCompleted(url); ret = true; } return (ret); } diff --git a/libfiletree/filetreeview.cpp b/libfiletree/filetreeview.cpp index c252763..3891c58 100644 --- a/libfiletree/filetreeview.cpp +++ b/libfiletree/filetreeview.cpp @@ -1,546 +1,533 @@ /* This file is part of the KDEproject Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filetreeview.h" #include #include #include #include #include #include #include #include #include "filetreeviewitem.h" #include "filetreebranch.h" #undef DEBUG_LISTING FileTreeView::FileTreeView(QWidget *parent) : QTreeWidget(parent) { setObjectName("FileTreeView"); //qDebug(); setSelectionMode(QAbstractItemView::SingleSelection); setExpandsOnDoubleClick(false); // we'll handle this ourselves setEditTriggers(QAbstractItemView::NoEditTriggers); // maybe changed later m_wantOpenFolderPixmaps = true; m_currentBeforeDropItem = nullptr; m_dropItem = nullptr; m_busyCount = 0; m_autoOpenTimer = new QTimer(this); m_autoOpenTimer->setInterval((QApplication::startDragTime() * 3) / 2); - connect(m_autoOpenTimer, SIGNAL(timeout()), SLOT(slotAutoOpenFolder())); - - /* The executed-Slot only opens a path, while the expanded-Slot populates it */ - connect(this, SIGNAL(itemActivated(QTreeWidgetItem*,int)), - SLOT(slotExecuted(QTreeWidgetItem*))); - connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), - SLOT(slotExpanded(QTreeWidgetItem*))); - connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), - SLOT(slotCollapsed(QTreeWidgetItem*))); - connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), - SLOT(slotDoubleClicked(QTreeWidgetItem*))); - - connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), - SLOT(slotDataChanged(QModelIndex,QModelIndex))); - - /* connections from the konqtree widget */ - connect(this, SIGNAL(itemSelectionChanged()), - SLOT(slotSelectionChanged())); - connect(this, SIGNAL(itemEntered(QTreeWidgetItem*,int)), - SLOT(slotOnItem(QTreeWidgetItem*))); + connect(m_autoOpenTimer, &QTimer::timeout, this, &FileTreeView::slotAutoOpenFolder); + + // The slotExecuted only opens a path, while the slotExpanded populates it + connect(this, &QTreeWidget::itemActivated, this, &FileTreeView::slotExecuted); + connect(this, &QTreeWidget::itemExpanded, this, &FileTreeView::slotExpanded); + connect(this, &QTreeWidget::itemCollapsed, this, &FileTreeView::slotCollapsed); + connect(this, &QTreeWidget::itemDoubleClicked, this, &FileTreeView::slotDoubleClicked); + connect(this, &QTreeWidget::itemSelectionChanged, this, &FileTreeView::slotSelectionChanged); + connect(this, &QTreeWidget::itemEntered, this, &FileTreeView::slotOnItem); + + connect(model(), &QAbstractItemModel::dataChanged, this, &FileTreeView::slotDataChanged); m_openFolderPixmap = QIcon::fromTheme("folder-open"); } FileTreeView::~FileTreeView() { - // we must make sure that the FileTreeTreeViewItems are deleted _before_ the + // We must make sure that the FileTreeTreeViewItems are deleted _before_ the // branches are deleted. Otherwise, the KFileItems would be destroyed - // and the FileTreeViewItems had dangling pointers to them. + // and the FileTreeViewItems will still hold dangling pointers to them. hide(); clear(); qDeleteAll(m_branches); m_branches.clear(); } // This is used when dragging and dropping out of the view to somewhere else. QMimeData *FileTreeView::mimeData(const QList items) const { QMimeData *mimeData = new QMimeData(); QList urlList; for (QList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { FileTreeViewItem *item = static_cast(*it); #ifdef DEBUG_LISTING qDebug() << item->url(); #endif // DEBUG_LISTING urlList.append(item->url()); } mimeData->setUrls(urlList); return (mimeData); } // Dragging and dropping into the view. void FileTreeView::setDropItem(QTreeWidgetItem *item) { if (item != nullptr) { m_dropItem = item; // TODO: make auto-open an option, don't start timer if not enabled m_autoOpenTimer->start(); } else { m_dropItem = nullptr; m_autoOpenTimer->stop(); } } void FileTreeView::dragEnterEvent(QDragEnterEvent *ev) { if (!ev->mimeData()->hasUrls()) { // not an URL drag ev->ignore(); return; } ev->acceptProposedAction(); QList items = selectedItems(); m_currentBeforeDropItem = (items.count() > 0 ? items.first() : nullptr); setDropItem(itemAt(ev->pos())); } void FileTreeView::dragMoveEvent(QDragMoveEvent *ev) { if (!ev->mimeData()->hasUrls()) { // not an URL drag ev->ignore(); return; } QTreeWidgetItem *item = itemAt(ev->pos()); if (item == nullptr || item->isDisabled()) { // over a valid item? // no, ignore drops on it setDropItem(nullptr); // clear drop item return; } //FileTreeViewItem *ftvi = static_cast(item); //if (!ftvi->isDir()) item = item->parent(); // if file, highlight parent dir setCurrentItem(item); // temporarily select it if (item != m_dropItem) { setDropItem(item); // changed, update drop item } ev->accept(); } void FileTreeView::dragLeaveEvent(QDragLeaveEvent *ev) { if (m_currentBeforeDropItem != nullptr) { // there was a current item // before the drag started setCurrentItem(m_currentBeforeDropItem); // restore its selection scrollToItem(m_currentBeforeDropItem); } else if (m_dropItem != nullptr) { // item selected by drag m_dropItem->setSelected(false); // clear that selection } m_currentBeforeDropItem = nullptr; setDropItem(nullptr); } void FileTreeView::dropEvent(QDropEvent *ev) { if (!ev->mimeData()->hasUrls()) { // not an URL drag ev->ignore(); return; } if (m_dropItem == nullptr) { return; // invalid drop target } FileTreeViewItem *item = static_cast(m_dropItem); #ifdef DEBUG_LISTING qDebug() << "onto" << item->url(); #endif // DEBUG_LISTING setDropItem(nullptr); // stop timer now // also clears m_dropItem! emit dropped(ev, item); ev->accept(); } void FileTreeView::slotCollapsed(QTreeWidgetItem *tvi) { FileTreeViewItem *item = static_cast(tvi); if (item != nullptr && item->isDir()) { item->setIcon(0, itemIcon(item)); } } void FileTreeView::slotExpanded(QTreeWidgetItem *tvi) { FileTreeViewItem *item = static_cast(tvi); if (item == nullptr) { return; } #ifdef DEBUG_LISTING qDebug() << item->text(0); #endif // DEBUG_LISTING FileTreeBranch *branch = item->branch(); // Check if the branch needs to be populated now if (item->isDir() && branch != nullptr && item->childCount() == 0) { #ifdef DEBUG_LISTING qDebug() << "need to populate" << item->url(); #endif // DEBUG_LISTING if (!branch->populate(item->url(), item)) { //qDebug() << "Branch populate failed!"; } } // set pixmap for open folders if (item->isDir() && item->isExpanded()) { item->setIcon(0, itemIcon(item)); } } // Called when an item is single- or double-clicked, according to the // configured selection model. // // If the item is a branch root, we don't want to expand/collapse it on // a single click, but just to select it. An explicit double click will // do the expand/collapse. void FileTreeView::slotExecuted(QTreeWidgetItem *item) { if (item == nullptr) { return; } FileTreeViewItem *ftvi = static_cast(item); if (ftvi != nullptr && ftvi->isDir() && !ftvi->isRoot()) { item->setExpanded(!item->isExpanded()); } } void FileTreeView::slotDoubleClicked(QTreeWidgetItem *item) { if (item == nullptr) { return; } FileTreeViewItem *ftvi = static_cast(item); if (ftvi != nullptr && ftvi->isRoot()) { item->setExpanded(!item->isExpanded()); } } void FileTreeView::slotAutoOpenFolder() { m_autoOpenTimer->stop(); #ifdef DEBUG_LISTING qDebug() << "children" << m_dropItem->childCount() << "expanded" << m_dropItem->isExpanded(); #endif // DEBUG_LISTING if (m_dropItem->childCount() == 0) { return; // nothing to expand } if (m_dropItem->isExpanded()) { return; // already expanded } m_dropItem->setExpanded(true); // expand the item } void FileTreeView::slotSelectionChanged() { if (m_dropItem != nullptr) { // don't do this during the dragmove } } void FileTreeView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (topLeft.column() != 0) return; // not the file name if (topLeft.row() != bottomRight.row()) return; // not a single row if (topLeft.column() != bottomRight.column()) return; // not a single column FileTreeViewItem *item = static_cast(itemFromIndex(topLeft)); if (item->url().hasFragment()) return; // ignore for sub-images QString oldName = item->url().fileName(); QString newName = item->text(0); if (oldName == newName) return; // no change of name if (newName.isEmpty()) return; // no new name emit fileRenamed(item, newName); item->branch()->itemRenamed(item); // update branch's item map } FileTreeBranch *FileTreeView::addBranch(const QUrl &path, const QString &name, bool showHidden) { const QIcon &folderPix = QIcon::fromTheme("inode-directory"); return (addBranch(path, name, folderPix, showHidden)); } FileTreeBranch *FileTreeView::addBranch(const QUrl &path, const QString &name, const QIcon &pix, bool showHidden) { //qDebug() << path; /* Open a new branch */ FileTreeBranch *newBranch = new FileTreeBranch(this, path, name, pix, showHidden); return (addBranch(newBranch)); } FileTreeBranch *FileTreeView::addBranch(FileTreeBranch *newBranch) { - connect(newBranch, SIGNAL(populateStarted(FileTreeViewItem*)), - SLOT(slotStartAnimation(FileTreeViewItem*))); - connect(newBranch, SIGNAL(populateFinished(FileTreeViewItem*)), - SLOT(slotStopAnimation(FileTreeViewItem*))); - - connect(newBranch, SIGNAL(newTreeViewItems(FileTreeBranch*,FileTreeViewItemList)), - SLOT(slotNewTreeViewItems(FileTreeBranch*,FileTreeViewItemList))); + connect(newBranch, &FileTreeBranch::populateStarted, this, &FileTreeView::slotStartAnimation); + connect(newBranch, &FileTreeBranch::populateFinished, this, &FileTreeView::slotStopAnimation); + connect(newBranch, &FileTreeBranch::newTreeViewItems, this, &FileTreeView::slotNewTreeViewItems); m_branches.append(newBranch); return (newBranch); } FileTreeBranch *FileTreeView::branch(const QString &searchName) const { for (FileTreeBranchList::const_iterator it = m_branches.constBegin(); it != m_branches.constEnd(); ++it) { FileTreeBranch *branch = (*it); QString bname = branch->name(); #ifdef DEBUG_LISTING qDebug() << "branch" << bname; #endif // DEBUG_LISTING if (bname == searchName) { #ifdef DEBUG_LISTING qDebug() << "Found requested branch"; #endif // DEBUG_LISTING return (branch); } } return (nullptr); } const FileTreeBranchList &FileTreeView::branches() const { return (m_branches); } bool FileTreeView::removeBranch(FileTreeBranch *branch) { if (m_branches.contains(branch)) { delete branch->root(); m_branches.removeOne(branch); return (true); } else { return (false); } } void FileTreeView::setDirOnlyMode(FileTreeBranch *branch, bool bom) { if (branch != nullptr) { branch->setDirOnlyMode(bom); } } void FileTreeView::slotNewTreeViewItems(FileTreeBranch *branch, const FileTreeViewItemList &items) { if (branch == nullptr) { return; } #ifdef DEBUG_LISTING qDebug(); #endif // DEBUG_LISTING /* Sometimes it happens that new items should become selected, i.e. if the user * creates a new dir, he probably wants it to be selected. This can not be done * right after creating the directory or file, because it takes some time until * the item appears here in the treeview. Thus, the creation code sets the member * m_neUrlToSelect to the required url. If this url appears here, the item becomes * selected and the member nextUrlToSelect will be cleared. */ if (!m_nextUrlToSelect.isEmpty()) { for (FileTreeViewItemList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { QUrl url = (*it)->url(); if (m_nextUrlToSelect.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments) == url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments)) { setCurrentItem(static_cast(*it)); m_nextUrlToSelect = QUrl(); break; } } } } QIcon FileTreeView::itemIcon(FileTreeViewItem *item) const { QIcon pix; if (item != nullptr) { /* Check whether it is a branch root */ FileTreeBranch *branch = item->branch(); if (item == branch->root()) { pix = branch->pixmap(); if (m_wantOpenFolderPixmaps && branch->root()->isExpanded()) { pix = branch->openPixmap(); } } else { // TODO: different modes, user Pixmaps ? pix = QIcon::fromTheme(item->fileItem()->iconName()); /* Only if it is a dir and the user wants open dir pixmap and it is open, * change the fileitem's pixmap to the open folder pixmap. */ if (item->isDir() && m_wantOpenFolderPixmaps) { if (item->isExpanded()) { pix = m_openFolderPixmap; } } } } return (pix); } void FileTreeView::slotStartAnimation(FileTreeViewItem *item) { if (item == nullptr) { return; } #ifdef DEBUG_LISTING qDebug() << "for" << item->text(0); #endif // DEBUG_LISTING ++m_busyCount; setCursor(Qt::BusyCursor); } void FileTreeView::slotStopAnimation(FileTreeViewItem *item) { if (item == nullptr) { return; } #ifdef DEBUG_LISTING qDebug() << "for" << item->text(0); #endif // DEBUG_LISTING if (m_busyCount <= 0) { return; } --m_busyCount; if (m_busyCount == 0) { unsetCursor(); } } FileTreeViewItem *FileTreeView::selectedFileTreeViewItem() const { QList items = selectedItems(); return (items.count() > 0 ? static_cast(items.first()) : nullptr); } const KFileItem *FileTreeView::selectedFileItem() const { FileTreeViewItem *item = selectedFileTreeViewItem(); return (item == nullptr ? nullptr : item->fileItem()); } QUrl FileTreeView::selectedUrl() const { FileTreeViewItem *item = selectedFileTreeViewItem(); return (item != nullptr ? item->url() : QUrl()); } FileTreeViewItem *FileTreeView::highlightedFileTreeViewItem() const { QList items = selectedItems(); if (items.isEmpty()) return (nullptr); return (static_cast(items.first())); } const KFileItem *FileTreeView::highlightedFileItem() const { FileTreeViewItem *item = highlightedFileTreeViewItem(); return (item == nullptr ? nullptr : item->fileItem()); } QUrl FileTreeView::highlightedUrl() const { FileTreeViewItem *item = highlightedFileTreeViewItem(); return (item != nullptr ? item->url() : QUrl()); } void FileTreeView::slotOnItem(QTreeWidgetItem *item) { FileTreeViewItem *i = static_cast(item); if (i != nullptr) emit onItem(i->url().url(QUrl::PreferLocalFile)); } FileTreeViewItem *FileTreeView::findItemInBranch(const QString &branchName, const QString &relUrl) const { FileTreeBranch *br = branch(branchName); return (findItemInBranch(br, relUrl)); } FileTreeViewItem *FileTreeView::findItemInBranch(FileTreeBranch *branch, const QString &relPath) const { if (branch==nullptr) return (nullptr); // no branch to search FileTreeViewItem *ret; if (relPath.isEmpty() || relPath=="/") ret = branch->root(); else ret = branch->findItemByPath(relPath); return (ret); } bool FileTreeView::showFolderOpenPixmap() const { return (m_wantOpenFolderPixmaps); } void FileTreeView::setShowFolderOpenPixmap(bool showIt) { m_wantOpenFolderPixmaps = showIt; } void FileTreeView::slotSetNextUrlToSelect(const QUrl &url) { m_nextUrlToSelect = url; }