]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/panels/places/placespanel.cpp
Add missing includes
[dolphin.git] / src / panels / places / placespanel.cpp
index f7ca7d010dea5487c91d4fdc9e17dd3c711dd3de..d49ad39ec49fd59e7960641765930f5fa99b623e 100644 (file)
@@ -1,61 +1,47 @@
-/***************************************************************************
- *   Copyright (C) 2008-2012 by Peter Penz <peter.penz19@gmail.com>        *
- *                                                                         *
- *   Based on KFilePlacesView from kdelibs:                                *
- *   Copyright (C) 2007 Kevin Ottens <ervin@kde.org>                       *
- *   Copyright (C) 2007 David Faure <faure@kde.org>                        *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2008-2012 Peter Penz <peter.penz19@gmail.com>
+ *
+ * Based on KFilePlacesView from kdelibs:
+ * SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
+ * SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 
 #include "placespanel.h"
 
 #include "dolphin_generalsettings.h"
-
 #include "global.h"
-#include <KFileItem>
-#include "dolphindebug.h"
-#include <KDirNotify>
-#include <QIcon>
-#include <KIO/Job>
-#include <KIO/DropJob>
-#include <KIO/EmptyTrashJob>
-#include <KIO/JobUiDelegate>
-#include <KFilePlacesModel>
-#include <KJobWidgets>
-#include <KLocalizedString>
-#include <KIconLoader>
-#include <kitemviews/kitemlistcontainer.h>
-#include <kitemviews/kitemlistcontroller.h>
-#include <kitemviews/kitemlistselectionmanager.h>
-#include <kitemviews/kstandarditem.h>
-#include <QMenu>
-#include <KMessageBox>
-#include <KNotification>
+#include "kitemviews/kitemlistcontainer.h"
+#include "kitemviews/kitemlistcontroller.h"
+#include "kitemviews/kitemlistselectionmanager.h"
+#include "kitemviews/kstandarditem.h"
 #include "placesitem.h"
-#include "placesitemeditdialog.h"
 #include "placesitemlistgroupheader.h"
 #include "placesitemlistwidget.h"
 #include "placesitemmodel.h"
 #include "placesview.h"
-#include <views/draganddrophelper.h>
+#include "trash/dolphintrash.h"
+#include "views/draganddrophelper.h"
+
+#include <KFilePlaceEditDialog>
+#include <KFilePlacesModel>
+#include <KIO/DropJob>
+#include <KIO/EmptyTrashJob>
+#include <KIO/Job>
+#include <KIconLoader>
+#include <KLocalizedString>
+#include <KMountPoint>
+#include <KPropertiesDialog>
+
+#include <QActionGroup>
 #include <QGraphicsSceneDragDropEvent>
-#include <QVBoxLayout>
-#include <QShowEvent>
+#include <QIcon>
+#include <QMenu>
 #include <QMimeData>
+#include <QVBoxLayout>
+#include <QToolTip>
+#include <QTimer>
 
 PlacesPanel::PlacesPanel(QWidget* parent) :
     Panel(parent),
@@ -66,8 +52,12 @@ PlacesPanel::PlacesPanel(QWidget* parent) :
     m_triggerStorageSetupButton(),
     m_itemDropEventIndex(-1),
     m_itemDropEventMimeData(nullptr),
-    m_itemDropEvent(nullptr)
+    m_itemDropEvent(nullptr),
+    m_tooltipTimer()
 {
+    m_tooltipTimer.setInterval(500);
+    m_tooltipTimer.setSingleShot(true);
+    connect(&m_tooltipTimer, &QTimer::timeout, this, &PlacesPanel::slotShowTooltip);
 }
 
 PlacesPanel::~PlacesPanel()
@@ -81,14 +71,14 @@ void PlacesPanel::proceedWithTearDown()
 
 bool PlacesPanel::urlChanged()
 {
-    if (!url().isValid() || url().scheme().contains(QStringLiteral("search"))) {
+    if (!url().isValid() || url().scheme().contains(QLatin1String("search"))) {
         // Skip results shown by a search, as possible identical
         // directory names are useless without parent-path information.
         return false;
     }
 
     if (m_controller) {
-        selectClosestItem();
+        selectItem();
     }
 
     return true;
@@ -97,8 +87,8 @@ bool PlacesPanel::urlChanged()
 void PlacesPanel::readSettings()
 {
     if (m_controller) {
-       const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
-       m_controller->setAutoActivationDelay(delay);
+        const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
+        m_controller->setAutoActivationDelay(delay);
     }
 }
 
@@ -121,11 +111,15 @@ void PlacesPanel::showEvent(QShowEvent* event)
                 this, &PlacesPanel::storageTearDownRequested);
         connect(m_model, &PlacesItemModel::storageTearDownExternallyRequested,
                 this, &PlacesPanel::storageTearDownExternallyRequested);
+        connect(m_model, &PlacesItemModel::storageTearDownSuccessful,
+                this, &PlacesPanel::storageTearDownSuccessful);
 
         m_view = new PlacesView();
         m_view->setWidgetCreator(new KItemListWidgetCreator<PlacesItemListWidget>());
         m_view->setGroupHeaderCreator(new KItemListGroupHeaderCreator<PlacesItemListGroupHeader>());
 
+        installEventFilter(this);
+
         m_controller = new KItemListController(m_model, m_view, this);
         m_controller->setSelectionBehavior(KItemListController::SingleSelection);
         m_controller->setSingleClickActivationEnforced(true);
@@ -143,15 +137,30 @@ void PlacesPanel::showEvent(QShowEvent* event)
         container->setEnabledFrame(false);
 
         QVBoxLayout* layout = new QVBoxLayout(this);
-        layout->setMargin(0);
+        layout->setContentsMargins(0, 0, 0, 0);
         layout->addWidget(container);
 
-        selectClosestItem();
+        selectItem();
     }
 
     Panel::showEvent(event);
 }
 
+bool PlacesPanel::eventFilter(QObject * /* obj */, QEvent *event)
+{
+    if (event->type() == QEvent::ToolTip) {
+
+        QHelpEvent *hoverEvent = reinterpret_cast<QHelpEvent *>(event);
+
+        m_hoveredIndex = m_view->itemAt(hoverEvent->pos());
+        m_hoverPos = mapToGlobal(hoverEvent->pos());
+
+        m_tooltipTimer.start();
+        return true;
+    }
+    return false;
+}
+
 void PlacesPanel::slotItemActivated(int index)
 {
     triggerItem(index, Qt::LeftButton);
@@ -175,11 +184,26 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
     QAction* editAction = nullptr;
     QAction* teardownAction = nullptr;
     QAction* ejectAction = nullptr;
-
-    const QString label = item->text();
+    QAction* mountAction = nullptr;
 
     const bool isDevice = !item->udi().isEmpty();
     const bool isTrash = (item->url().scheme() == QLatin1String("trash"));
+    if (isTrash) {
+        emptyTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"));
+        emptyTrashAction->setEnabled(item->icon() == QLatin1String("user-trash-full"));
+        menu.addSeparator();
+    }
+
+    QAction* openInNewTabAction = menu.addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open in New Tab"));
+    QAction* openInNewWindowAction = menu.addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open in New Window"));
+    QAction* propertiesAction = nullptr;
+    if (item->url().isLocalFile()) {
+        propertiesAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action:inmenu", "Properties"));
+    }
+    if (!isDevice && !isTrash) {
+        menu.addSeparator();
+    }
+    
     if (isDevice) {
         ejectAction = m_model->ejectAction(index);
         if (ejectAction) {
@@ -189,29 +213,31 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
 
         teardownAction = m_model->teardownAction(index);
         if (teardownAction) {
+            // Disable teardown option for root and home partitions
+            bool teardownEnabled = item->url() != QUrl::fromLocalFile(QDir::rootPath());
+            if (teardownEnabled) {
+                KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(QDir::homePath());
+                if (mountPoint && item->url() == QUrl::fromLocalFile(mountPoint->mountPoint())) {
+                    teardownEnabled = false;
+                }
+            }
+            teardownAction->setEnabled(teardownEnabled);
+
             teardownAction->setParent(&menu);
             menu.addAction(teardownAction);
         }
 
-        if (teardownAction || ejectAction) {
-            menu.addSeparator();
+        if (item->storageSetupNeeded()) {
+            mountAction = menu.addAction(QIcon::fromTheme(QStringLiteral("media-mount")), i18nc("@action:inmenu", "Mount"));
         }
-    } else {
-        if (isTrash) {
-            emptyTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"));
-            emptyTrashAction->setEnabled(item->icon() == QLatin1String("user-trash-full"));
+
+        if (teardownAction || ejectAction || mountAction) {
             menu.addSeparator();
         }
     }
 
-    QAction* openInNewWindowAction = menu.addAction(QIcon::fromTheme("window-new"), i18nc("@item:inmenu", "Open in New Window"));
-    QAction* openInNewTabAction = menu.addAction(QIcon::fromTheme("tab-new"), i18nc("@item:inmenu", "Open in New Tab"));
-    if (!isDevice && !isTrash) {
-        menu.addSeparator();
-    }
-
     if (!isDevice) {
-        editAction = menu.addAction(QIcon::fromTheme("document-properties"), i18nc("@item:inmenu", "Edit..."));
+        editAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-entry")), i18nc("@item:inmenu", "Edit..."));
     }
 
     QAction* removeAction = nullptr;
@@ -219,7 +245,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
         removeAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@item:inmenu", "Remove"));
     }
 
-    QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide"));
+    QAction* hideAction = menu.addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide"));
     hideAction->setCheckable(true);
     hideAction->setChecked(item->isHidden());
 
@@ -228,7 +254,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
     QAction* action = menu.exec(pos.toPoint());
     if (action) {
         if (action == emptyTrashAction) {
-            emptyTrash();
+            Trash::empty(this);
         } else {
             // The index might have changed if devices were added/removed while
             // the context menu was open.
@@ -245,21 +271,30 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
                 m_model->deleteItem(index);
             } else if (action == hideAction) {
                 item->setHidden(hideAction->isChecked());
+                if (!m_model->hiddenCount()) {
+                    showHiddenEntries(false);
+                }
             } else if (action == openInNewWindowAction) {
                 Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this);
             } else if (action == openInNewTabAction) {
                 // TriggerItem does set up the storage first and then it will
                 // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton.
                 triggerItem(index, Qt::MiddleButton);
+            } else if (action == mountAction) {
+                m_model->requestStorageSetup(index);
             } else if (action == teardownAction) {
                 m_model->requestTearDown(index);
             } else if (action == ejectAction) {
                 m_model->requestEject(index);
+            } else if (action == propertiesAction) {
+                KPropertiesDialog* dialog = new KPropertiesDialog(item->url(), this);
+                dialog->setAttribute(Qt::WA_DeleteOnClose);
+                dialog->show();
             }
         }
     }
 
-    selectClosestItem();
+    selectItem();
 }
 
 void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
