2 * SPDX-FileCopyrightText: 2008-2012 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
5 * Based on KFilePlacesView from kdelibs:
6 * SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
7 * SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "placespanel.h"
14 #include "dolphin_generalsettings.h"
15 #include "dolphin_placespanelsettings.h"
16 #include "dolphinplacesmodelsingleton.h"
17 #include "settings/dolphinsettingsdialog.h"
18 #include "views/draganddrophelper.h"
20 #include <KFilePlacesModel>
21 #include <KIO/DropJob>
23 #include <KLocalizedString>
24 #include <KProtocolManager>
31 #include <Solid/StorageAccess>
33 PlacesPanel::PlacesPanel(QWidget
*parent
)
34 : KFilePlacesView(parent
)
36 setDropOnPlaceEnabled(true);
37 connect(this, &PlacesPanel::urlsDropped
, this, &PlacesPanel::slotUrlsDropped
);
39 setAutoResizeItemsEnabled(false);
41 setTeardownFunction([this](const QModelIndex
&index
) {
42 slotTearDownRequested(index
);
45 m_openInSplitView
= new QAction(QIcon::fromTheme(QStringLiteral("view-right-new")), i18nc("@action:inmenu", "Open in Split View"));
46 m_openInSplitView
->setPriority(QAction::HighPriority
);
47 connect(m_openInSplitView
, &QAction::triggered
, this, [this]() {
48 const QUrl url
= currentIndex().data(KFilePlacesModel::UrlRole
).toUrl();
49 Q_EMIT
openInSplitViewRequested(url
);
51 addAction(m_openInSplitView
);
53 m_configureTrashAction
= new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure Trash…"));
54 m_configureTrashAction
->setPriority(QAction::HighPriority
);
55 connect(m_configureTrashAction
, &QAction::triggered
, this, &PlacesPanel::slotConfigureTrash
);
56 addAction(m_configureTrashAction
);
58 connect(this, &PlacesPanel::contextMenuAboutToShow
, this, &PlacesPanel::slotContextMenuAboutToShow
);
60 connect(this, &PlacesPanel::iconSizeChanged
, this, [](const QSize
&newSize
) {
61 int iconSize
= qMin(newSize
.width(), newSize
.height());
63 // Don't store 0 size, let's keep -1 for default/small/automatic
66 PlacesPanelSettings
*settings
= PlacesPanelSettings::self();
67 settings
->setIconSize(iconSize
);
72 PlacesPanel::~PlacesPanel() = default;
74 void PlacesPanel::setUrl(const QUrl
&url
)
76 // KFilePlacesView::setUrl no-ops when no model is set but we only set it in showEvent()
77 // Remember the URL and set it in showEvent
79 KFilePlacesView::setUrl(url
);
82 QList
<QAction
*> PlacesPanel::customContextMenuActions() const
84 return m_customContextMenuActions
;
87 void PlacesPanel::setCustomContextMenuActions(const QList
<QAction
*> &actions
)
89 m_customContextMenuActions
= actions
;
92 void PlacesPanel::proceedWithTearDown()
94 if (m_indexToTearDown
.isValid()) {
95 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
96 placesModel
->requestTeardown(m_indexToTearDown
);
98 qWarning() << "Places entry to tear down is no longer valid";
102 void PlacesPanel::readSettings()
104 if (GeneralSettings::autoExpandFolders()) {
105 setDragAutoActivationDelay(750);
107 setDragAutoActivationDelay(0);
110 const int iconSize
= qMax(0, PlacesPanelSettings::iconSize());
111 setIconSize(QSize(iconSize
, iconSize
));
114 void PlacesPanel::showEvent(QShowEvent
*event
)
116 if (!event
->spontaneous() && !model()) {
119 auto *placesModel
= DolphinPlacesModelSingleton::instance().placesModel();
120 setModel(placesModel
);
122 connect(placesModel
, &KFilePlacesModel::errorMessage
, this, &PlacesPanel::errorMessage
);
123 connect(placesModel
, &KFilePlacesModel::teardownDone
, this, &PlacesPanel::slotTearDownDone
);
125 connect(placesModel
, &QAbstractItemModel::rowsInserted
, this, &PlacesPanel::slotRowsInserted
);
126 connect(placesModel
, &QAbstractItemModel::rowsAboutToBeRemoved
, this, &PlacesPanel::slotRowsAboutToBeRemoved
);
128 for (int i
= 0; i
< model()->rowCount(); ++i
) {
129 connectDeviceSignals(model()->index(i
, 0, QModelIndex()));
135 KFilePlacesView::showEvent(event
);
138 static bool isInternalDrag(const QMimeData
*mimeData
)
140 const auto formats
= mimeData
->formats();
141 for (const auto &format
: formats
) {
142 // from KFilePlacesModel::_k_internalMimetype
143 if (format
.startsWith(QLatin1String("application/x-kfileplacesmodel-"))) {
150 void PlacesPanel::dragMoveEvent(QDragMoveEvent
*event
)
152 const QModelIndex index
= indexAt(event
->position().toPoint());
153 if (index
.isValid()) {
154 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
156 // Reject drag ontop of a non-writable protocol
157 // We don't know whether we're dropping inbetween or ontop of a place
158 // so still allow internal drag events so that re-arranging still works.
159 if (!isInternalDrag(event
->mimeData())) {
160 const QUrl url
= placesModel
->url(index
);
161 if (!url
.isValid() || !KProtocolManager::supportsWriting(url
)) {
162 event
->setDropAction(Qt::IgnoreAction
);
164 DragAndDropHelper::updateDropAction(event
, url
);
169 KFilePlacesView::dragMoveEvent(event
);
172 void PlacesPanel::slotConfigureTrash()
174 const QUrl url
= currentIndex().data(KFilePlacesModel::UrlRole
).toUrl();
176 DolphinSettingsDialog
*settingsDialog
= new DolphinSettingsDialog(url
, this);
177 settingsDialog
->setCurrentPage(settingsDialog
->trashSettings
);
178 settingsDialog
->setAttribute(Qt::WA_DeleteOnClose
);
179 settingsDialog
->show();
182 void PlacesPanel::slotUrlsDropped(const QUrl
&dest
, QDropEvent
*event
, QWidget
*parent
)
184 KIO::DropJob
*job
= DragAndDropHelper::dropUrls(dest
, event
, parent
);
186 connect(job
, &KIO::DropJob::result
, this, [this](KJob
*job
) {
187 if (job
->error() && job
->error() != KIO::ERR_USER_CANCELED
) {
188 Q_EMIT
errorMessage(job
->errorString());
194 void PlacesPanel::slotContextMenuAboutToShow(const QModelIndex
&index
, QMenu
*menu
)
198 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
199 const QUrl url
= placesModel
->url(index
);
200 const Solid::Device device
= placesModel
->deviceForIndex(index
);
202 m_configureTrashAction
->setVisible(url
.scheme() == QLatin1String("trash"));
203 m_openInSplitView
->setVisible(url
.isValid());
205 // show customContextMenuActions only on the view's context menu
206 if (!url
.isValid() && !device
.isValid()) {
207 addActions(m_customContextMenuActions
);
209 const auto actions
= this->actions();
210 for (QAction
*action
: actions
) {
211 if (m_customContextMenuActions
.contains(action
)) {
212 removeAction(action
);
218 void PlacesPanel::slotTearDownRequested(const QModelIndex
&index
)
220 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
222 Solid::StorageAccess
*storageAccess
= placesModel
->deviceForIndex(index
).as
<Solid::StorageAccess
>();
223 if (!storageAccess
) {
227 m_indexToTearDown
= QPersistentModelIndex(index
);
229 // disconnect the Solid::StorageAccess::teardownRequested
230 // to prevent emitting PlacesPanel::storageTearDownExternallyRequested
231 // after we have emitted PlacesPanel::storageTearDownRequested
232 disconnect(storageAccess
, &Solid::StorageAccess::teardownRequested
, this, &PlacesPanel::slotTearDownRequestedExternally
);
233 Q_EMIT
storageTearDownRequested(storageAccess
->filePath());
236 void PlacesPanel::slotTearDownRequestedExternally(const QString
&udi
)
239 auto *storageAccess
= static_cast<Solid::StorageAccess
*>(sender());
241 Q_EMIT
storageTearDownExternallyRequested(storageAccess
->filePath());
244 void PlacesPanel::slotTearDownDone(const QModelIndex
&index
, Solid::ErrorType error
, const QVariant
&errorData
)
246 Q_UNUSED(errorData
); // All error handling is currently done in frameworks.
248 if (index
== m_indexToTearDown
) {
249 if (error
== Solid::ErrorType::NoError
) {
250 // No error; it must have been unmounted successfully
251 Q_EMIT
storageTearDownSuccessful();
253 m_indexToTearDown
= QPersistentModelIndex();
257 void PlacesPanel::slotRowsInserted(const QModelIndex
&parent
, int first
, int last
)
259 for (int i
= first
; i
<= last
; ++i
) {
260 connectDeviceSignals(model()->index(first
, 0, parent
));
264 void PlacesPanel::slotRowsAboutToBeRemoved(const QModelIndex
&parent
, int first
, int last
)
266 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
268 for (int i
= first
; i
<= last
; ++i
) {
269 const QModelIndex index
= placesModel
->index(i
, 0, parent
);
271 Solid::StorageAccess
*storageAccess
= placesModel
->deviceForIndex(index
).as
<Solid::StorageAccess
>();
272 if (!storageAccess
) {
276 disconnect(storageAccess
, &Solid::StorageAccess::teardownRequested
, this, nullptr);
280 void PlacesPanel::connectDeviceSignals(const QModelIndex
&index
)
282 auto *placesModel
= static_cast<KFilePlacesModel
*>(model());
284 Solid::StorageAccess
*storageAccess
= placesModel
->deviceForIndex(index
).as
<Solid::StorageAccess
>();
285 if (!storageAccess
) {
289 connect(storageAccess
, &Solid::StorageAccess::teardownRequested
, this, &PlacesPanel::slotTearDownRequestedExternally
);
292 #include "moc_placespanel.cpp"