File indexing completed on 2024-05-05 04:19:15
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2009 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "folderviewcontextmanageritem.h" 0023 0024 // Qt 0025 #include <QDir> 0026 #include <QDragEnterEvent> 0027 #include <QHeaderView> 0028 #include <QMimeData> 0029 #include <QTreeView> 0030 0031 // KF 0032 #include <KUrlMimeData> 0033 0034 // Local 0035 #include "fileoperations.h" 0036 #include "gwenview_app_debug.h" 0037 #include "lib/touch/touch_helper.h" 0038 #include <lib/contextmanager.h> 0039 #include <lib/eventwatcher.h> 0040 #include <lib/scrollerutils.h> 0041 0042 namespace Gwenview 0043 { 0044 /** 0045 * This treeview accepts url drops 0046 */ 0047 class UrlDropTreeView : public QTreeView 0048 { 0049 public: 0050 explicit UrlDropTreeView(QWidget *parent = nullptr) 0051 : QTreeView(parent) 0052 { 0053 } 0054 0055 protected: 0056 void dragEnterEvent(QDragEnterEvent *event) override 0057 { 0058 QAbstractItemView::dragEnterEvent(event); 0059 setDirtyRegion(mDropRect); 0060 if (event->mimeData()->hasUrls()) { 0061 event->acceptProposedAction(); 0062 } 0063 } 0064 0065 void dragMoveEvent(QDragMoveEvent *event) override 0066 { 0067 QAbstractItemView::dragMoveEvent(event); 0068 0069 const QModelIndex index = indexAt(event->pos()); 0070 0071 // This code has been copied from Dolphin 0072 // (panels/folders/paneltreeview.cpp) 0073 setDirtyRegion(mDropRect); 0074 mDropRect = visualRect(index); 0075 setDirtyRegion(mDropRect); 0076 0077 if (index.isValid()) { 0078 event->acceptProposedAction(); 0079 } else { 0080 event->ignore(); 0081 } 0082 } 0083 0084 void dropEvent(QDropEvent *event) override 0085 { 0086 const QList<QUrl> urlList = KUrlMimeData::urlsFromMimeData(event->mimeData()); 0087 const QModelIndex index = indexAt(event->pos()); 0088 if (!index.isValid()) { 0089 qCWarning(GWENVIEW_APP_LOG) << "Invalid index!"; 0090 return; 0091 } 0092 const QUrl destUrl = static_cast<MODEL_CLASS *>(model())->urlForIndex(index); 0093 FileOperations::showMenuForDroppedUrls(this, urlList, destUrl); 0094 } 0095 0096 bool viewportEvent(QEvent *event) override 0097 { 0098 if (event->type() == QEvent::TouchBegin) { 0099 return true; 0100 } 0101 const QPoint pos = Touch_Helper::simpleTapPosition(event); 0102 if (pos != QPoint(-1, -1)) { 0103 expand(indexAt(pos)); 0104 Q_EMIT activated(indexAt(pos)); 0105 } 0106 0107 return QTreeView::viewportEvent(event); 0108 } 0109 0110 private: 0111 QRect mDropRect; 0112 }; 0113 0114 FolderViewContextManagerItem::FolderViewContextManagerItem(ContextManager *manager) 0115 : AbstractContextManagerItem(manager) 0116 { 0117 mModel = nullptr; 0118 0119 setupView(); 0120 0121 connect(contextManager(), &ContextManager::currentDirUrlChanged, this, &FolderViewContextManagerItem::slotCurrentDirUrlChanged); 0122 } 0123 0124 void FolderViewContextManagerItem::slotCurrentDirUrlChanged(const QUrl &url) 0125 { 0126 if (url.isValid() && mUrlToSelect != url) { 0127 mUrlToSelect = url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments); 0128 mExpandingIndex = QModelIndex(); 0129 } 0130 if (!mView->isVisible()) { 0131 return; 0132 } 0133 0134 expandToSelectedUrl(); 0135 } 0136 0137 void FolderViewContextManagerItem::expandToSelectedUrl() 0138 { 0139 if (!mUrlToSelect.isValid()) { 0140 return; 0141 } 0142 0143 if (!mModel) { 0144 setupModel(); 0145 } 0146 0147 const QModelIndex index = findClosestIndex(mExpandingIndex, mUrlToSelect); 0148 if (!index.isValid()) { 0149 return; 0150 } 0151 mExpandingIndex = index; 0152 0153 QUrl url = mModel->urlForIndex(mExpandingIndex); 0154 if (mUrlToSelect == url) { 0155 // We found our url 0156 QItemSelectionModel *selModel = mView->selectionModel(); 0157 selModel->setCurrentIndex(mExpandingIndex, QItemSelectionModel::ClearAndSelect); 0158 mView->scrollTo(mExpandingIndex); 0159 mUrlToSelect = QUrl(); 0160 mExpandingIndex = QModelIndex(); 0161 } else { 0162 // We found a parent of our url 0163 mView->setExpanded(mExpandingIndex, true); 0164 } 0165 } 0166 0167 void FolderViewContextManagerItem::slotRowsInserted(const QModelIndex &parentIndex, int /*start*/, int /*end*/) 0168 { 0169 // Can't trigger the case where parentIndex is invalid, but it most 0170 // probably happen when root items are created. In this case we trigger 0171 // expandToSelectedUrl without checking the url. 0172 // See bug #191771 0173 if (!parentIndex.isValid() || mModel->urlForIndex(parentIndex).isParentOf(mUrlToSelect)) { 0174 mExpandingIndex = parentIndex; 0175 // Hack because otherwise indexes are not in correct order! 0176 QMetaObject::invokeMethod(this, &FolderViewContextManagerItem::expandToSelectedUrl, Qt::QueuedConnection); 0177 } 0178 } 0179 0180 void FolderViewContextManagerItem::slotActivated(const QModelIndex &index) 0181 { 0182 if (!index.isValid()) { 0183 return; 0184 } 0185 0186 QUrl url = mModel->urlForIndex(index); 0187 Q_EMIT urlChanged(url); 0188 } 0189 0190 void FolderViewContextManagerItem::setupModel() 0191 { 0192 mModel = new MODEL_CLASS(this); 0193 mView->setModel(mModel); 0194 #ifndef USE_PLACETREE 0195 for (int col = 1; col <= mModel->columnCount(); ++col) { 0196 mView->header()->setSectionHidden(col, true); 0197 } 0198 mModel->dirLister()->openUrl(QUrl("/")); 0199 #endif 0200 QObject::connect(mModel, &MODEL_CLASS::rowsInserted, this, &FolderViewContextManagerItem::slotRowsInserted); 0201 } 0202 0203 void FolderViewContextManagerItem::setupView() 0204 { 0205 mView = new UrlDropTreeView; 0206 mView->setEditTriggers(QAbstractItemView::NoEditTriggers); 0207 mView->setAcceptDrops(true); 0208 mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); 0209 mView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 0210 0211 // Necessary to get the drop target highlighted 0212 mView->viewport()->setAttribute(Qt::WA_Hover); 0213 0214 mView->setHeaderHidden(true); 0215 0216 // This is tricky: QTreeView header has stretchLastSection set to true. 0217 // In this configuration, the header gets quite wide and cause an 0218 // horizontal scrollbar to appear. 0219 // To avoid this, set stretchLastSection to false and resizeMode to 0220 // Stretch (we still want the column to take the full width of the 0221 // widget). 0222 mView->header()->setStretchLastSection(false); 0223 mView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0224 0225 ScrollerUtils::setQScroller(mView->viewport()); 0226 0227 setWidget(mView); 0228 QObject::connect(mView, &QTreeView::activated, this, &FolderViewContextManagerItem::slotActivated); 0229 EventWatcher::install(mView, QEvent::Show, this, SLOT(expandToSelectedUrl())); 0230 } 0231 0232 QModelIndex FolderViewContextManagerItem::findClosestIndex(const QModelIndex &parent, const QUrl &wantedUrl) 0233 { 0234 Q_ASSERT(mModel); 0235 QModelIndex index = parent; 0236 if (!index.isValid()) { 0237 index = findRootIndex(wantedUrl); 0238 if (!index.isValid()) { 0239 return {}; 0240 } 0241 } 0242 0243 QUrl url = mModel->urlForIndex(index); 0244 if (!url.isParentOf(wantedUrl)) { 0245 qCWarning(GWENVIEW_APP_LOG) << url << "is not a parent of" << wantedUrl << "!"; 0246 return {}; 0247 } 0248 0249 QString relativePath = QDir(url.path()).relativeFilePath(wantedUrl.path()); 0250 QModelIndex lastFoundIndex = index; 0251 const QStringList relativePathList = relativePath.split(QDir::separator(), Qt::SkipEmptyParts); 0252 for (const QString &pathPart : relativePathList) { 0253 bool found = false; 0254 for (int row = 0; row < mModel->rowCount(lastFoundIndex); ++row) { 0255 QModelIndex index = mModel->index(row, 0, lastFoundIndex); 0256 if (index.data().toString() == pathPart) { 0257 // FIXME: Check encoding 0258 found = true; 0259 lastFoundIndex = index; 0260 break; 0261 } 0262 } 0263 if (!found) { 0264 break; 0265 } 0266 } 0267 return lastFoundIndex; 0268 } 0269 0270 QModelIndex FolderViewContextManagerItem::findRootIndex(const QUrl &wantedUrl) 0271 { 0272 QModelIndex matchIndex; 0273 int matchUrlLength = 0; 0274 for (int row = 0; row < mModel->rowCount(); ++row) { 0275 QModelIndex index = mModel->index(row, 0); 0276 QUrl url = mModel->urlForIndex(index); 0277 int urlLength = url.url().length(); 0278 if (url.isParentOf(wantedUrl) && urlLength > matchUrlLength) { 0279 matchIndex = index; 0280 matchUrlLength = urlLength; 0281 } 0282 } 0283 if (!matchIndex.isValid()) { 0284 qCWarning(GWENVIEW_APP_LOG) << "Found no root index for" << wantedUrl; 0285 } 0286 return matchIndex; 0287 } 0288 0289 } // namespace 0290 0291 #include "moc_folderviewcontextmanageritem.cpp"