1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-only |
6 | */ |
7 | |
8 | #include "kfileplacesitem_p.h" |
9 | |
10 | #include <QDateTime> |
11 | #include <QDir> |
12 | #include <QIcon> |
13 | |
14 | #include <KBookmarkManager> |
15 | #include <KConfig> |
16 | #include <KConfigGroup> |
17 | #include <KIconUtils> |
18 | #include <KLocalizedString> |
19 | #include <KMountPoint> |
20 | #include <kprotocolinfo.h> |
21 | #include <solid/block.h> |
22 | #include <solid/genericinterface.h> |
23 | #include <solid/networkshare.h> |
24 | #include <solid/opticaldisc.h> |
25 | #include <solid/opticaldrive.h> |
26 | #include <solid/portablemediaplayer.h> |
27 | #include <solid/storageaccess.h> |
28 | #include <solid/storagedrive.h> |
29 | #include <solid/storagevolume.h> |
30 | |
31 | static bool isTrash(const KBookmark &bk) |
32 | { |
33 | return bk.url().toString() == QLatin1String("trash:/" ); |
34 | } |
35 | |
36 | KFilePlacesItem::KFilePlacesItem(KBookmarkManager *manager, const QString &address, const QString &udi, KFilePlacesModel *parent) |
37 | : QObject(static_cast<QObject *>(parent)) |
38 | , m_manager(manager) |
39 | , m_folderIsEmpty(true) |
40 | , m_isCdrom(false) |
41 | , m_isAccessible(false) |
42 | , m_isTeardownAllowed(false) |
43 | , m_isTeardownOverlayRecommended(false) |
44 | , m_isTeardownInProgress(false) |
45 | , m_isSetupInProgress(false) |
46 | , m_isEjectInProgress(false) |
47 | , m_isReadOnly(false) |
48 | { |
49 | updateDeviceInfo(udi); |
50 | |
51 | setBookmark(m_manager->findByAddress(address)); |
52 | |
53 | if (udi.isEmpty() && m_bookmark.metaDataItem(QStringLiteral("ID" )).isEmpty()) { |
54 | m_bookmark.setMetaDataItem(QStringLiteral("ID" ), value: generateNewId()); |
55 | } else if (udi.isEmpty()) { |
56 | if (isTrash(bk: m_bookmark)) { |
57 | KConfig cfg(QStringLiteral("trashrc" ), KConfig::SimpleConfig); |
58 | const KConfigGroup group = cfg.group(QStringLiteral("Status" )); |
59 | m_folderIsEmpty = group.readEntry(key: "Empty" , defaultValue: true); |
60 | } |
61 | } |
62 | |
63 | // Hide SSHFS network device mounted by kdeconnect, since we already have the kdeconnect:// place. |
64 | if (isDevice() && !isHidden() && m_access && device().vendor() == QLatin1String("fuse.sshfs" )) { |
65 | const QString storageFilePath = m_access->filePath(); |
66 | // kdeconnect mounts into runtime dir or temp, anyone else cannot be kdeconnect. |
67 | QString runtimePath = QStandardPaths::writableLocation(type: QStandardPaths::RuntimeLocation); |
68 | if (runtimePath.isEmpty()) { |
69 | runtimePath = QStandardPaths::writableLocation(type: QStandardPaths::TempLocation); |
70 | } |
71 | if (!runtimePath.isEmpty()) { |
72 | runtimePath.append(c: QLatin1Char('/')); |
73 | } |
74 | |
75 | if (runtimePath.isEmpty() || storageFilePath.startsWith(s: runtimePath)) { |
76 | // Not using findByPath() as it resolves symlinks, potentially blocking, |
77 | // but here we know we query for an existing actual mount point. |
78 | const auto mountPoints = KMountPoint::currentMountPoints(); |
79 | auto it = std::find_if(first: mountPoints.cbegin(), last: mountPoints.cend(), pred: [&storageFilePath](const KMountPoint::Ptr &mountPoint) { |
80 | return mountPoint->mountPoint() == storageFilePath; |
81 | }); |
82 | if (it != mountPoints.cend()) { |
83 | if ((*it)->mountedFrom().startsWith(s: QLatin1String("kdeconnect@" ))) { |
84 | // Hide only if the user never set the "Hide" checkbox on the device. |
85 | if (m_bookmark.metaDataItem(QStringLiteral("IsHidden" )).isEmpty()) { |
86 | setHidden(true); |
87 | } |
88 | } |
89 | } |
90 | } |
91 | } |
92 | } |
93 | |
94 | KFilePlacesItem::~KFilePlacesItem() |
95 | { |
96 | } |
97 | |
98 | QString KFilePlacesItem::id() const |
99 | { |
100 | if (isDevice()) { |
101 | return bookmark().metaDataItem(QStringLiteral("UDI" )); |
102 | } else { |
103 | return bookmark().metaDataItem(QStringLiteral("ID" )); |
104 | } |
105 | } |
106 | |
107 | bool KFilePlacesItem::hasSupportedScheme(const QStringList &schemes) const |
108 | { |
109 | if (schemes.isEmpty()) { |
110 | return true; |
111 | } |
112 | |
113 | // StorageAccess is always local, doesn't need to be accessible to know this |
114 | if (m_access && schemes.contains(str: QLatin1String("file" ))) { |
115 | return true; |
116 | } |
117 | |
118 | if (m_networkShare && schemes.contains(str: m_networkShare->url().scheme())) { |
119 | return true; |
120 | } |
121 | |
122 | if (m_player) { |
123 | const QStringList protocols = m_player->supportedProtocols(); |
124 | for (const QString &protocol : protocols) { |
125 | if (schemes.contains(str: protocol)) { |
126 | return true; |
127 | } |
128 | } |
129 | } |
130 | |
131 | return false; |
132 | } |
133 | |
134 | bool KFilePlacesItem::isDevice() const |
135 | { |
136 | return !bookmark().metaDataItem(QStringLiteral("UDI" )).isEmpty(); |
137 | } |
138 | |
139 | KFilePlacesModel::DeviceAccessibility KFilePlacesItem::deviceAccessibility() const |
140 | { |
141 | if (m_isTeardownInProgress || m_isEjectInProgress) { |
142 | return KFilePlacesModel::TeardownInProgress; |
143 | } else if (m_isSetupInProgress) { |
144 | return KFilePlacesModel::SetupInProgress; |
145 | } else if (m_isAccessible) { |
146 | return KFilePlacesModel::Accessible; |
147 | } else { |
148 | return KFilePlacesModel::SetupNeeded; |
149 | } |
150 | } |
151 | |
152 | bool KFilePlacesItem::isTeardownAllowed() const |
153 | { |
154 | return m_isTeardownAllowed; |
155 | } |
156 | |
157 | bool KFilePlacesItem::isTeardownOverlayRecommended() const |
158 | { |
159 | return m_isTeardownOverlayRecommended; |
160 | } |
161 | |
162 | bool KFilePlacesItem::isEjectAllowed() const |
163 | { |
164 | return m_isCdrom; |
165 | } |
166 | |
167 | KBookmark KFilePlacesItem::bookmark() const |
168 | { |
169 | return m_bookmark; |
170 | } |
171 | |
172 | void KFilePlacesItem::setBookmark(const KBookmark &bookmark) |
173 | { |
174 | m_bookmark = bookmark; |
175 | |
176 | if (m_device.isValid()) { |
177 | m_bookmark.setMetaDataItem(QStringLiteral("UDI" ), value: m_device.udi()); |
178 | if (m_volume && !m_volume->uuid().isEmpty()) { |
179 | m_bookmark.setMetaDataItem(QStringLiteral("uuid" ), value: m_volume->uuid()); |
180 | } |
181 | } |
182 | |
183 | if (bookmark.metaDataItem(QStringLiteral("isSystemItem" )) == QLatin1String("true" )) { |
184 | // This context must stay as it is - the translated system bookmark names |
185 | // are created with 'KFile System Bookmarks' as their context, so this |
186 | // ensures the right string is picked from the catalog. |
187 | // (coles, 13th May 2009) |
188 | |
189 | m_text = i18nc("KFile System Bookmarks" , bookmark.text().toUtf8().data()); |
190 | } else { |
191 | m_text = bookmark.text(); |
192 | } |
193 | |
194 | if (!isDevice()) { |
195 | const QString protocol = bookmark.url().scheme(); |
196 | if (protocol == QLatin1String("timeline" ) || protocol == QLatin1String("recentlyused" )) { |
197 | m_groupType = KFilePlacesModel::RecentlySavedType; |
198 | } else if (protocol.contains(s: QLatin1String("search" ))) { |
199 | m_groupType = KFilePlacesModel::SearchForType; |
200 | } else if (protocol == QLatin1String("bluetooth" ) || protocol == QLatin1String("obexftp" ) || protocol == QLatin1String("kdeconnect" )) { |
201 | m_groupType = KFilePlacesModel::DevicesType; |
202 | } else if (protocol == QLatin1String("tags" )) { |
203 | m_groupType = KFilePlacesModel::TagsType; |
204 | } else if (protocol == QLatin1String("remote" ) || KProtocolInfo::protocolClass(protocol) != QLatin1String(":local" )) { |
205 | m_groupType = KFilePlacesModel::RemoteType; |
206 | } else { |
207 | m_groupType = KFilePlacesModel::PlacesType; |
208 | } |
209 | } else { |
210 | if (m_drive && m_drive->isRemovable()) { |
211 | m_groupType = KFilePlacesModel::RemovableDevicesType; |
212 | } else if (m_networkShare) { |
213 | m_groupType = KFilePlacesModel::RemoteType; |
214 | } else { |
215 | m_groupType = KFilePlacesModel::DevicesType; |
216 | } |
217 | } |
218 | |
219 | switch (m_groupType) { |
220 | case KFilePlacesModel::PlacesType: |
221 | m_groupName = i18nc("@item" , "Places" ); |
222 | break; |
223 | case KFilePlacesModel::RemoteType: |
224 | m_groupName = i18nc("@item" , "Remote" ); |
225 | break; |
226 | case KFilePlacesModel::RecentlySavedType: |
227 | m_groupName = i18nc("@item The place group section name for recent dynamic lists" , "Recent" ); |
228 | break; |
229 | case KFilePlacesModel::SearchForType: |
230 | m_groupName = i18nc("@item" , "Search For" ); |
231 | break; |
232 | case KFilePlacesModel::DevicesType: |
233 | m_groupName = i18nc("@item" , "Devices" ); |
234 | break; |
235 | case KFilePlacesModel::RemovableDevicesType: |
236 | m_groupName = i18nc("@item" , "Removable Devices" ); |
237 | break; |
238 | case KFilePlacesModel::TagsType: |
239 | m_groupName = i18nc("@item" , "Tags" ); |
240 | break; |
241 | case KFilePlacesModel::UnknownType: |
242 | Q_UNREACHABLE(); |
243 | break; |
244 | } |
245 | } |
246 | |
247 | Solid::Device KFilePlacesItem::device() const |
248 | { |
249 | return m_device; |
250 | } |
251 | |
252 | QVariant KFilePlacesItem::data(int role) const |
253 | { |
254 | if (role == KFilePlacesModel::GroupRole) { |
255 | return QVariant(m_groupName); |
256 | } else if (role != KFilePlacesModel::HiddenRole && role != Qt::BackgroundRole && isDevice()) { |
257 | return deviceData(role); |
258 | } else { |
259 | return bookmarkData(role); |
260 | } |
261 | } |
262 | |
263 | KFilePlacesModel::GroupType KFilePlacesItem::groupType() const |
264 | { |
265 | return m_groupType; |
266 | } |
267 | |
268 | bool KFilePlacesItem::isHidden() const |
269 | { |
270 | return m_bookmark.metaDataItem(QStringLiteral("IsHidden" )) == QLatin1String("true" ); |
271 | } |
272 | |
273 | void KFilePlacesItem::setHidden(bool hide) |
274 | { |
275 | if (m_bookmark.isNull() || isHidden() == hide) { |
276 | return; |
277 | } |
278 | m_bookmark.setMetaDataItem(QStringLiteral("IsHidden" ), value: hide ? QStringLiteral("true" ) : QStringLiteral("false" )); |
279 | } |
280 | |
281 | QVariant KFilePlacesItem::bookmarkData(int role) const |
282 | { |
283 | KBookmark b = bookmark(); |
284 | |
285 | if (b.isNull()) { |
286 | return QVariant(); |
287 | } |
288 | |
289 | switch (role) { |
290 | case Qt::DisplayRole: |
291 | return m_text; |
292 | case Qt::DecorationRole: |
293 | return QIcon::fromTheme(name: iconNameForBookmark(bookmark: b)); |
294 | case Qt::ToolTipRole: { |
295 | const KFilePlacesModel::GroupType type = groupType(); |
296 | // Don't display technical gibberish in the URL, particularly search. |
297 | if (type != KFilePlacesModel::RecentlySavedType && type != KFilePlacesModel::SearchForType && type != KFilePlacesModel::TagsType) { |
298 | return b.url().toDisplayString(options: QUrl::PreferLocalFile); |
299 | } |
300 | return QString(); |
301 | } |
302 | case Qt::BackgroundRole: |
303 | if (isHidden()) { |
304 | return QColor(Qt::lightGray); |
305 | } else { |
306 | return QVariant(); |
307 | } |
308 | case KFilePlacesModel::UrlRole: |
309 | return b.url(); |
310 | case KFilePlacesModel::SetupNeededRole: |
311 | return false; |
312 | case KFilePlacesModel::HiddenRole: |
313 | return isHidden(); |
314 | case KFilePlacesModel::IconNameRole: |
315 | return iconNameForBookmark(bookmark: b); |
316 | default: |
317 | return QVariant(); |
318 | } |
319 | } |
320 | |
321 | QVariant KFilePlacesItem::deviceData(int role) const |
322 | { |
323 | Solid::Device d = device(); |
324 | |
325 | if (d.isValid()) { |
326 | switch (role) { |
327 | case Qt::DisplayRole: |
328 | if (m_deviceDisplayName.isEmpty()) { |
329 | m_deviceDisplayName = d.displayName(); |
330 | } |
331 | return m_deviceDisplayName; |
332 | case Qt::DecorationRole: |
333 | // qDebug() << "adding emblems" << m_emblems << "to device icon" << m_deviceIconName; |
334 | return KIconUtils::addOverlays(iconName: m_deviceIconName, overlays: m_emblems); |
335 | case Qt::ToolTipRole: { |
336 | if (m_access && m_isAccessible) { |
337 | // For loop devices, show backing file path rather than /dev/loop123. |
338 | QString mountedFrom = m_backingFile; |
339 | if (mountedFrom.isEmpty() && m_block) { |
340 | mountedFrom = m_block->device(); |
341 | } |
342 | |
343 | if (!mountedFrom.isEmpty()) { |
344 | return i18nc("@info:tooltip path (mounted from)" , "%1 (from %2)" , m_access->filePath(), mountedFrom); |
345 | } |
346 | } else if (!m_backingFile.isEmpty()) { |
347 | return m_backingFile; |
348 | } else if (m_block) { |
349 | return m_block->device(); |
350 | } |
351 | return QString(); |
352 | } |
353 | case KFilePlacesModel::UrlRole: |
354 | if (m_access) { |
355 | const QString path = m_access->filePath(); |
356 | return path.isEmpty() ? QUrl() : QUrl::fromLocalFile(localfile: path); |
357 | } else if (m_disc && (m_disc->availableContent() & Solid::OpticalDisc::Audio) != 0) { |
358 | Solid::Block *block = d.as<Solid::Block>(); |
359 | if (block) { |
360 | QString device = block->device(); |
361 | return QUrl(QStringLiteral("audiocd:/?device=%1" ).arg(a: device)); |
362 | } |
363 | // We failed to get the block device. Assume audiocd:/ can |
364 | // figure it out, but cannot handle multiple disc drives. |
365 | // See https://bugs.kde.org/show_bug.cgi?id=314544#c40 |
366 | return QUrl(QStringLiteral("audiocd:/" )); |
367 | } else if (m_player) { |
368 | const QStringList protocols = m_player->supportedProtocols(); |
369 | if (!protocols.isEmpty()) { |
370 | const QString protocol = protocols.first(); |
371 | if (protocol == QLatin1String("mtp" )) { |
372 | return QUrl(QStringLiteral("%1:udi=%2" ).arg(args: protocol, args: d.udi())); |
373 | } else { |
374 | QUrl url; |
375 | url.setScheme(protocol); |
376 | url.setHost(host: d.udi().section(asep: QLatin1Char('/'), astart: -1)); |
377 | url.setPath(QStringLiteral("/" )); |
378 | return url; |
379 | } |
380 | } |
381 | return QVariant(); |
382 | } else { |
383 | return QVariant(); |
384 | } |
385 | case KFilePlacesModel::SetupNeededRole: |
386 | if (m_access) { |
387 | return !m_isAccessible; |
388 | } else { |
389 | return QVariant(); |
390 | } |
391 | |
392 | case KFilePlacesModel::TeardownAllowedRole: |
393 | if (m_access) { |
394 | return m_isTeardownAllowed; |
395 | } else { |
396 | return QVariant(); |
397 | } |
398 | |
399 | case KFilePlacesModel::EjectAllowedRole: |
400 | return m_isAccessible && m_isCdrom; |
401 | |
402 | case KFilePlacesModel::TeardownOverlayRecommendedRole: |
403 | return m_isTeardownOverlayRecommended; |
404 | |
405 | case KFilePlacesModel::DeviceAccessibilityRole: |
406 | return deviceAccessibility(); |
407 | |
408 | case KFilePlacesModel::FixedDeviceRole: { |
409 | if (m_drive != nullptr) { |
410 | return !m_drive->isRemovable(); |
411 | } |
412 | return true; |
413 | } |
414 | |
415 | case KFilePlacesModel::CapacityBarRecommendedRole: |
416 | return m_isAccessible && !m_isCdrom && !m_networkShare && !m_isReadOnly; |
417 | |
418 | case KFilePlacesModel::IconNameRole: |
419 | return m_deviceIconName; |
420 | |
421 | default: |
422 | return QVariant(); |
423 | } |
424 | } else { |
425 | return QVariant(); |
426 | } |
427 | } |
428 | |
429 | KBookmark KFilePlacesItem::createBookmark(KBookmarkManager *manager, const QString &label, const QUrl &url, const QString &iconName, KFilePlacesItem *after) |
430 | { |
431 | KBookmarkGroup root = manager->root(); |
432 | if (root.isNull()) { |
433 | return KBookmark(); |
434 | } |
435 | QString empty_icon = iconName; |
436 | if (url.toString() == QLatin1String("trash:/" )) { |
437 | if (empty_icon.endsWith(s: QLatin1String("-full" ))) { |
438 | empty_icon.chop(n: 5); |
439 | } else if (empty_icon.isEmpty()) { |
440 | empty_icon = QStringLiteral("user-trash" ); |
441 | } |
442 | } |
443 | KBookmark bookmark = root.addBookmark(text: label, url, icon: empty_icon); |
444 | bookmark.setMetaDataItem(QStringLiteral("ID" ), value: generateNewId()); |
445 | |
446 | if (after) { |
447 | root.moveBookmark(bookmark, after: after->bookmark()); |
448 | } |
449 | |
450 | return bookmark; |
451 | } |
452 | |
453 | KBookmark KFilePlacesItem::createSystemBookmark(KBookmarkManager *manager, |
454 | const char *untranslatedLabel, |
455 | const QUrl &url, |
456 | const QString &iconName, |
457 | const KBookmark &after) |
458 | { |
459 | KBookmark bookmark = createBookmark(manager, label: QString::fromUtf8(utf8: untranslatedLabel), url, iconName); |
460 | if (!bookmark.isNull()) { |
461 | bookmark.setMetaDataItem(QStringLiteral("isSystemItem" ), QStringLiteral("true" )); |
462 | } |
463 | if (!after.isNull()) { |
464 | manager->root().moveBookmark(bookmark, after); |
465 | } |
466 | return bookmark; |
467 | } |
468 | |
469 | KBookmark KFilePlacesItem::createDeviceBookmark(KBookmarkManager *manager, const Solid::Device &device) |
470 | { |
471 | KBookmarkGroup root = manager->root(); |
472 | if (root.isNull()) { |
473 | return KBookmark(); |
474 | } |
475 | KBookmark bookmark = root.createNewSeparator(); |
476 | bookmark.setMetaDataItem(QStringLiteral("UDI" ), value: device.udi()); |
477 | bookmark.setMetaDataItem(QStringLiteral("isSystemItem" ), QStringLiteral("true" )); |
478 | |
479 | const auto storage = device.as<Solid::StorageVolume>(); |
480 | if (storage) { |
481 | bookmark.setMetaDataItem(QStringLiteral("uuid" ), value: storage->uuid()); |
482 | } |
483 | return bookmark; |
484 | } |
485 | |
486 | KBookmark KFilePlacesItem::createTagBookmark(KBookmarkManager *manager, const QString &tag) |
487 | { |
488 | // TODO: Currently KFilePlacesItem::setBookmark() only decides by the "isSystemItem" property |
489 | // if the label text should be looked up for translation. So there is a small risk that |
490 | // labelTexts which match existing untranslated system labels accidentally get translated. |
491 | KBookmark bookmark = createBookmark(manager, label: tag, url: QUrl(QLatin1String("tags:/" ) + tag), QStringLiteral("tag" )); |
492 | if (!bookmark.isNull()) { |
493 | bookmark.setMetaDataItem(QStringLiteral("tag" ), value: tag); |
494 | bookmark.setMetaDataItem(QStringLiteral("isSystemItem" ), QStringLiteral("true" )); |
495 | } |
496 | |
497 | return bookmark; |
498 | } |
499 | |
500 | QString KFilePlacesItem::generateNewId() |
501 | { |
502 | static int count = 0; |
503 | |
504 | // return QString::number(count++); |
505 | |
506 | return QString::number(QDateTime::currentSecsSinceEpoch()) + QLatin1Char('/') + QString::number(count++); |
507 | |
508 | // return QString::number(QDateTime::currentSecsSinceEpoch()) |
509 | // + '/' + QString::number(qrand()); |
510 | } |
511 | |
512 | bool KFilePlacesItem::updateDeviceInfo(const QString &udi) |
513 | { |
514 | if (m_device.udi() == udi) { |
515 | return false; |
516 | } |
517 | |
518 | if (m_access) { |
519 | m_access->disconnect(receiver: this); |
520 | } |
521 | |
522 | if (m_opticalDrive) { |
523 | m_opticalDrive->disconnect(receiver: this); |
524 | } |
525 | |
526 | m_device = Solid::Device(udi); |
527 | if (m_device.isValid()) { |
528 | m_access = m_device.as<Solid::StorageAccess>(); |
529 | m_volume = m_device.as<Solid::StorageVolume>(); |
530 | m_block = m_device.as<Solid::Block>(); |
531 | m_disc = m_device.as<Solid::OpticalDisc>(); |
532 | m_player = m_device.as<Solid::PortableMediaPlayer>(); |
533 | m_networkShare = m_device.as<Solid::NetworkShare>(); |
534 | m_deviceIconName = m_device.icon(); |
535 | m_emblems = m_device.emblems(); |
536 | |
537 | if (auto *genericIface = m_device.as<Solid::GenericInterface>()) { |
538 | m_backingFile = genericIface->property(QStringLiteral("BackingFile" )).toString(); |
539 | } |
540 | |
541 | m_drive = nullptr; |
542 | m_opticalDrive = nullptr; |
543 | |
544 | Solid::Device parentDevice = m_device; |
545 | while (parentDevice.isValid() && !m_drive) { |
546 | m_drive = parentDevice.as<Solid::StorageDrive>(); |
547 | m_opticalDrive = parentDevice.as<Solid::OpticalDrive>(); |
548 | parentDevice = parentDevice.parent(); |
549 | } |
550 | |
551 | if (m_access) { |
552 | connect(sender: m_access.data(), signal: &Solid::StorageAccess::setupRequested, context: this, slot: [this] { |
553 | m_isSetupInProgress = true; |
554 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
555 | }); |
556 | connect(sender: m_access.data(), signal: &Solid::StorageAccess::setupDone, context: this, slot: [this] { |
557 | m_isSetupInProgress = false; |
558 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
559 | }); |
560 | |
561 | connect(sender: m_access.data(), signal: &Solid::StorageAccess::teardownRequested, context: this, slot: [this] { |
562 | m_isTeardownInProgress = true; |
563 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
564 | }); |
565 | connect(sender: m_access.data(), signal: &Solid::StorageAccess::teardownDone, context: this, slot: [this] { |
566 | m_isTeardownInProgress = false; |
567 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
568 | }); |
569 | |
570 | connect(sender: m_access.data(), signal: &Solid::StorageAccess::accessibilityChanged, context: this, slot: &KFilePlacesItem::onAccessibilityChanged); |
571 | onAccessibilityChanged(m_access->isAccessible()); |
572 | } |
573 | |
574 | if (m_opticalDrive) { |
575 | connect(sender: m_opticalDrive.data(), signal: &Solid::OpticalDrive::ejectRequested, context: this, slot: [this] { |
576 | m_isEjectInProgress = true; |
577 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
578 | }); |
579 | connect(sender: m_opticalDrive.data(), signal: &Solid::OpticalDrive::ejectDone, context: this, slot: [this] { |
580 | m_isEjectInProgress = false; |
581 | Q_EMIT itemChanged(id: id(), roles: {KFilePlacesModel::DeviceAccessibilityRole}); |
582 | }); |
583 | } |
584 | } else { |
585 | m_access = nullptr; |
586 | m_volume = nullptr; |
587 | m_disc = nullptr; |
588 | m_player = nullptr; |
589 | m_drive = nullptr; |
590 | m_opticalDrive = nullptr; |
591 | m_networkShare = nullptr; |
592 | m_deviceIconName.clear(); |
593 | m_emblems.clear(); |
594 | } |
595 | |
596 | return true; |
597 | } |
598 | |
599 | void KFilePlacesItem::onAccessibilityChanged(bool isAccessible) |
600 | { |
601 | m_isAccessible = isAccessible; |
602 | m_isCdrom = m_device.is<Solid::OpticalDrive>() || m_opticalDrive || (m_volume && m_volume->fsType() == QLatin1String("iso9660" )); |
603 | m_emblems = m_device.emblems(); |
604 | |
605 | if (auto generic = m_device.as<Solid::GenericInterface>()) { |
606 | // TODO add Solid API for this. |
607 | m_isReadOnly = generic->property(QStringLiteral("ReadOnly" )).toBool(); |
608 | } |
609 | |
610 | m_isTeardownAllowed = isAccessible; |
611 | if (m_isTeardownAllowed) { |
612 | if (m_access->filePath() == QDir::rootPath()) { |
613 | m_isTeardownAllowed = false; |
614 | } else { |
615 | const auto homeDevice = Solid::Device::storageAccessFromPath(path: QDir::homePath()); |
616 | const auto *homeAccess = homeDevice.as<Solid::StorageAccess>(); |
617 | if (homeAccess && m_access->filePath() == homeAccess->filePath()) { |
618 | m_isTeardownAllowed = false; |
619 | } |
620 | } |
621 | } |
622 | |
623 | m_isTeardownOverlayRecommended = m_isTeardownAllowed && !m_networkShare; |
624 | if (m_isTeardownOverlayRecommended) { |
625 | if (m_drive && !m_drive->isRemovable()) { |
626 | m_isTeardownOverlayRecommended = false; |
627 | } |
628 | } |
629 | |
630 | Q_EMIT itemChanged(id: id()); |
631 | } |
632 | |
633 | QString KFilePlacesItem::iconNameForBookmark(const KBookmark &bookmark) const |
634 | { |
635 | if (!m_folderIsEmpty && isTrash(bk: bookmark)) { |
636 | return bookmark.icon() + QLatin1String("-full" ); |
637 | } else { |
638 | return bookmark.icon(); |
639 | } |
640 | } |
641 | |
642 | #include "moc_kfileplacesitem_p.cpp" |
643 | |