@@ -268,12 +303,11 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
 
     QAction* addAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@item:inmenu", "Add Entry..."));
 
-    QAction* showAllAction = nullptr;
-    if (m_model->hiddenCount() > 0) {
-        showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
-        showAllAction->setCheckable(true);
-        showAllAction->setChecked(m_model->hiddenItemsShown());
-    }
+    QAction* showAllAction = menu.addAction(i18nc("@item:inmenu", "Show Hidden Places"));
+    showAllAction->setCheckable(true);
+    showAllAction->setChecked(m_model->hiddenItemsShown());
+    showAllAction->setIcon(QIcon::fromTheme(m_model->hiddenItemsShown() ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
+    showAllAction->setEnabled(m_model->hiddenCount());
 
     buildGroupContextMenu(&menu, m_controller->indexCloseToMousePressedPosition());
 
@@ -288,13 +322,13 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
 
     const int iconSizeCount = 4;
     static const IconSizeInfo iconSizes[iconSizeCount] = {
-        {KIconLoader::SizeSmall,        I18N_NOOP2_NOSTRIP("Small icon size", "Small (%1x%2)")},
-        {KIconLoader::SizeSmallMedium,  I18N_NOOP2_NOSTRIP("Medium icon size", "Medium (%1x%2)")},
-        {KIconLoader::SizeMedium,       I18N_NOOP2_NOSTRIP("Large icon size", "Large (%1x%2)")},
-        {KIconLoader::SizeLarge,        I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")}
+        {KIconLoader::SizeSmall,        I18NC_NOOP("Small icon size", "Small (%1x%2)")},
+        {KIconLoader::SizeSmallMedium,  I18NC_NOOP("Medium icon size", "Medium (%1x%2)")},
+        {KIconLoader::SizeMedium,       I18NC_NOOP("Large icon size", "Large (%1x%2)")},
+        {KIconLoader::SizeLarge,        I18NC_NOOP("Huge icon size", "Huge (%1x%2)")}
     };
 
-    QMap<QAction*, int> iconSizeActionMap;
+    QHash<QAction*, int> iconSizeActionMap;
     QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu);
 
     for (int i = 0; i < iconSizeCount; ++i) {
@@ -312,7 +346,8 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
     menu.addMenu(iconSizeSubMenu);
 
     menu.addSeparator();
-    foreach (QAction* action, customContextMenuActions()) {
+    const auto actions = customContextMenuActions();
+    for (QAction* action : actions) {
         menu.addAction(action);
     }
 
@@ -321,13 +356,13 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
         if (action == addAction) {
             addEntry();
         } else if (action == showAllAction) {
-            m_model->setHiddenItemsShown(showAllAction->isChecked());
+            showHiddenEntries(showAllAction->isChecked());
         } else if (iconSizeActionMap.contains(action)) {
             m_view->setIconSize(iconSizeActionMap.value(action));
         }
     }
 
-    selectClosestItem();
+    selectItem();
 }
 
 QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index)
@@ -337,12 +372,15 @@ QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index)
     }
 
     KFilePlacesModel::GroupType groupType = m_model->groupType(index);
