File indexing completed on 2024-04-21 04:58:08

0001 /*
0002     SPDX-FileCopyrightText: 2019 Raphael Rosch <kde-dev@insaner.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 /*
0008 
0009 TODO:
0010 -sidepanel not triggering changes in session properly
0011 -"Configure sidebar" > "Add new" has no option to actually add anything there
0012 -places panel does not respond to view location changes 
0013 -detect icon size for places panel
0014 -doubleclick on image (to open kuickview) causes sidebar to deselect
0015 
0016 -"View mode" to "sidebar" causes crash and ruins session -- cannot undo
0017 
0018 
0019 BUGS:
0020 -(konq bug) sftp cannot save file being edited, because: "A file named sftp://hostname/path/to/file already exists."
0021     maybe: https://phabricator.kde.org/D23384 , or https://phabricator.kde.org/D15318
0022 -(konq bug) loading session from cmdline causes crash, but not when konq is loaded fresh
0023 
0024 */
0025 
0026 
0027 
0028 #include "tree_module.h"
0029 #include <konq_events.h>
0030 
0031 #include <KLocalizedString>
0032 #include <KPluginFactory>
0033 
0034 #include <QAction>
0035 #include <QKeyEvent>
0036 
0037 #include <QHeaderView>
0038 
0039 
0040 KonqSideBarTreeModule::KonqSideBarTreeModule(QWidget *parent,
0041         const KConfigGroup &configGroup)
0042     : KonqSidebarModule(parent, configGroup)
0043 {
0044     m_initURL = cleanupURL(QUrl(configGroup.readPathEntry("URL", QString()))); // because the .desktop file url might be "~"
0045     treeView = new QTreeView(parent);
0046     treeView->setHeaderHidden(true);
0047     treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0048     treeView->setTextElideMode(Qt::ElideMiddle);
0049     treeView->setDragEnabled(true);
0050 
0051     model = new KDirModel(this);
0052     sorted_model = new KDirSortFilterProxyModel(this);
0053     sorted_model->setSortFoldersFirst(true);
0054     sorted_model->setSourceModel(model); 
0055     model->dirLister()->setDirOnlyMode(true);
0056     model->dirLister()->setShowHiddenFiles(configGroup.readEntry("ShowHiddenFolders", false));
0057 
0058     model->openUrl(m_initURL, KDirModel::ShowRoot);
0059 
0060     treeView->setModel(sorted_model);
0061 
0062     for (int i = 1; i <= 6; i++) {
0063         treeView->setColumnHidden(i, true);
0064     }
0065 
0066     connect(treeView, &QTreeView::expanded,
0067             this, &KonqSideBarTreeModule::slotUpdateColWidth);
0068     connect(treeView, &QTreeView::collapsed,
0069             this, &KonqSideBarTreeModule::slotUpdateColWidth);
0070 
0071     model->expandToUrl(m_initURL); // KDirModel is async, we'll just have to wait for slotKDirCompleted()
0072     connect(model, &KDirModel::expand,
0073             this, &KonqSideBarTreeModule::slotKDirExpand_setRootIndex);
0074         
0075     QItemSelectionModel *selectionModel = treeView->selectionModel();
0076     connect(selectionModel, &QItemSelectionModel::selectionChanged,
0077             this, &KonqSideBarTreeModule::slotSelectionChanged);
0078 }
0079 
0080 void KonqSideBarTreeModule::customEvent(QEvent *ev) // active view has changed
0081 {
0082     if (KParts::PartActivateEvent::test(ev)) {
0083         KParts::ReadOnlyPart* rpart = static_cast<KParts::ReadOnlyPart *>( static_cast<KParts::PartActivateEvent *>(ev)->part() );
0084         if (!rpart->url().isEmpty()) {
0085             setSelection(rpart->url());
0086         }
0087     }
0088 }
0089 
0090 QUrl KonqSideBarTreeModule::cleanupURL(const QUrl &dirtyURL)
0091 {
0092     if (!dirtyURL.isValid()) {
0093         return dirtyURL;
0094     }
0095     QUrl url = dirtyURL;
0096     if (url.isRelative()) {
0097         url.setScheme("file");
0098         if (url.path() == "~") {
0099             const QString homePath = QDir::homePath();
0100             if (!homePath.endsWith("/")) {
0101                 url.setPath(homePath + "/");
0102             } else {
0103                 url.setPath(homePath);
0104             }
0105         }
0106     }
0107     return url;
0108 }
0109 
0110 KonqSideBarTreeModule::~KonqSideBarTreeModule()
0111 {
0112 }
0113 
0114 QWidget *KonqSideBarTreeModule::getWidget()
0115 {
0116     return treeView;
0117 }
0118 
0119 void KonqSideBarTreeModule::handleURL(const QUrl &url)
0120 {
0121     QUrl handleURL = url;
0122     
0123     if (handleURL.scheme().isNull()) {
0124         setSelectionIndex(QModelIndex());
0125         m_lastURL = QUrl();
0126         return;
0127     }
0128 
0129     m_lastURL = handleURL;
0130     setSelection(handleURL);
0131 }
0132 
0133 void KonqSideBarTreeModule::setSelection(const QUrl &target_url, bool do_openURLreq) // do_openURLreq=true)
0134 {
0135     QModelIndex index = sorted_model->mapFromSource(model->indexForUrl(target_url));
0136 
0137     m_lastURL = target_url;
0138 
0139     if (!index.isValid() && target_url.scheme() == m_initURL.scheme()) {
0140         if (do_openURLreq) {
0141             connect(model, &KDirModel::expand,
0142                 this,  &KonqSideBarTreeModule::slotKDirExpand_setSelection   );
0143             model->expandToUrl(target_url); // KDirModel is async, we'll just have to wait for slotKDirExpand_setSelection()          
0144         }
0145     }
0146 
0147     setSelectionIndex(index);
0148 }
0149 
0150 void KonqSideBarTreeModule::setSelectionIndex(const QModelIndex &index)
0151 {
0152     if (index == treeView->selectionModel()->currentIndex()) {
0153         return;
0154     }
0155     treeView->expand(index);
0156     treeView->scrollTo(index);
0157     treeView->setCurrentIndex(index);
0158 }
0159 
0160 void KonqSideBarTreeModule::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) 
0161 {
0162     QModelIndex index = treeView->selectionModel()->currentIndex();
0163 
0164     QUrl urlFromIndex = getUrlFromIndex(index);
0165     if (index.isValid() && m_lastURL != urlFromIndex) {
0166         emit openUrlRequest(urlFromIndex);
0167     }
0168     slotUpdateColWidth();
0169 }
0170 
0171 // needed because when there is only one column, QTreeView does not trigger resize
0172 void KonqSideBarTreeModule::slotUpdateColWidth()
0173 {
0174     treeView->resizeColumnToContents(0);
0175 }
0176 
0177 // needed because KDirModel is async
0178 void KonqSideBarTreeModule::slotKDirExpand_setRootIndex()
0179 {
0180     QModelIndex index = getIndexFromUrl(m_initURL);
0181     if (index.isValid()) {
0182         disconnect(model, &KDirModel::expand,
0183             this, &KonqSideBarTreeModule::slotKDirExpand_setRootIndex);
0184         treeView->setRootIndex(index.parent());
0185         treeView->expand(index);
0186     }
0187 }
0188 
0189 void KonqSideBarTreeModule::slotKDirExpand_setSelection(const QModelIndex &index)
0190 {
0191     QUrl urlFromIndex = getUrlFromIndex(index); // these are only going to be valid
0192     if (urlFromIndex == m_lastURL) {
0193         disconnect(model, &KDirModel::expand,
0194             this, &KonqSideBarTreeModule::slotKDirExpand_setSelection);
0195         setSelection(m_lastURL, false);
0196     }
0197     slotUpdateColWidth();
0198 }
0199 
0200 
0201 // resolves index to the correct model (due to use of KDirSortFilterProxyModel)
0202 QModelIndex KonqSideBarTreeModule::resolveIndex(const QModelIndex &index)
0203 {
0204     if (index.isValid() && index.model() != model && model != nullptr) {
0205         return static_cast<const KDirSortFilterProxyModel*>(index.model())->mapToSource(index);
0206     } else {
0207         return index;
0208     }
0209 }
0210 
0211 QUrl KonqSideBarTreeModule::getUrlFromIndex(const QModelIndex &index)
0212 {
0213     QUrl resolvedUrl;
0214 
0215     if (index.isValid()) {
0216         KFileItem itemForIndex = model->itemForIndex(resolveIndex(index));
0217         if (!itemForIndex.isNull()) {
0218             resolvedUrl = itemForIndex.url();
0219         }
0220     }
0221 
0222     return resolvedUrl;
0223 }
0224 
0225 QModelIndex KonqSideBarTreeModule::getIndexFromUrl(const QUrl &url) const
0226 {
0227     return sorted_model->mapFromSource(model->indexForUrl(url));
0228 }
0229 
0230 
0231 class KonqSidebarTreePlugin : public KonqSidebarPlugin
0232 {
0233 public:
0234     KonqSidebarTreePlugin(QObject *parent, const QVariantList &args)
0235         : KonqSidebarPlugin(parent, args) {}
0236     ~KonqSidebarTreePlugin() override {}
0237 
0238     KonqSidebarModule *createModule(QWidget *parent,
0239                                             const KConfigGroup &configGroup,
0240                                             const QString &desktopname,
0241                                             const QVariant &unused) override
0242     {
0243         Q_UNUSED(desktopname);
0244         Q_UNUSED(unused);
0245 
0246         return new KonqSideBarTreeModule(parent, configGroup);
0247     }
0248 
0249     QList<QAction *> addNewActions(QObject *parent,
0250                                            const QList<KConfigGroup> &existingModules,
0251                                            const QVariant &unused) override
0252     {
0253         Q_UNUSED(existingModules);
0254         Q_UNUSED(unused);
0255         QAction *action = new QAction(parent);
0256         action->setText(i18nc("@action:inmenu Add", "Tree Sidebar Module"));
0257         action->setIcon(QIcon::fromTheme("folder-favorites"));
0258         return QList<QAction *>() << action;
0259     }
0260 
0261     QString templateNameForNewModule(const QVariant &actionData,
0262             const QVariant &unused) const override
0263     {
0264         Q_UNUSED(actionData);
0265         Q_UNUSED(unused);
0266         return QString::fromLatin1("treesidebarplugin%1.desktop");
0267     }
0268 
0269     bool createNewModule(const QVariant &actionData,
0270                                  KConfigGroup &configGroup,
0271                                  QWidget *parentWidget,
0272                                  const QVariant &unused) override
0273     {
0274         Q_UNUSED(actionData);
0275         Q_UNUSED(parentWidget);
0276         Q_UNUSED(unused);
0277         configGroup.writeEntry("Type", "Link");
0278         configGroup.writeEntry("Icon", "folder-favorites");
0279         configGroup.writeEntry("Name", i18nc("@title:tab", "Tree"));
0280         configGroup.writeEntry("X-KDE-KonqSidebarModule", "konqsidebar_tree");
0281         return true;
0282     }
0283 };
0284 
0285 K_PLUGIN_FACTORY_WITH_JSON(KonqSidebarTreePluginFactory, "konqsidebar_tree.json", registerPlugin<KonqSidebarTreePlugin>();)
0286 // K_EXPORT_PLUGIN(KonqSidebarTreePluginFactory())
0287 
0288 #include "tree_module.moc"