-    QAction *hideGroupAction = menu->addAction(i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group()));
+    QAction *hideGroupAction = menu->addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group()));
     hideGroupAction->setCheckable(true);
     hideGroupAction->setChecked(m_model->isGroupHidden(groupType));
 
     connect(hideGroupAction, &QAction::triggered, this, [this, groupType, hideGroupAction]{
         m_model->setGroupHidden(groupType, hideGroupAction->isChecked());
+        if (!m_model->hiddenCount()) {
+            showHiddenEntries(false);
+        }
     });
 
     return hideGroupAction;
@@ -423,17 +461,8 @@ void PlacesPanel::slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget*
 {
     KIO::DropJob *job = DragAndDropHelper::dropUrls(dest, event, parent);
     if (job) {
-        connect(job, &KIO::DropJob::result, this, [this](KJob *job) { if (job->error()) emit errorMessage(job->errorString()); });
-    }
-}
-
-void PlacesPanel::slotTrashUpdated(KJob* job)
-{
-    if (job->error()) {
-        emit errorMessage(job->errorString());
+        connect(job, &KIO::DropJob::result, this, [this](KJob *job) { if (job->error()) Q_EMIT errorMessage(job->errorString()); });
     }
-    // as long as KIO doesn't do this, do it ourselves
-    KNotification::event(QStringLiteral("Trash: emptied"), QString(), QPixmap(), nullptr, KNotification::DefaultEvent);
 }
 
 void PlacesPanel::slotStorageSetupDone(int index, bool success)
@@ -455,28 +484,23 @@ void PlacesPanel::slotStorageSetupDone(int index, bool success)
     }
 }
 
-void PlacesPanel::emptyTrash()
+void PlacesPanel::slotShowTooltip()
 {
-    KIO::JobUiDelegate uiDelegate;
-    uiDelegate.setWindow(window());
-    if (uiDelegate.askDeleteConfirmation(QList<QUrl>(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) {
-        KIO::Job* job = KIO::emptyTrash();
-        KJobWidgets::setWindow(job, window());
-        connect(job, &KIO::Job::result, this, &PlacesPanel::slotTrashUpdated);
-    }
+    const QUrl url = m_model->data(m_hoveredIndex).value("url").value<QUrl>();
+    const QString text = url.toDisplayString(QUrl::PreferLocalFile);
+    QToolTip::showText(m_hoverPos, text);
 }
 
 void PlacesPanel::addEntry()
 {
     const int index = m_controller->selectionManager()->currentItem();
     const QUrl url = m_model->data(index).value("url").toUrl();
+    const QString text = url.fileName().isEmpty() ? url.toDisplayString(QUrl::PreferLocalFile) : url.fileName();
 
-    QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
-    dialog->setWindowTitle(i18nc("@title:window", "Add Places Entry"));
-    dialog->setAllowGlobal(true);
-    dialog->setUrl(url);
+    QPointer<KFilePlaceEditDialog> dialog = new KFilePlaceEditDialog(true, url, text, QString(), true, false, KIconLoader::SizeMedium, this);
     if (dialog->exec() == QDialog::Accepted) {
-        m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon());
+        const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString();
+        m_model->createPlacesItem(dialog->label(), dialog->url(), dialog->icon(), appName);
     }
 
     delete dialog;
@@ -485,17 +509,18 @@ void PlacesPanel::addEntry()
 void PlacesPanel::editEntry(int index)
 {
     QHash<QByteArray, QVariant> data = m_model->data(index);
+    const QUrl url = data.value("url").toUrl();
+    const QString text = data.value("text").toString();
+    const QString iconName = data.value("iconName").toString();
+    const bool applicationLocal = !data.value("applicationName").toString().isEmpty();
 
-    QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
-    dialog->setWindowTitle(i18nc("@title:window", "Edit Places Entry"));
-    dialog->setIcon(data.value("iconName").toString());
-    dialog->setText(data.value("text").toString());
-    dialog->setUrl(data.value("url").toUrl());
-    dialog->setAllowGlobal(true);
+    QPointer<KFilePlaceEditDialog> dialog = new KFilePlaceEditDialog(true, url, text, iconName, true, applicationLocal, KIconLoader::SizeMedium, this);
     if (dialog->exec() == QDialog::Accepted) {
         PlacesItem* oldItem = m_model->placesItem(index);
         if (oldItem) {
-            oldItem->setText(dialog->text());
+            const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString();
+            oldItem->setApplicationName(appName);
+            oldItem->setText(dialog->label());
             oldItem->setUrl(dialog->url());
             oldItem->setIcon(dialog->icon());
             m_model->refresh();
@@ -505,13 +530,17 @@ void PlacesPanel::editEntry(int index)
     delete dialog;
 }
 
-void PlacesPanel::selectClosestItem()
+void PlacesPanel::selectItem()
 {
     const int index = m_model->closestItem(url());
     KItemListSelectionManager* selectionManager = m_controller->selectionManager();
     selectionManager->setCurrentItem(index);
     selectionManager->clearSelection();
-    selectionManager->setSelected(index);
+
+    const QUrl closestUrl = m_model->url(index);
+    if (!closestUrl.path().isEmpty() && url() == closestUrl) {
+        selectionManager->setSelected(index);
+    }
 }
 
 void PlacesPanel::triggerItem(int index, Qt::MouseButton button)
@@ -535,10 +564,24 @@ void PlacesPanel::triggerItem(int index, Qt::MouseButton button)
         const QUrl url = m_model->data(index).value("url").toUrl();
         if (!url.isEmpty()) {
             if (button == Qt::MiddleButton) {
-                emit placeMiddleClicked(KFilePlacesModel::convertedUrl(url));
+                Q_EMIT placeMiddleClicked(KFilePlacesModel::convertedUrl(url));
             } else {
-                emit placeActivated(KFilePlacesModel::convertedUrl(url));
+                Q_EMIT placeActivated(KFilePlacesModel::convertedUrl(url));
             }
         }
     }
 }
+
+void PlacesPanel::showHiddenEntries(bool shown)
+{
+    m_model->setHiddenItemsShown(shown);
+    Q_EMIT showHiddenEntriesChanged(shown);
+}
+
+int PlacesPanel::hiddenListCount()
+{
+    if(!m_model) {
+        return 0;
+    }
+    return m_model->hiddenCount();
+}